Files
nextcraft/apps/cli/README.md
T
CIAgent 1a88ab3199
Release / release (push) Successful in 3m45s
CI / python (push) Successful in 1m52s
CI / node (push) Successful in 1m0s
merge: milestone/v0.8-ship-anywhere -> main (v0.8 complete — Ship Anywhere)
The v0.8 milestone: installable on any Debian-based server via docker
(Gitea registry image, honest sandbox degrade), deb (amd64+arm64,
advisory postinst), and arch-aware binary — with release consistency
enforced by pre-commit hooks + Gitea Actions (3-runner fleet on CT 113,
machine-made releases with the full 8-asset matrix + registry image), the
CLI carrying the full suite (guide, channel-aware doctor), and the admin
runbook (deploy/README.md, evidence-backed recipes).

---ci---
phase: 6
milestone: v0.8
status: complete
requirements:
  covered: [REQ-8-001, REQ-8-002, REQ-8-003, REQ-8-004, REQ-8-005]
  partial: []
---/ci---
2026-09-29 16:04:40 +00:00

3.3 KiB

@nextcraft/cli — nextcraft

The bootstrap CLI for the Nextcraft monorepo, shipped as a self-contained linux x64 binary (Node SEA) on every release.

Commands

See the root README quickstart for the user-facing flow. Internals:

  • src/index.ts — argv dispatch, exit-code contract (0 ok / 1 failure / 2 usage), direct-run guard (argv[0] === argv[1] detects SEA context — the installer renames the binary, so filename matching is unreliable)
  • src/commands/ — doctor / bootstrap / verify / dev; all orchestration delegates to apps/ai-service/scripts/*.sh via src/lib/spawn.ts (array-args only, SIGTERM→SIGKILL timeout ladder)
  • src/checks/ — pure logic: version compare, .env template diff
  • tests/ — node:test suites: dispatch, checks, spawn, command stubs, real-box doctor integration, install.sh fixture-server E2E (tamper rejection, degradation), release-assets token isolation, fresh-clone E2E

Build

pnpm cli:typecheck          # tsc --noEmit
pnpm cli:test               # node:test suites
pnpm cli:build              # tsc -p tsconfig.build.json -> dist/
pnpm --filter @nextcraft/cli build:binary <tag>   # SEA binary + sha256 sidecar

build:binary <tag>: esbuild bundle (CJS, node18 target, version stamped via NEXTCRAFT_VERSION_STAMP define — --version reports the tag it was built as) → node --experimental-sea-config → postject injection into a copy of the system node binary → dist/nextcraft-linux-x64 + dist/nextcraft-linux-x64.sha256. The binary runs without node on PATH (runtime embedded, ~117 MB).

Release pipeline

Every ship from v0.3.2 onward runs scripts/release-assets.sh <tag> after tag+merge:

  1. Builds the binary stamped with the tag
  2. Resolves GITEA_TOKEN from .env* files ONLY (.ciagent/.env.secrets first) — never from shell env
  3. Attaches nextcraft-linux-x64 + nextcraft-linux-x64.sha256 to the Gitea release (bounded retry, best-effort — never blocks the ship)

scripts/install.sh (POSIX sh, dash-safe): platform gate → Gitea latest-release API resolve → exact-name asset match → sha256 verify BEFORE install (mismatch = hard stop) → ~/.local/bin install → PATH hint. Any failure degrades to printed source-bootstrap instructions.

Secrets policy

The CLI never generates, writes, or echoes secrets. bootstrap copies .env.example → .env only when absent and warns on missing optional keys (mock providers keep the stack runnable keyless). Real keys live only in gitignored .ciagent/.env.secrets, exported by apps/ai-service/scripts/dev.sh.

Troubleshooting

Symptom Cause / fix
pnpm not found in doctor corepack enable pnpm (installs to ~/.local/bin — ensure PATH includes it)
doctor passes but verify fails on venv re-run nextcraft bootstrap (venv/pip resolution is idempotent)
port 8420 busy in verify stop the process on :8420 (kill $(lsof -t -i:8420)) or set AI_PORT
install.sh says "no binary assets yet" release predates the binary pipeline (pre-v0.3.2); use source bootstrap
Binary silent after rename fixed since v0.3.2 (SEA argv detection); re-download the latest release
Checksum mismatch on install do NOT run the download; delete it and retry — report if it persists