Emits

Docs

The emit CLI

The emit command ships in the @mzedstudio/emits-cli package. Run it installed (npm i -g @mzedstudio/emits-cli) or on demand (npx -y @mzedstudio/emits-cli <verb>). Most verbs accept two shared flags: --endpoint <url> to point at a different API origin (honored only with EMITS_ALLOW_CUSTOM_ENDPOINT=1 set; see the agent API), and --json for machine-readable output.

Under --json a failure writes one object to stderr and exits non-zero: { "error": { "kind", "code", "message", "hint", "details" } }. kind is the transport-level shape, code is the server's own verdict — the field to branch on — and details carries every other field the refusal sent, verbatim. So STORAGE_QUOTA_EXCEEDED hands a script plan, usedBytes, incomingBytes and maxBytes, and PUBLISH_QUOTA_EXCEEDED hands it plan, used, max and periodStart — the epoch-millisecond reset a sentence cannot state unambiguously — instead of prose to regex. code, hint and details each appear only when there is one, and details is nested rather than spread so a field the server adds later can never collide with the four above.

Your credentials and active account live in the machine-global ~/.emits/ home, which is why every verb works from any directory; a pre-rename ~/.shortwind/ home is migrated automatically.

emit login

Authenticate via the OAuth device flow and store a credential in the global home. Prints a verification URL and code to enter in a browser, then polls until approved. Requests pages:read and pages:write by default; pass --scope <scope> (repeatable) to request more, e.g. --scope domains:bind.

emit whoami

Show the signed-in account and verify it against the server (GET /v1/token): label, account id, scopes, endpoint, and a session verdict, plus any other accounts in the home. Exits 1 when not logged in or when the server rejected the stored credential, so scripts can gate on it (a merely-expired access token is refreshed transparently first, like every other verb — rejection means the credential is truly dead). An unreachable server keeps exit 0 with the cached values marked as cached: offline is not signed out. --offline skips the round-trip and reports the store as unverified. --json emits the same as a machine-readable object, with liveness under an additive session key.

emit init-global

Repair the global ~/.emits/ home (login creates it, so you rarely need this). --force overwrites an existing home.

emit publish <file>

Create a page from an HTML file (POST /v1/pages), freeze version 1, and print the live URL, id, and version.

  • --domain <slug>: desired subdomain/slug.
  • --tag <tag>: attach a tag (repeatable).
  • --visibility <level>: public | unlisted | private.
  • --idempotency-key <key>: safe-retry key.
  • --bundle: publish <file>'s whole directory as a linked multi-page unit, with <file> as the entry point. See multi-page publishes.

On a slug collision it prints the existing page id and the update command to reuse it. With --bundle it prints --domain <slug> instead: a bundle is updated by re-publishing under the slug you name, and the 409 carries that slug when the handle was derived rather than given.

Page identifiers

Every verb that takes an <id> — update, get, delete, visibility — accepts any of three forms: the page id printed by emit publish, the page's slug, or the label in its URL (usually the slug, but publish appends a suffix when the label you asked for is taken). Resolution is scoped to the signed-in account, so an identifier this account does not own is a plain 404, the same answer as one that never existed — nothing to retry.

emit update <id> <file>

Republish HTML to the same URL as a new version (PATCH /v1/pages/{id}). Accepts --idempotency-key <key>.

It is refused on a bundle's entry page. A bundle's other pages serve from their own stored copies, which an update of the entry alone would not touch — the landing page would move to the new version while every sub-page stayed on the old one. Re-publish the whole bundle instead: emit publish <entry> --bundle --domain <its slug>.

emit find

Locate existing pages (GET /v1/pages). Prints a table of id, slug, version, visibility, and tags, or no pages found.

  • --q <query>: free-text query.
  • --tag <tag>: filter by tag (repeatable; matches pages carrying all of them).

emit get <id>

Fetch page metadata and the full version list (GET /v1/pages/{id}).

emit delete <id>

Tombstone a page so it stops resolving (DELETE /v1/pages/{id}). Prompts for confirmation unless you pass -y / --yes.

emit visibility <id> <level>

Set a page's visibility to public, unlisted, or private without republishing (PATCH /v1/pages/{id}/visibility).

emit bind-domain <hostname>

Bind an account-level custom domain (POST /v1/domains). Requires the domains:bind scope; if your credential lacks it, the CLI re-runs login to step up for this one operation. See custom domains.

emit domains

List the account's custom domains and their status (GET /v1/domains).

emit approve-domain <hostname>

Approve a domain sitting at pending-human (POST /v1/domains/approve).

emit skill

Print the Emits SKILL.md, the document that teaches a coding agent when to reach for Emits and which verbs exist. Writes to stdout, or to a file with --out <file> (its references/ land alongside). The same renderer produces the Agent Plugin's skill, so the two cannot disagree.

emit mcp

Run the Emits MCP server on stdio, for agent clients that launch tools as subprocesses. Prints nothing on success: stdout is the JSON-RPC channel. See MCP.

This is the transport that can read your disk, and that is the functional reason to choose it over the hosted endpoint: publish_bundle here accepts a dir — a folder the server walks, uploads and publishes itself, images and fonts included — and publish and update accept a path to a single .html document instead of its text. A tool call carries no bytes, so over the hosted endpoint a site with a logo in it has to be published with emit publish <entry.html> --bundle instead, or dropped into the web uploader.