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.