Publishing
Runbook for publishing pi-mistral-native to npmjs.org (public, owner-executed).
The package publishes under the unscoped name pi-mistral-native with a
minimal-surface tarball: the compiled extension, README, changelog, banner
image, and license, with pi-package in keywords so pi’s package gallery
can list it.
Publishing is an owner action. Agents and CI never publish; they document this runbook, keep the pipeline green, and verify everything short of the publish itself.
Tarball surface (manifest decision)
Section titled “Tarball surface (manifest decision)”The files array ships dist/extensions, assets/banner.png, README.md,
CHANGELOG.md, LICENSE, and the pi manifest points at the built entry
points (pi.extensions: ["dist/extensions/*.js"]):
- pi resolves package resources via the
pimanifest, paths relative to the package root, globs included — built JavaScript works and is whatnpm testactually verified. - Shipping the compiled
dist/keeps the published artifact identical to the tested artifact and does not rely on pi’s TypeScript transpilation in foreign installs; shippingextensions/*.tssources instead would work too (pi loads TS directly) but duplicates the runtime artifact next tomainand leaves the tarball’s entry points untested-by-build. filesnamesdist/extensions, notdist, so the compiled test suite (dist/test/*.test.js, used bynpm test) stays out of the tarball.CHANGELOG.mdships because pi shows package changelogs under “Extension Updates” after an update, reading them from the package root; without the file in the tarball users see nothing after upgrading.assets/banner.pngships so the README’s relative banner image resolves in every renderer that serves the readme from the package (registry previews read the shipped files, not the repository).pi.imagein thepimanifest additionally gives the pi gallery a preview image; the gallery does not render SVG, hence the PNG (the animated SVG stays in the repository for contexts that render it well).exportsis deliberately absent: pi resolves the package via thepimanifest, not the Node module resolution of consumers;mainremains as a conventional pointer for humans.peerDependenciesstay at"*"for@earendil-works/pi-ai,@earendil-works/pi-coding-agent, andtypebox; host packages are never bundled. Theprepackscript runs the build so the tarball cannot ship stale or missingdist/output.
Decisions: ADR 0009 — Ship built dist as the tarball surface
-
Preconditions — on
main, clean working tree, all checks green:Terminal window npm run checknpm testnpm pack --dry-run # verify the file list before packing -
Login check — never publish from CI or with a token committed to the repository:
Terminal window npm whoami # must print the intended npm account -
Versioning — releases follow semantic versioning. Bump the version with
npm version(which also tags the commit), not by hand:Terminal window npm version patch # or minor / major / prereleaseWhen the bump already landed via a release branch (as in a chore wave),
npm versioncannot be used — it would bump past the shipped version. Create the tag explicitly at the publish point instead:Terminal window git tag v<version> && git push origin v<version>Do not bump versions ahead of time; the version in
package.jsonis the published version. npmjs does not allow overwriting a published version — see Unpublish below — so every publish needs its own version. -
Pack and inspect —
npm pack(theprepackscript rebuildsdist/); open the tarball if anything looks off:Terminal window npm packtar -tzf pi-mistral-native-<version>.tgz -
Publish — public access from the repository root:
Terminal window npm publish --access publicIf the publishing account has two-factor authentication enabled, npm requires an OTP for the publish:
Terminal window npm publish --access public --otp=<one-time-password> -
Smoke test the published package — fresh scratch directory, no local checkout in scope.
pi -e npm:…loads the package for one invocation without touching~/.pi/agent/settings.json:Terminal window mkdir /tmp/pi-mistral-smoke && cd /tmp/pi-mistral-smokepi -e npm:pi-mistral-native --list-models # provider appears once a credential resolvesrm -rf /tmp/pi-mistral-smokeFor a persistent install (personal scope), clean up with
pi remove npm:pi-mistral-nativeafterwards.The smoke test must go through the npm registry. Installing from a local tarball (
pi install <path-to-tarball>) is not a valid smoke test for an npm-published package: a local tarball does not exercise the npm registry resolution path, so it can pass even when the published package is broken or stale. The only valid check that the package actually arrived on npmjs ispi install npm:pi-mistral-native(or thepi -eone-shot form above), run after a realnpm publish. This was learned the hard way: a local-tarball install verifies packing, not publishing. -
Tag and release —
npm versionalready created the tag; push it and optionally create a GitHub release forv<version>onhttps://github.com/awerner/pi-mistral-nativewith the tarball attached:Terminal window git push origin main --follow-tags
Unpublish
Section titled “Unpublish”npmjs does not allow overwriting a published version. If a publish turns out to be broken:
- Within 72 hours of publishing,
npm unpublish pi-mistral-native@<version>can remove a single broken version without special conditions. - After that window, unpublishing is restricted by npm policy: removing the
package entirely requires
npm unpublish pi-mistral-native --force, and npm may still serve the cached content for a while. Unpublished versions cannot be reused for a different tarball. - The practical rule: bump the version and publish a fixed release rather
than unpublish-republish cycles;
npm unpublishis for mistakes.
Local install verification (without publishing)
Section titled “Local install verification (without publishing)”pi install accepts npm registry, git, or a local directory as sources —
a raw tarball is not one (pi registers only the path string and the reload
fails on .tgz). Verify from a packed tarball by unpacking it into a
scratch directory and pointing pi at that directory (equivalent to what
npm publishes):
npm pack # produces pi-mistral-native-<version>.tgzmkdir /tmp/pi-mistral-verify && cd /tmp/pi-mistral-verifytar -xzf /path/to/pi-mistral-native-<version>.tgz # unpacks into ./packageMISTRAL_API_KEY=… pi -e ./package --list-models | grep mistral-nativerm -rf /tmp/pi-mistral-verifyThe provider appears in --list-models only once pi can resolve a
credential for it (MISTRAL_API_KEY or a stored mistral-native API key).
Note the same silent-failure mode as in a local checkout: if the manifest
glob matches no file (e.g. a stale tarball without dist/extensions/), pi
loads nothing and only the missing provider in --list-models hints at it.
- Never publish from a branch other than
mainpost-merge, and never withnpm run checkornpm testfailing. - Never commit an
.npmrcwith a registry token or OTP; usenpm whoami/ environment-level login state. - Never add the SDK or other runtime dependencies: the minimal surface is the point.
- Never publish with agent-controlled credentials: npm credentials stay with the owner, on the owner’s machine; agents document and verify only.
Decisions: ADR 0008 — Direct fetch over vendor SDK