publish is a thin client. It does not compile, package, or upload anything. It sends one authenticated request to the platform and prints the URL the platform answers with.
When to use publish
- Sending a reviewer a link to a build instead of a zip file.
- Wiring a share link into CI after
buildproduced the artifacts. - Pinning a share link to one specific build rather than the project’s latest.
publish resolves a project that already exists on extension.dev, so it needs a build the platform recorded. To send someone the build sitting in your own dist/ right now, upload that build instead: Share an unpublished build for review.
publish talks to the extension.dev platform, which is a separate product
from Extension.js. The Extension.js commands that run on your machine
(create, dev, build, preview, start) never need an account.
publish does.Usage
Token requirement
publish refuses to run without an access token. It looks in three places, in this order:
--token <token>on the command line.EXTENSION_DEV_TOKENin the environment (preferred for CI).- The stored device login that
npx @extension.dev/mcp loginwrites.
1 before any network call happens, and prints this to stderr:
Scope checks
A stored device login is scoped to one project. Publishing from an unrelated directory would mint a share link for that project without naming it anywhere obvious.publish treats that mismatch as a refusal, not a warning:
- When the directory’s project name does not match the stored login’s project, the command refuses and names both.
- Pass
--project <slug>to publish the login’s project on purpose from anywhere. - Passing
--projectwith a slug that does not match the stored login also refuses. - A
--tokenorEXTENSION_DEV_TOKENtoken skips the stored-login comparison entirely.
package.json, then manifest.json, then src/manifest.json, then the folder name.
Arguments and flags
What it prints
Pretty output is a single line, the share URL, so it pipes cleanly:--output json prints one envelope. The platform response sits in value, and it carries more than the URL:
value.shareUrl and value.visibility only. There is no token to carry.
With --build-sha, the URL points at that build instead of the project overview: https://<workspace>.extension.dev/<project>/builds/<sha>.
Public and private projects
A project is either public or private.publish only reads that setting and never changes it. The platform decides what kind of link you get:
Both answers point at the same page. Visibility decides whether a token is attached, not which address you get.
Pinning to a build
--build-sha links to one build instead of the project’s latest. The platform verifies the sha against the project’s build index and answers with a 404 and an UNKNOWN_BUILD code when no completed build matches, so a typo fails loudly instead of producing a link to the wrong artifact.
Examples
Publishing from CI
A short-lived link for one reviewer
Behavior notes
publishnever compiles. Runbuildfirst when you want the link to point at fresh output.- Every failure path exits with code
1: a missing token, an unreachable platform, or any non-2xx response, which is printed aspublish failed (<status>): <message>. --apiaccepts a base URL with or without a trailing slash. The command appends/api/cli/publishitself.--ttlis clamped by the platform to the 1 to 168 hour range.- The
?share=token this command returns is not the 30 day revocable preview link. That link comes from a different verb, which uploads the build;publishuploads nothing. See the platform’s publish page.
Next steps
- Produce the artifacts to share with
build. - Validate the artifacts locally first with
preview. - Hand someone an unpublished build behind a link with no zip and no install, in Share an unpublished build for review.
- Read how builds are recorded in Builds.

