Files
CIAgent 135ea21a61 merge: hotfix/fresh-box-experience → main (v0.3.5 fresh-box experience)
Fixes the v0.3.4 fresh-box failures: silent CLI under bare-word PATH
invocation (SEA argv detection), bootstrap dying on venv creation without an
actionable hint (poisoned-partial-venv recovery + apt hint + doctor
venv-capability probe + preflight), installer falsely 'verifying' a silent
binary, and localhost-only server binding (network mode: 0.0.0.0 + wildcard
CORS + hostname-derived API URL — remote browsing zero-config).

38 CLI tests, 3 web tests, 409 ai-service tests green; build/typecheck/lint
clean.

---ci---
phase: hotfix
milestone: v0.4
status: complete
type: hotfix
requirements:
  covered: [REQ-4-001, REQ-4-002, REQ-4-003, REQ-4-004, REQ-4-005]
  partial: []
---/ci---
2026-09-13 00:05:37 +00:00
..

@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