Each release follows the current upstream runtime and provider schema. The lock file records the verified installer for reproducible builds; it is not a promise to support older runtime versions.
This document covers the maintainer-only workflows for synchronizing the upstream runtime, building the npm tarball, and publishing releases. End users do not need any of this — see the main README for installation and usage.
The same path used by CI can be run locally:
brew install sevenzip
bun run sync -- --platform linux --arch x64Use the committed artifact instead of the latest updater manifest when rebuilding a reviewed release:
bun run sync:lockedThe synchronization command:
- reads the public stable-channel manifest used by ZCode Desktop, with the static CDN manifest as a fallback only when the service response is unavailable or invalid;
- downloads the matching installer;
- verifies its SHA-512 from the manifest;
- extracts
resources/glm; - applies the version-independent runtime patch plan and injects the local
@zcode/tuiadapter, retaining upstream text and marking the runtime as modified; - extracts the strict global CLI option contract and validates the official CLI version;
- records provenance, CLI capabilities and each patch result in
vendor/extraction.json; - records the remote artifact URL and SHA-512 in
zcode-runtime.lock.json; - aligns the npm version prefix with the ZCode App version while preserving the independently incremented CLI build revision.
The published package is controlled by the files allowlist in package.json.
It contains only:
bin/zcode.js, the bundled executable Node.js launcher;vendor/, the extracted and patchedzcode.cjsruntime, included built-in plugins (including document, PDF, presentation and spreadsheet plugins with their original licenses) and the compiled local@zcode/tuiadapter;setting.example.json,provider.example.jsonandzcode-runtime.lock.json;- the English and Simplified Chinese configuration guides and provider field
references in
docs/; docs/THIRD_PARTY_CONTENT.mdand the license texts, upstream notices and source provenance inLICENSES/;README.md,LICENSEand the required npmpackage.json.
Tests, GitHub workflows, build scripts, launcher/TUI TypeScript sources, local
config, .release/ artifacts and development node_modules are not published.
npm installs the declared pi-tui and playwright-core dependencies. The launcher and TUI
are compiled to JavaScript with tsdown; its launcher banner adds the Node.js
shebang directly, with no post-build rewrite. The compiled TUI is injected into
vendor/ before publication.
To request a preview of an open PR (including a contributor's fork), kingsword09 posts a new PR conversation comment containing exactly:
/pkg-pr-new
Only GitHub user ID 19650362 may request PR previews. Other commenters,
edited comments, commands in code blocks, regular issues, and reruns by other
accounts are ignored or rejected. New PR commits do not publish automatically;
post the command again to test the updated commit.
The comment workflow must first be merged into main: GitHub runs
issue_comment workflows from the default branch. The owner can also run
Publish commit preview from Actions on main, with an optional
pull_request number. An empty number previews main. Pushes to main retain
their automatic preview builds.
.github/workflows/release-commit.yml resolves and pins the PR's head SHA in a
trusted authorization job, builds that exact commit with a read-only token,
runs the release checks, and install-tests the npm tarball. The exact tested
tarball is uploaded to pkg.pr.new without repacking it. PR scripts receive no
repository-write or npm-publishing credentials. A separate job running only
the trusted workflow revision posts the result and logs link back to the PR.
Unauthorized comments cannot cancel an active preview build.
Install the pkg-pr-new GitHub App on this
repository before the first preview publication. No npm token or npm publish
permission is needed. The publisher is pinned in devDependencies and bun.lock.
The workflow uses pkg.pr.new's default compact URLs and posts the actual returned URL as the install command, followed by the full repository URL for the same commit. If pkg.pr.new falls back to a full URL, that returned URL is used directly. Publication failures report their logs without an install link. Use the link in that comment or the workflow summary to test an existing session:
npx --yes https://pkg.pr.new/zcode-app-cli@<commit-sha> --resume <session-id>Use the exact URL emitted by the successful workflow. This runs the preview
without replacing the globally installed CLI. It uses the user's normal session
store, so the tester can verify their affected sessions. Record the preview URL
with the test result: preview tarballs retain the source package version, while
their URLs identify the commit. Mutable branch and PR-number aliases are not
used by comment-triggered previews. They do not update npm's latest tag or create
a release tag. The tested tarball is also retained as a workflow artifact for
14 days, including when pkg.pr.new publication fails.
Package versions use <app-version>-<build>, for example 3.3.5-2. The prefix
tracks the upstream ZCode App. The globally increasing build revision tracks
fixes and features in this project. Do not use 3.3.5+build.2: SemVer ignores
+build metadata when comparing upgrades.
Increment the project revision before publishing a local fix or feature:
bun run version:buildThe release workflows normally perform that increment for you. The command is also available for local inspection and exceptional manual preparation.
Publishing is split into two workflows so the committed version, npm package, Git tag and GitHub Release all describe the same release:
.github/workflows/prepare-release.ymlextracts and validates the current official runtime, then opens or updates a Release PR containing the exactpackage.jsonversion andzcode-runtime.lock.jsonbuild input;- a maintainer reviews and merges that PR;
.github/workflows/publish.ymlchecks out its merge commit, rebuilds the exact locked runtime, audits and install-tests the tarball, publishes through npm Trusted Publishing, then createsv<version>and the corresponding GitHub Release.
The generated vendor/ directory remains ignored by Git and is rebuilt in both
workflows. Its updater URL and SHA-512 are committed in
zcode-runtime.lock.json. Preparation resolves the latest manifest; publishing
downloads the committed URL and verifies the locked SHA-512, so a later upstream
update cannot silently change a reviewed release.
The scheduled workflow follows the Desktop stable channel (channel=1) without
credentials or a personal device_mid. Static latest-*.yml files can lag the
service and are therefore recovery inputs, not the primary update signal. If
upstream rolls its stable channel back, synchronization keeps a newer committed
lock instead of silently downgrading it; adopting a rollback requires an
explicit maintainer review of zcode-runtime.lock.json.
The preparation workflow checks upstream once per day at 01:30 in the
Asia/Shanghai timezone, in upstream mode. GitHub documents scheduled
triggers as best effort:
under high Actions load a scheduled event can still be delayed or dropped. The
preparation itself is idempotent:
the fixed release branch is created or updated only when the runtime lock or
package version changes. A same-version upstream repack increments the global
build so npm still receives an immutable new version. From the Actions page,
run Prepare ZCode CLI release with one of these modes:
cliincrements the global build and also aligns with the latest App;upstreamchecks for an App update without incrementing the build.
The modes use release/zcode-cli and release/zcode-upstream respectively. If
both PRs are open, merge one and rerun the other preparation mode so its version
is recalculated from the new main branch.
Runtime patches are registered as required or optional. Required bridge,
OAuth, HTTP and public CLI contract failures stop preparation. Optional
diagnostics and presentation fixes record a skipped capability and allow the
validated runtime to continue. Session cache aggregation lives in the local TUI
and reads persisted messages through the adapter, so upstream minifier symbol
changes do not affect it.
app-server-standalone-auth is required. It enables local CLI credentials by
default while preserving explicit Host authentication. Missing or ambiguous
protocol anchors and partial auth injections stop synchronization; a release
must not silently lose account-backed app-server turns. Runtime tests exercise
both credential owners against a local HTTPS model fixture.
If a required compatibility check fails, synchronization writes JSON and
Markdown reports under .release/. The scheduled workflow uploads both files
and creates or updates the fixed Automated upstream runtime compatibility
failure issue. A later successful scheduled validation closes that issue.
This separates an upstream incompatibility from download, checksum, permission
and publishing failures without weakening the release gate. Discovery failures
are still uploaded for diagnosis, but they do not create a compatibility issue.
The workflow also runs a least-privilege keepalive job on scheduled events. It calls GitHub's workflow-enable API instead of creating dummy commits, which prevents the public-repository 60-day inactivity rule from disabling this schedule. This cannot eliminate platform-wide outages or dropped events; for a hard delivery deadline, use an external scheduler or run the workflow manually:
gh workflow run prepare-release.yml --ref main -f kind=upstream
gh workflow enable prepare-release.ymlMerging either Release PR publishes automatically. Publish ZCode CLI
release can also be started manually for recovery. Its publish checkbox can
be disabled to run all validation and consistency checks without changing npm,
Git tags or GitHub Releases. Publication, tag creation and GitHub Release
creation are independently idempotent, so a partially completed run can be
retried safely.
Local packaging uses the same commands as the publishing workflow. Start from the exact clean commit whose version will be published:
bun install --frozen-lockfile
bun run release:build
git diff --exit-code -- package.json zcode-runtime.lock.json
bun run release:packrelease:build runs TypeScript checking and all tests, downloads the artifact
from zcode-runtime.lock.json, verifies its SHA-512, builds and injects the TUI,
then runs runtime and PTY smoke tests. release:pack runs the offline
prepack guard, creates .release/zcode-app-cli-<version>.tgz, audits every
included path and executable mode, installs it into a temporary directory, and
runs the installed zcode --version. It also checks the runtime modification
notice and initializes the included plugins using an isolated temporary home,
verifying that Browser Use and the four document plugins are enabled and their
original license files are present. Installation is limited to three minutes,
the version check to 15 seconds, and plugin initialization to 30 seconds;
timed-out checks terminate their subprocesses before cleanup. Only after all
checks pass are the final size, integrity and file count written to
.release/release.json.
Inspect that manifest and then publish explicitly:
npm login
npm publish --access public --tag latest --provenance=falseThe final npm publish intentionally remains explicit to avoid an accidental
registry mutation. It reruns the same offline prepack guard and fails if the
compiled TUI, runtime provenance, lock file, launcher permissions or package
allowlist are stale. --provenance=false applies only to this local bootstrap
path; GitHub OIDC generates provenance automatically.
Publish from the repository root as shown, rather than passing the .tgz to
npm publish: directory publication lets npm record the current Git gitHead,
which the recovery workflow later verifies. The tarball is the audited preview
and install-test artifact.
If the package does not exist on npm yet, bootstrap it once from the exact
committed main revision using the local release build above. If its Git diff
check fails, do not publish from that working tree; refresh the Release PR or
restore the committed version and lock first.
Synchronization preserves the build when the upstream App version changes. For
example, syncing 3.3.5-12 against ZCode App 3.4.0 produces 3.4.0-12.
Before enabling publication:
- confirm that
zcode-app-cliis the npm package name you control; - review the licenses of the selected Desktop artifact and its dependencies.
The public first-party source is Apache-2.0, but its source revision is not
the provenance record for the extracted binary. Keep the license and copied
notices in
LICENSES/, the runtime modification notice, and the artifact metadata. The four document-related plugins are included in this project's non-commercial distribution with their original skill license files. See Third-party content; - under the GitHub repository's Settings → Actions → General, enable Allow GitHub Actions to create and approve pull requests;
- open the package on npmjs.com and select Settings → Trusted Publisher → GitHub Actions;
- enter the GitHub organization or user, repository, and workflow filename
publish.yml; leave the environment name empty because this workflow does not use a GitHub Environment, and selectnpm publishunder Allowed actions; - save the publisher, prepare a
clirelease, and merge its Release PR to verify an OIDC publication.
The publisher runs on a GitHub-hosted runner with Node 24, grants
id-token: write, and updates npm to the current release. It never reads an
NPM_TOKEN repository secret; npm attaches provenance from the OIDC identity.
After verifying the first OIDC release, npm recommends setting Publishing
access to require 2FA and disallow tokens, then revoking any obsolete
automation token.
The publisher skips an identical existing version, refuses older versions,
verifies every existing npm release's gitHead, and refuses to reuse a tag
that points at another commit. The latest dist-tag therefore advances only
to the newest validated App-plus-build release.