Skip to main content

Module publishing

Module publishing 

Source
Expand description

§Publishing and installing

A plugin is installed on your account, not on a machine: every viewer signed in to the account runs its UI half, and daemons run its daemon half. The account keeps the package and records which version each plugin runs.

§The package

standard-plugin pack writes one deterministic tarball for a plugin or a companion pair and prints its SHA-256, the package’s identity on the account:

manifest.json                  the account manifest: id, version, entries, grants
ui/standard-plugin.json        the UI half's runtime manifest
ui/<module>.wasm               its component
ui/browser/...                 the jco build, when built; viewers ignore it
daemon/standard-plugin.json    the companion's runtime manifest
daemon/<module>.wasm

manifest.json names each half’s component (ui.entry, daemon.entry) and carries the grants of both halves with their reasons: the set the user approves. daemon.placement (and daemon.failover) come from the daemon half’s daemon section (manifest). The same bundle always packs to the same bytes. A package is at most 8 MiB.

§Publishing

A plugin reaches users from one of four sources. Each is recorded on the account with the plugin, so later versions are found and installed the same way (see “Updates and approval” below).

§GitHub Releases

Attach the packed .tar to a GitHub Release:

  • A repository with one plugin tags releases v<version> (or <version>) and attaches <id>-<version>.tar. Users install it with owner/repo.
  • A repository with several plugins tags each release <id>@<version> (or <id>-v<version>) and attaches that plugin’s <id>-<version>.tar. Users install one with owner/repo#<id>, or with owner/repo: the install lists the repository’s plugins (each id’s newest published version, and the first line of its release notes) and asks which one.
  • Any asset named *.standard-plugin.tar is accepted too.

Drafts and pre-releases are skipped unless a user names the tag (owner/repo#<id>@<tag>). When GitHub publishes an asset’s SHA-256 (digest), the download must match it. Lookups use GitHub’s REST API without credentials; a GITHUB_TOKEN in the environment is sent to api.github.com only (for private repositories or rate limits) and is never stored.

A private repository installs too. When the installing viewer cannot read it, one of the account’s machines that is signed in to GitHub (a GITHUB_TOKEN or GH_TOKEN in its environment, or the GitHub CLI) reads it instead, and the install dialog names that machine while it looks. The token never leaves that machine.

A repository can release with a workflow: a script tags <id>@<version> from the plugin’s manifest, and pushing the tag builds, packs and attaches <id>-<version>.tar.

§npm

Publish an npm package that contains the packed .tar and names it in package.json:

{
  "name": "@standardagents/foobar",
  "version": "1.2.0",
  "files": ["dist/foobar-1.2.0.tar"],
  "standardPlugin": { "package": "dist/foobar-1.2.0.tar" }
}

standardPlugin.package is a path inside the package, relative to its root. A standardPlugin without package is a runtime v1 plugin and is refused. Installs resolve the latest dist-tag, or the dist-tag, version or range the user names (npm:@scope/name@^1.2, name@next), and the tarball must match its dist.integrity (sha512). The version’s publisher (_npmUser) is shown as the publisher.

Any https:// link to a .tar (the packed plugin), a .tgz/.tar.gz (a gzip of it, or an npm-style tarball as above), or a .zip holding the packed .tar or a built plugin directory (packed at install). npm tarball links (https://registry.npmjs.org/…/-/x.tgz) are links too. Updates recheck the link with a conditional request, so an unchanged file is not downloaded again.

§A local file

A built plugin directory or a packed .tar on the installing machine. The account records its absolute path; there are no automatic updates.

§A file on another machine

The path can be on any machine of your account: pick the machine in the install dialog, or run standard plugin install <path> --from-machine <machine>. That machine reads the path, verifies the package and uploads it to the account; only the package’s summary (id, name, version, grants) comes back, never the file. ~ is the home directory of that machine’s user, and a relative path is refused. Only a built plugin directory or a packed .tar is read. It has no automatic updates; standard plugin update <id> asks that machine to read the path again (it must be online).

§Limits

Downloads are https only (redirects included), at most 32 MiB and 90 seconds. Archives unpack to at most 64 MiB and 4,096 files; an entry with an absolute path or .. refuses the archive, and links are skipped, so nothing lands outside it. Every source ends in the same verification as a local package: the manifest, the package’s SHA-256, and its grants.

§Installing

standard plugin install acme/standard-plugins#weather
standard plugin install @acme/weather
standard plugin install npm:@acme/weather@^1.2
standard plugin install https://example.com/weather-1.2.0.tar
standard plugin install ./my-card            # a built plugin directory
standard plugin install my-card-0.1.0.tar
standard plugin install ~/plugins/my-card.tar --from-machine studio
standard plugin install acme/standard-plugins        # lists them, asks which
standard plugin install acme/standard-plugins#weather --from-machine studio
standard-plugin install                      # check, pack, then `standard plugin install .`

A GitHub repository of several plugins without #<id> lists its plugins and asks for a number; with --yes or without a terminal it refuses and names each owner/repo#<id> to rerun with. --from-machine also takes a GitHub source: only that machine is asked to resolve it.

standard plugin install resolves the source, lists every grant with its reason (high-risk grants first: machine.full, network.full, every process.exec: and fetch:), asks y/N (--yes approves without asking), uploads the package, and installs it with its source. When the account already has a plugin with that id it updates it, approves the grants you just saw, and records the new source. Without a terminal to ask and without --yes, it lists the grants and stops, saying so; review them and rerun with --yes.

A singleton daemon runs on one machine, which the install pins: --machine <id or name> names it; without it the command uses the account’s only machine or asks which one (--yes with several machines refuses and asks for --machine). Installing an installed singleton again with --machine moves it there; the Plugins view pins it too (P, then a machine and Enter).

standard plugin install ./sync --machine studio

In a viewer, Install plugin… in the command palette (or I in the Plugins view) runs the same install in one card: the source (with a machine picker and a file browser for paths), then the consent screen, which lists the plugin’s permissions with their reasons, high risk first, then the install, which ends by saying where the plugin appears. The browser viewer installs from a file on one of the account’s machines; GitHub, npm and link sources install from a native viewer or the CLI.

§Updates and approval

A plugin installed from GitHub, npm or a link updates from it automatically unless its auto-update is off (standard plugin source <id> --auto-update off, or U in the Plugins view). Nothing polls. A viewer checks when it starts and when its Plugins view opens, at most once an hour per plugin, and of several viewers starting together one checks and installs. standard plugin update [id] [--all] checks now, whatever the hour or the switch. What the source said, or why it failed, is shown in the Plugins view under the plugin, never as a popup.

standard plugin update                 # every plugin with a source
standard plugin update quota-meters
standard plugin source quota-meters    # where it updates from and the last check
standard plugin source quota-meters --auto-update off
standard plugin uninstall quota-meters # asks first; --yes to skip the question

standard plugin uninstall <id> removes the plugin from the account, the same as X in the Plugins view: it stops in every viewer and on every machine. It asks for a confirmation at the terminal; with no terminal to ask on (a script, a pipe) it refuses unless --yes is given.

An update that keeps the approved grants, or drops some, runs at once: every viewer starts the new version beside the old one and swaps to it once it has activated. An update that adds a grant waits for approval: the Plugins view (command palette, Plugins) shows a card with the added grants and their reasons; Enter approves exactly the update’s grants, D keeps the installed version. A newer version a check found (with auto-update off) shows as available; Enter installs it through the install dialog.