@camstack/agent 1.1.58 → 1.1.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-2P5A5KTL.mjs → chunk-54PFYHKN.mjs} +2 -2
- package/dist/{chunk-DIBZT6GT.mjs → chunk-ADZNLSIO.mjs} +61 -33
- package/dist/chunk-ADZNLSIO.mjs.map +1 -0
- package/dist/cli.js +60 -32
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/index.js +60 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/starter.js +60 -32
- package/dist/starter.js.map +1 -1
- package/dist/starter.mjs +1 -1
- package/package.json +4 -4
- package/dist/chunk-DIBZT6GT.mjs.map +0 -1
- /package/dist/{chunk-2P5A5KTL.mjs.map → chunk-54PFYHKN.mjs.map} +0 -0
package/dist/starter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../node-root/src/index.ts","../../node-root/src/dev-uploads.ts","../../node-root/src/root-package-spec.ts","../../node-root/src/root-state.ts","../../node-root/src/root-single-copy.ts","../../node-root/src/boot-plan.ts","../../node-root/src/semver-compare.ts","../../node-root/src/workspace-detect.ts","../../node-root/src/starter-core.ts","../../node-root/src/root-update-service.ts","../src/starter.ts"],"sourcesContent":["/**\n * @camstack/node-root — the shared, zero-runtime-dep node-root boot core.\n *\n * PRIVATE workspace package: it is BUNDLED at build time into the hub's\n * `dist/server-root/index.js` (tsup) and into the agent's tsup output —\n * never published, never resolved from node_modules at runtime. Both node\n * starters and both update services are thin per-node adapters over these\n * modules, so the boot-plan / probation / rollback semantics can never\n * drift between the hub and the agent.\n *\n * Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md\n */\nexport * from './dev-uploads.js'\nexport * from './root-package-spec.js'\nexport * from './root-state.js'\nexport * from './root-single-copy.js'\nexport * from './boot-plan.js'\nexport * from './semver-compare.js'\nexport * from './workspace-detect.js'\nexport * from './starter-core.js'\nexport * from './root-update-service.js'\n","/**\n * Dev server-deploy channel — layout + manifest for locally-uploaded root\n * package closures (ZERO-DEP: node:fs/node:path only, same constraint as\n * root-state.ts — the baked starter bundles this module).\n *\n * `camstack deploy-server` packs every workspace package in the server's\n * `@camstack/*` closure at one uniform dev version\n * (`<base>-dev.<epochSeconds>`) and uploads the tarballs to\n * `POST /api/server-upload`. The hub stores them under:\n *\n * <dataDir>/server-root/dev-uploads/<version>/\n * <flattened-pkg-name>.tgz one per uploaded package\n * manifest.json { version, packages: { name → filename } }\n *\n * `RootUpdateService.stageAndActivate` detects the dir and stages from these\n * tarballs (synthetic package.json with `file:` refs + `overrides`) instead\n * of the npm registry. Everything downstream — probation boot, promote,\n * rollback — is version-string driven and unchanged; the `-dev.` suffix is\n * self-describing.\n *\n * Spec: docs/superpowers/specs — dev server-deploy channel (2026-07-13).\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nexport const DEV_UPLOADS_DIRNAME = 'dev-uploads'\nexport const DEV_UPLOAD_MANIFEST_FILE = 'manifest.json'\n\n/** Number of dev-uploads entries retained by the promote-time sweep. */\nexport const DEV_UPLOADS_KEEP_COUNT = 3\n\n/**\n * Dev-channel version shape: `<semver-base>-dev.<epochSeconds>`. The suffix\n * is sortable, self-describing, and can never collide with a published\n * registry version. The upload route and the staging branch BOTH gate on it.\n */\nconst DEV_CHANNEL_VERSION_RE = /-dev\\.\\d+$/\n\nexport function isDevChannelVersion(version: string): boolean {\n return DEV_CHANNEL_VERSION_RE.test(version)\n}\n\n/**\n * The `<epochSeconds>` ordering key of a dev-channel version (or dir name).\n * Returns null for anything that is not dev-channel shaped — the sweep\n * treats those as oldest so malformed residue is collected first.\n */\nexport function devChannelEpoch(version: string): number | null {\n const match = /-dev\\.(\\d+)$/.exec(version)\n if (match === null) return null\n const epoch = Number.parseInt(match[1] ?? '', 10)\n return Number.isNaN(epoch) ? null : epoch\n}\n\n/** `<dataDir>/server-root/dev-uploads` */\nexport function devUploadsDir(rootDir: string): string {\n return path.join(rootDir, DEV_UPLOADS_DIRNAME)\n}\n\n/** `<dataDir>/server-root/dev-uploads/<version>` */\nexport function devUploadVersionDir(rootDir: string, version: string): string {\n return path.join(devUploadsDir(rootDir), version)\n}\n\n// ---------------------------------------------------------------------------\n// Manifest IO\n// ---------------------------------------------------------------------------\n\n/**\n * Written by the hub's `/api/server-upload` route after validating every\n * tarball; read by the staging branch. Maps each uploaded package NAME to\n * its tgz FILENAME inside the version dir — staging never has to re-derive\n * package names from flattened filenames.\n */\nexport interface DevUploadManifest {\n readonly version: string\n readonly packages: Readonly<Record<string, string>>\n}\n\nexport function devUploadManifestPath(versionDirPath: string): string {\n return path.join(versionDirPath, DEV_UPLOAD_MANIFEST_FILE)\n}\n\nfunction isDevUploadManifest(v: unknown): v is DevUploadManifest {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (typeof m['version'] !== 'string') return false\n const packages = m['packages']\n if (typeof packages !== 'object' || packages === null || Array.isArray(packages)) return false\n return Object.values(packages).every((filename) => typeof filename === 'string')\n}\n\n/** Read + validate the manifest. Returns null when missing/corrupt. */\nexport function readDevUploadManifest(versionDirPath: string): DevUploadManifest | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(devUploadManifestPath(versionDirPath), 'utf-8'))\n return isDevUploadManifest(raw) ? raw : null\n } catch {\n return null\n }\n}\n\n/** Write the manifest atomically (tmp sibling + rename). Creates the dir. */\nexport function writeDevUploadManifest(versionDirPath: string, manifest: DevUploadManifest): void {\n fs.mkdirSync(versionDirPath, { recursive: true })\n const target = devUploadManifestPath(versionDirPath)\n const tmp = `${target}.tmp`\n fs.writeFileSync(tmp, JSON.stringify(manifest, null, 2), 'utf-8')\n fs.renameSync(tmp, target)\n}\n","/**\n * Root-package spec — the per-node-type parameters of the shared node-root\n * boot machinery. Everything else (the `server-root/` data-dir layout, the\n * `state.json` schema, the probation/rollback boot plan, the update service)\n * is IDENTICAL between the hub and the agent; only the root package's name\n * and its loadable entry differ.\n *\n * Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md\n */\n\nexport interface RootPackageSpec {\n /** The npm name of the node's root package. */\n readonly packageName: string\n /**\n * Path segments of the loadable entry INSIDE the root package dir\n * (`node_modules/<packageName>/<...entryRelPath>`).\n */\n readonly entryRelPath: readonly string[]\n}\n\n/** The hub's root package — the starter loads its launcher. */\nexport const HUB_ROOT_SPEC: RootPackageSpec = {\n packageName: '@camstack/server',\n entryRelPath: ['dist', 'launcher.js'],\n}\n\n/** The agent's root package — the starter loads its CLI entry. */\nexport const AGENT_ROOT_SPEC: RootPackageSpec = {\n packageName: '@camstack/agent',\n entryRelPath: ['dist', 'cli.js'],\n}\n","/**\n * Data-dir server-root layout + state file — ZERO-DEP (node:fs/node:path only).\n *\n * Loaded by each node's baked STARTER before any node_modules resolution is\n * trusted, so this module must never import from `@camstack/*` (runtime) or\n * any npm package. The hub's `ServerUpdateService` and the agent's update\n * service consume the same module — one source of truth for the layout and\n * the state shape. The layout is IDENTICAL on both node types (the dir is\n * named `server-root/` everywhere); only the root package name + entry differ\n * and arrive via {@link RootPackageSpec}.\n *\n * Layout (`<dataDir>/server-root/`):\n * versions/<semver>/ npm-installed closure for one version\n * node_modules/<pkg>/<entry> ← the loadable entry (spec-defined)\n * state.json pointer file (ServerRootState, atomic)\n *\n * A POINTER FILE (state.json) is used instead of `current`/`previous`\n * symlinks: atomic via tmp+rename, works on every fs, and carries the\n * probation/rollback bookkeeping in the same durable record.\n *\n * Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { RootPackageSpec } from './root-package-spec.js'\n\nexport const SERVER_ROOT_DIRNAME = 'server-root'\nexport const SERVER_ROOT_STATE_FILE = 'state.json'\n/**\n * Restart-intent marker filename. Written by the update service moments before\n * a graceful self-restart (apply/rollback), then read + consumed by an\n * external process supervisor so it can tell an INTENTIONAL restart-for-update\n * apart from a crash — a crash-backoff policy must never penalise an update\n * restart. Lives in the server-root dir next to `state.json`.\n *\n * (Phase 3 — the Electron shell supervisor reads it; the docker restart policy\n * relaunches unconditionally so the marker is a harmless no-op there.)\n */\nexport const RESTART_INTENT_FILE = '.restart-intent'\n\n// ---------------------------------------------------------------------------\n// State shape\n// ---------------------------------------------------------------------------\n\nexport interface ServerRootPendingBoot {\n /** The freshly-staged version awaiting its probation boot. */\n readonly version: string\n /** The version that was current when the update was requested (null = baked). */\n readonly fromVersion: string | null\n readonly requestedAtMs: number\n /** 0 = not yet booted; >=1 = a probation boot started and never confirmed. */\n readonly bootAttempts: number\n}\n\nexport interface ServerRootRollbackInfo {\n readonly fromVersion: string\n /** null = rolled back all the way to the baked seed. */\n readonly toVersion: string | null\n readonly atMs: number\n readonly reason: string\n}\n\nexport interface ServerRootState {\n readonly schemaVersion: 1\n /** The confirmed-healthy active version (null = boot from the baked seed). */\n readonly currentVersion: string | null\n /** N-1 kept for rollback (its versions/ dir is retained). */\n readonly previousVersion: string | null\n readonly pendingBoot: ServerRootPendingBoot | null\n /** Set when the most recent update attempt was rolled back. */\n readonly rolledBack: ServerRootRollbackInfo | null\n}\n\nexport function emptyServerRootState(): ServerRootState {\n return {\n schemaVersion: 1,\n currentVersion: null,\n previousVersion: null,\n pendingBoot: null,\n rolledBack: null,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Layout helpers\n// ---------------------------------------------------------------------------\n\nexport function serverRootDir(dataDir: string): string {\n return path.join(dataDir, SERVER_ROOT_DIRNAME)\n}\n\nexport function versionsDir(rootDir: string): string {\n return path.join(rootDir, 'versions')\n}\n\nexport function versionDir(rootDir: string, version: string): string {\n return path.join(versionsDir(rootDir), version)\n}\n\n/** `versions/<v>/node_modules/<packageName>` — the installed root package dir. */\nexport function rootPackageDir(versionDirPath: string, spec: RootPackageSpec): string {\n return path.join(versionDirPath, 'node_modules', ...spec.packageName.split('/'))\n}\n\n/** The loadable entry inside the installed root package dir. */\nexport function rootEntryPath(versionDirPath: string, spec: RootPackageSpec): string {\n return path.join(rootPackageDir(versionDirPath, spec), ...spec.entryRelPath)\n}\n\nexport function stateFilePath(rootDir: string): string {\n return path.join(rootDir, SERVER_ROOT_STATE_FILE)\n}\n\n// ---------------------------------------------------------------------------\n// State IO (atomic, corruption-tolerant)\n// ---------------------------------------------------------------------------\n\nfunction isNullableString(v: unknown): v is string | null {\n return v === null || typeof v === 'string'\n}\n\nfunction isPendingBoot(v: unknown): v is ServerRootPendingBoot {\n if (typeof v !== 'object' || v === null) return false\n const p = v as Record<string, unknown>\n return (\n typeof p['version'] === 'string' &&\n isNullableString(p['fromVersion']) &&\n typeof p['requestedAtMs'] === 'number' &&\n typeof p['bootAttempts'] === 'number'\n )\n}\n\nfunction isRollbackInfo(v: unknown): v is ServerRootRollbackInfo {\n if (typeof v !== 'object' || v === null) return false\n const r = v as Record<string, unknown>\n return (\n typeof r['fromVersion'] === 'string' &&\n isNullableString(r['toVersion']) &&\n typeof r['atMs'] === 'number' &&\n typeof r['reason'] === 'string'\n )\n}\n\nexport function isServerRootState(v: unknown): v is ServerRootState {\n if (typeof v !== 'object' || v === null) return false\n const s = v as Record<string, unknown>\n if (s['schemaVersion'] !== 1) return false\n if (!isNullableString(s['currentVersion'])) return false\n if (!isNullableString(s['previousVersion'])) return false\n if (s['pendingBoot'] !== null && !isPendingBoot(s['pendingBoot'])) return false\n if (s['rolledBack'] !== null && !isRollbackInfo(s['rolledBack'])) return false\n return true\n}\n\n/** Read + validate the state file. Returns null when missing/corrupt/mismatched. */\nexport function readServerRootState(rootDir: string): ServerRootState | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(stateFilePath(rootDir), 'utf-8'))\n return isServerRootState(raw) ? raw : null\n } catch {\n return null\n }\n}\n\n/** Write the state file atomically (tmp sibling + rename). Creates rootDir. */\nexport function writeServerRootState(rootDir: string, state: ServerRootState): void {\n fs.mkdirSync(rootDir, { recursive: true })\n const target = stateFilePath(rootDir)\n const tmp = `${target}.tmp`\n fs.writeFileSync(tmp, JSON.stringify(state, null, 2), 'utf-8')\n fs.renameSync(tmp, target)\n}\n\n// ---------------------------------------------------------------------------\n// Restart-intent marker (supervisor: update-restart vs crash discrimination)\n// ---------------------------------------------------------------------------\n\n/**\n * Payload of the restart-intent marker. `requestedAtMs` lets a reader ignore a\n * STALE marker (e.g. one left behind by a hard kill between write and\n * relaunch) via a freshness window instead of mis-classifying a much-later\n * crash as an update restart. `reason` carries the human `requestedBy` string.\n */\nexport interface RestartIntentMarker {\n readonly requestedAtMs: number\n readonly reason: string\n}\n\nexport function restartIntentMarkerPath(rootDir: string): string {\n return path.join(rootDir, RESTART_INTENT_FILE)\n}\n\nfunction isRestartIntentMarker(v: unknown): v is RestartIntentMarker {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n return typeof m['requestedAtMs'] === 'number' && typeof m['reason'] === 'string'\n}\n\n/** Write the marker atomically (tmp sibling + rename). Creates rootDir. */\nexport function writeRestartIntentMarker(rootDir: string, marker: RestartIntentMarker): void {\n fs.mkdirSync(rootDir, { recursive: true })\n const target = restartIntentMarkerPath(rootDir)\n const tmp = `${target}.tmp`\n fs.writeFileSync(tmp, JSON.stringify(marker, null, 2), 'utf-8')\n fs.renameSync(tmp, target)\n}\n\n/** Read + validate the marker. Returns null when missing/corrupt. */\nexport function readRestartIntentMarker(rootDir: string): RestartIntentMarker | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(restartIntentMarkerPath(rootDir), 'utf-8'))\n return isRestartIntentMarker(raw) ? raw : null\n } catch {\n return null\n }\n}\n\n/** Delete the marker if present (idempotent — a missing file is not an error). */\nexport function clearRestartIntentMarker(rootDir: string): void {\n try {\n fs.rmSync(restartIntentMarkerPath(rootDir), { force: true })\n } catch {\n /* best-effort */\n }\n}\n\n// ---------------------------------------------------------------------------\n// Version-dir validation\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the minimum required Node major from an `engines.node` range like\n * `>=22`, `>= 22.12`, `^24.0.0`, `22.x`. Returns null when unparseable\n * (validation is then permissive — a bogus range must not brick boot).\n */\nexport function minNodeMajorOf(enginesNode: string): number | null {\n const match = /(\\d+)/.exec(enginesNode)\n if (match === null) return null\n const major = Number.parseInt(match[1] ?? '', 10)\n return Number.isNaN(major) ? null : major\n}\n\n/**\n * Validate that `closureDir` holds a loadable root-package closure for `spec`\n * (its installed package lives at `closureDir/node_modules/<packageName>`).\n * Returns null when valid, else a human-readable reason. Checks:\n * 1. the entry file exists,\n * 2. the package.json parses with the right `name` (and `version` when\n * `expectedVersion` is given),\n * 3. `engines.node` (when parseable) is satisfied by the running runtime —\n * the starter refuses a native-ABI mismatch with a clear error instead\n * of segfaulting (design risk: node bump vs native prebuilds).\n *\n * Shared by the single-copy `current/` model (`expectedVersion` omitted — the\n * version is whatever the closure carries) and the legacy `versions/<v>/`\n * model (`expectedVersion` set — the dir name must match the package.json).\n */\nexport function validateClosureDir(\n closureDir: string,\n nodeMajor: number,\n spec: RootPackageSpec,\n expectedVersion?: string,\n): string | null {\n const entry = rootEntryPath(closureDir, spec)\n if (!fs.existsSync(entry)) {\n return `root entry missing: ${entry}`\n }\n\n const pkgJsonPath = path.join(rootPackageDir(closureDir, spec), 'package.json')\n let pkg: Record<string, unknown>\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n return `package.json malformed: ${pkgJsonPath}`\n }\n pkg = raw as Record<string, unknown>\n } catch {\n return `package.json unreadable: ${pkgJsonPath}`\n }\n\n if (pkg['name'] !== spec.packageName) {\n return `package name mismatch: expected ${spec.packageName}, got ${String(pkg['name'])}`\n }\n if (expectedVersion !== undefined && pkg['version'] !== expectedVersion) {\n return `package version mismatch: expected ${expectedVersion}, package.json says ${String(pkg['version'])}`\n }\n\n const engines = pkg['engines']\n if (typeof engines === 'object' && engines !== null) {\n const enginesNode = (engines as Record<string, unknown>)['node']\n if (typeof enginesNode === 'string') {\n const minMajor = minNodeMajorOf(enginesNode)\n if (minMajor !== null && nodeMajor < minMajor) {\n return `engines.node \"${enginesNode}\" requires Node >= ${minMajor}, runtime is ${nodeMajor}`\n }\n }\n }\n\n return null\n}\n\n/**\n * Validate that `versions/<version>` holds a loadable root-package closure for\n * `spec` (legacy multi-copy model — the dir name must match the package\n * version). Thin wrapper over {@link validateClosureDir}. Returns null when\n * valid, else a human-readable reason.\n */\nexport function validateVersionDir(\n rootDir: string,\n version: string,\n nodeMajor: number,\n spec: RootPackageSpec,\n): string | null {\n return validateClosureDir(versionDir(rootDir, version), nodeMajor, spec, version)\n}\n","/**\n * Single-copy framework layout — the ONE physical copy of a node's runtime\n * root closure. ZERO-DEP (node:fs/node:path only): loaded by the baked STARTER\n * before any node_modules resolution is trusted, so it must never import from\n * `@camstack/*` (runtime) or any npm package.\n *\n * Replaces the legacy multi-copy model (`versions/<X>` + an N-1 rollback copy +\n * a separate `/data/framework` copy + a `/data/addons/@camstack/system` copy).\n * There is now exactly ONE closure:\n *\n * <dataDir>/server-root/current/ the ONE canonical closure\n * node_modules/<packageName>/<entry> ← the loadable entry\n * <dataDir>/server-root/.pending-root-swap.json a staged update awaiting boot\n * <dataDir>/server-root/.staging-* transient npm-install dir\n * <dataDir>/server-root/.trash-* transient old-copy graveyard\n *\n * hub-main, the forked addon-runners, and the framework BUILTINS all resolve\n * `@camstack/{system,types,sdk,shm-ring}` from `current/node_modules`; there is\n * no drift and no seed-vs-data-root divergence. `applyServerUpdate` stages a\n * new closure into `.staging-*`, writes the pending-root-swap marker and\n * restarts; the STARTER performs the atomic swap of `current` on the next boot\n * (nothing holds `current` open at that point) — mirroring the trusted\n * launcher-framework-swap pattern. NO auto-rollback: a bad update is recovered\n * manually (re-apply a known-good version, or reinstall the image seed).\n *\n * Spec: docs/superpowers/specs/2026-07-18-single-framework-copy-collapse-design.md\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { RootPackageSpec } from './root-package-spec.js'\nimport {\n readServerRootState,\n rootEntryPath,\n stateFilePath,\n validateClosureDir,\n versionDir,\n versionsDir,\n} from './root-state.js'\n\n// ---------------------------------------------------------------------------\n// Layout\n// ---------------------------------------------------------------------------\n\n/** Name of the single canonical closure dir under `server-root/`. */\nexport const CURRENT_DIRNAME = 'current'\n/** Marker written by `applyServerUpdate`, consumed by the starter's swap. */\nexport const PENDING_ROOT_SWAP_FILE = '.pending-root-swap.json'\n\n/**\n * Legacy `/data/framework` swap markers (the retired per-package framework\n * mechanism). Cleared during migration so a stale marker can never swap a\n * package INTO the single copy on the first collapse boot. They live in the\n * DATA dir (one level up from `server-root/`), not in `rootDir`.\n */\nconst LEGACY_FRAMEWORK_MARKERS: readonly string[] = [\n '.pending-framework-swap.json',\n '.framework-swap-confirm.json',\n]\n\n/** The single canonical closure dir for `rootDir` (= `server-root/`). */\nexport function currentDir(rootDir: string): string {\n return path.join(rootDir, CURRENT_DIRNAME)\n}\n\n/** Absolute path of the loadable entry inside the single copy. */\nexport function currentEntryPath(rootDir: string, spec: RootPackageSpec): string {\n return rootEntryPath(currentDir(rootDir), spec)\n}\n\n/** A fresh, same-fs staging dir for an npm install (sibling of `current`). */\nexport function stagingDirPath(rootDir: string, target: string, pid: number, now: number): string {\n return path.join(rootDir, `.staging-${target}-${pid}-${now}`)\n}\n\n// ---------------------------------------------------------------------------\n// Pending-root-swap marker (atomic, corruption-tolerant)\n// ---------------------------------------------------------------------------\n\nexport interface PendingRootSwap {\n readonly schemaVersion: 1\n /** The staged version awaiting its atomic swap into `current` on next boot. */\n readonly version: string\n /** Absolute path of the validated `.staging-*` closure to swap in. */\n readonly stagingPath: string\n readonly requestedAtMs: number\n}\n\nexport function pendingRootSwapPath(rootDir: string): string {\n return path.join(rootDir, PENDING_ROOT_SWAP_FILE)\n}\n\nfunction isPendingRootSwap(v: unknown): v is PendingRootSwap {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n return (\n m['schemaVersion'] === 1 &&\n typeof m['version'] === 'string' &&\n typeof m['stagingPath'] === 'string' &&\n typeof m['requestedAtMs'] === 'number'\n )\n}\n\n/** Read + validate the pending-root-swap marker. Null when missing/corrupt. */\nexport function readPendingRootSwap(rootDir: string): PendingRootSwap | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(pendingRootSwapPath(rootDir), 'utf-8'))\n return isPendingRootSwap(raw) ? raw : null\n } catch {\n return null\n }\n}\n\n/** Write the marker atomically (tmp sibling + rename). Creates rootDir. */\nexport function writePendingRootSwap(rootDir: string, marker: PendingRootSwap): void {\n fs.mkdirSync(rootDir, { recursive: true })\n const target = pendingRootSwapPath(rootDir)\n const tmp = `${target}.tmp`\n fs.writeFileSync(tmp, JSON.stringify(marker, null, 2), 'utf-8')\n fs.renameSync(tmp, target)\n}\n\n/** Delete the marker if present (idempotent). */\nexport function clearPendingRootSwap(rootDir: string): void {\n try {\n fs.rmSync(pendingRootSwapPath(rootDir), { force: true })\n } catch {\n /* best-effort */\n }\n}\n\n// ---------------------------------------------------------------------------\n// Native prebuild assertion\n// ---------------------------------------------------------------------------\n\n/**\n * Native packages whose prebuilt `.node` bindings MUST resolve from the single\n * copy — a runner that can't load them breaks decode / snapshot / the SQLite\n * store. The closure carries them as transitive deps of the root package.\n */\nexport const REQUIRED_NATIVE_PACKAGES: readonly string[] = ['better-sqlite3', 'sharp', 'node-av']\n\n/**\n * Where each native package's `.node` binding lives inside `node_modules`. The\n * prebuild often sits in a platform-suffixed SIBLING scope (sharp → `@img/…`,\n * node-av → `@seydx/…`), so we search the package dir AND its scope root.\n */\nconst NATIVE_SEARCH_DIRS: Readonly<Record<string, readonly string[]>> = {\n 'better-sqlite3': ['better-sqlite3'],\n sharp: ['sharp', '@img'],\n 'node-av': ['node-av', '@seydx'],\n}\n\n/** Bounded recursive scan for ANY `*.node` file under `dir`. */\nfunction hasDotNode(dir: string, maxDepth: number): boolean {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n return false\n }\n for (const entry of entries) {\n if (entry.isFile() && entry.name.endsWith('.node')) return true\n }\n if (maxDepth <= 0) return false\n for (const entry of entries) {\n if (entry.isDirectory() && hasDotNode(path.join(dir, entry.name), maxDepth - 1)) return true\n }\n return false\n}\n\n/**\n * Return the required native packages whose `.node` binding could NOT be found\n * under `<closureDir>/node_modules`. Empty ⇒ the closure carries every\n * required native prebuild. Used to ABORT a swap into a closure that would\n * brick the forked runners.\n */\nexport function findMissingNativePrebuilds(closureDir: string): string[] {\n const nm = path.join(closureDir, 'node_modules')\n const missing: string[] = []\n for (const pkg of REQUIRED_NATIVE_PACKAGES) {\n const searchDirs = NATIVE_SEARCH_DIRS[pkg] ?? [pkg]\n const found = searchDirs.some((rel) => hasDotNode(path.join(nm, ...rel.split('/')), 4))\n if (!found) missing.push(pkg)\n }\n return missing\n}\n\n// ---------------------------------------------------------------------------\n// Swap + migration\n// ---------------------------------------------------------------------------\n\ntype LogFn = (message: string) => void\n\nfunction rmrf(target: string): void {\n try {\n fs.rmSync(target, { recursive: true, force: true })\n } catch {\n /* best-effort */\n }\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\nexport interface ApplyRootSwapResult {\n readonly applied: boolean\n readonly version: string | null\n readonly reason: string\n}\n\n/**\n * Perform a staged root swap if a pending-root-swap marker is present. Runs in\n * the STARTER, BEFORE the closure is loaded, so nothing holds `current` open.\n *\n * Fail-CLOSED + crash-safe:\n * 1. Validate the staged closure (entry + version + native prebuilds). Any\n * failure discards the staging dir + the marker and leaves `current`\n * untouched (the operator's manual-recovery net) — never a partial swap.\n * 2. Swap: rename `current` aside to `.trash-*`, then rename `staging` →\n * `current`. If the second rename fails, restore `current` from the trash.\n * No N-1 retention — the trash is deleted immediately.\n *\n * Idempotent: a no-marker call is a no-op; a crash mid-swap self-heals on the\n * next boot (staging still present → retried; already-swapped → marker's\n * staging path is gone → the marker is discarded and the new `current` stands).\n */\nexport function applyPendingRootSwap(\n rootDir: string,\n spec: RootPackageSpec,\n nodeMajor: number,\n now: () => number,\n log: LogFn,\n): ApplyRootSwapResult {\n const marker = readPendingRootSwap(rootDir)\n if (marker === null) return { applied: false, version: null, reason: 'no pending swap' }\n\n const staging = marker.stagingPath\n const dest = currentDir(rootDir)\n\n const invalid = validateClosureDir(staging, nodeMajor, spec, marker.version)\n if (invalid !== null) {\n // Staging gone but `current` is already the target ⇒ a prior swap\n // completed and only the marker survived a crash. Accept + clear.\n if (\n !fs.existsSync(staging) &&\n validateClosureDir(dest, nodeMajor, spec, marker.version) === null\n ) {\n clearPendingRootSwap(rootDir)\n log(`[single-copy] pending swap ${marker.version} already applied — cleared marker`)\n return { applied: true, version: marker.version, reason: 'already applied' }\n }\n log(`[single-copy] staged swap ${marker.version} invalid — discarding (${invalid})`)\n rmrf(staging)\n clearPendingRootSwap(rootDir)\n return { applied: false, version: null, reason: invalid }\n }\n\n const missing = findMissingNativePrebuilds(staging)\n if (missing.length > 0) {\n log(\n `[single-copy] staged swap ${marker.version} missing native prebuilds: ${missing.join(', ')} — discarding`,\n )\n rmrf(staging)\n clearPendingRootSwap(rootDir)\n return {\n applied: false,\n version: null,\n reason: `missing native prebuilds: ${missing.join(', ')}`,\n }\n }\n\n let trash: string | null = null\n try {\n if (fs.existsSync(dest)) {\n trash = path.join(rootDir, `.trash-${now()}-${process.pid}`)\n fs.renameSync(dest, trash)\n }\n try {\n fs.renameSync(staging, dest)\n } catch (err) {\n if (trash !== null && !fs.existsSync(dest)) {\n try {\n fs.renameSync(trash, dest)\n } catch {\n /* leave it for the confirm-boot sweep */\n }\n }\n throw err\n }\n } catch (err) {\n log(`[single-copy] swap of ${marker.version} FAILED — current preserved (${errMsg(err)})`)\n return { applied: false, version: null, reason: errMsg(err) }\n }\n\n if (trash !== null) rmrf(trash)\n clearPendingRootSwap(rootDir)\n log(`[single-copy] applied staged root swap → ${marker.version}`)\n return { applied: true, version: marker.version, reason: 'applied' }\n}\n\nexport interface MigrateResult {\n readonly migrated: boolean\n readonly version: string | null\n readonly reason: string\n}\n\n/**\n * One-time migration from the legacy multi-copy layout to the single copy.\n * Idempotent + safe to run on every boot: when a valid `current` already\n * exists it is a no-op.\n *\n * On a hub that predates the collapse, `current` is absent but\n * `versions/<active>` exists (recorded by the legacy `state.json`, or\n * discoverable by scanning `versions/`). Migration ADOPTS the active version's\n * closure as the single copy (`versions/<active>` → `current`), then removes\n * the legacy `versions/` tree, the legacy `state.json`, and the legacy\n * `/data/framework` swap markers — leaving non-framework addons in\n * `/data/addons` untouched. When no valid legacy closure exists (fresh\n * install) it is a no-op and the caller boots the baked seed.\n */\nexport function migrateToSingleCopy(\n rootDir: string,\n spec: RootPackageSpec,\n nodeMajor: number,\n now: () => number,\n log: LogFn,\n): MigrateResult {\n const dest = currentDir(rootDir)\n if (fs.existsSync(dest) && validateClosureDir(dest, nodeMajor, spec) === null) {\n return { migrated: false, version: null, reason: 'current already present' }\n }\n\n // Candidate legacy versions: prefer what state.json recorded as active, then\n // fall back to whatever is on disk under versions/ (state missing/corrupt).\n const candidates: string[] = []\n const legacyState = readServerRootState(rootDir)\n if (legacyState !== null) {\n for (const v of [\n legacyState.currentVersion,\n legacyState.pendingBoot?.version ?? null,\n legacyState.previousVersion,\n ]) {\n if (typeof v === 'string' && !candidates.includes(v)) candidates.push(v)\n }\n }\n try {\n for (const entry of fs.readdirSync(versionsDir(rootDir))) {\n if (!entry.startsWith('.') && !candidates.includes(entry)) candidates.push(entry)\n }\n } catch {\n /* no versions dir */\n }\n\n const source = candidates.find(\n (v) => validateClosureDir(versionDir(rootDir, v), nodeMajor, spec, v) === null,\n )\n if (source === undefined) {\n return { migrated: false, version: null, reason: 'no valid legacy versions/<X> to adopt' }\n }\n\n // An invalid leftover `current` (partial migration) must not block the rename.\n if (fs.existsSync(dest)) {\n const aside = path.join(rootDir, `.trash-${now()}-${process.pid}-stale-current`)\n try {\n fs.renameSync(dest, aside)\n } catch {\n rmrf(dest)\n }\n }\n\n try {\n fs.renameSync(versionDir(rootDir, source), dest)\n } catch (err) {\n log(`[single-copy] migration rename of versions/${source} failed (${errMsg(err)})`)\n return { migrated: false, version: null, reason: errMsg(err) }\n }\n\n // Retire the legacy machinery (best-effort — none of these can brick boot).\n rmrf(versionsDir(rootDir))\n rmrf(stateFilePath(rootDir))\n const dataDir = path.dirname(rootDir)\n for (const marker of LEGACY_FRAMEWORK_MARKERS) rmrf(path.join(dataDir, marker))\n log(`[single-copy] migrated legacy versions/${source} → current`)\n return { migrated: true, version: source, reason: 'adopted legacy version' }\n}\n\n/**\n * Best-effort sweep of orphaned transient dirs (`.staging-*` / `.trash-*`)\n * older than `staleMs`. Called post-boot once the node is confirmed healthy —\n * a concurrent `applyServerUpdate` may be mid-install into a FRESH `.staging-*`\n * dir, so only dirs whose mtime is older than the window are collected.\n */\nexport function sweepTransientRootDirs(rootDir: string, now: number, staleMs: number): void {\n let entries: string[]\n try {\n entries = fs.readdirSync(rootDir)\n } catch {\n return\n }\n for (const entry of entries) {\n if (!entry.startsWith('.staging-') && !entry.startsWith('.trash-')) continue\n const full = path.join(rootDir, entry)\n try {\n if (now - fs.statSync(full).mtimeMs < staleMs) continue\n } catch {\n continue\n }\n rmrf(full)\n }\n}\n","/**\n * Pure boot-plan decision for the server-root starter — ZERO-DEP.\n *\n * Single-copy model (2026-07-18 collapse): the swap + migration have already\n * run by the time this is consulted, so the decision reduces to \"is the ONE\n * canonical `current/` closure present and valid?\". If yes → boot it; if no\n * (fresh install, or a corrupt/absent copy) → boot the baked image seed.\n *\n * The probation / auto-rollback / N-1 / baked-seed-adoption machinery of the\n * legacy multi-copy model is GONE: there is no second copy to compare against\n * or roll back to (operator's accepted tradeoff — manual recovery only).\n *\n * Spec: docs/superpowers/specs/2026-07-18-single-framework-copy-collapse-design.md\n */\n\nexport type BootPlan =\n | { readonly kind: 'current'; readonly reason: string }\n | { readonly kind: 'baked'; readonly reason: string }\n\n/**\n * Decide the boot target from a single boolean: whether the canonical\n * `current/` closure validated. Never throws.\n */\nexport function planBoot(currentValid: boolean): BootPlan {\n if (currentValid) {\n return { kind: 'current', reason: 'single-copy closure present and valid' }\n }\n return { kind: 'baked', reason: 'no valid single-copy closure — booting the baked seed' }\n}\n","/**\n * Minimal semver comparison — zero-dep (used by the starter, which must never\n * import from node_modules). Handles `MAJOR.MINOR.PATCH[-prerelease]`:\n * numeric segment compare, missing segments = 0, prerelease < its release,\n * prereleases of the same base compare lexically. Malformed segments compare\n * as 0 (best effort — never throws).\n */\n\ntype SemverParts = {\n readonly nums: readonly number[]\n readonly prerelease: string | null\n}\n\nfunction parse(version: string): SemverParts {\n const dashIdx = version.indexOf('-')\n const base = dashIdx === -1 ? version : version.slice(0, dashIdx)\n const prerelease = dashIdx === -1 ? null : version.slice(dashIdx + 1)\n const nums = base.split('.').map((seg) => {\n const n = Number.parseInt(seg, 10)\n return Number.isNaN(n) ? 0 : n\n })\n return { nums, prerelease }\n}\n\n/** Returns -1 when a < b, 0 when equal, 1 when a > b. */\nexport function compareSemver(a: string, b: string): -1 | 0 | 1 {\n const pa = parse(a)\n const pb = parse(b)\n const len = Math.max(pa.nums.length, pb.nums.length)\n for (let i = 0; i < len; i++) {\n const na = pa.nums[i] ?? 0\n const nb = pb.nums[i] ?? 0\n if (na < nb) return -1\n if (na > nb) return 1\n }\n if (pa.prerelease === null && pb.prerelease === null) return 0\n if (pa.prerelease === null) return 1\n if (pb.prerelease === null) return -1\n if (pa.prerelease < pb.prerelease) return -1\n if (pa.prerelease > pb.prerelease) return 1\n return 0\n}\n","/**\n * Workspace-checkout detection for the starter — ZERO-DEP.\n *\n * The starter must defer to plain resolution in a dev checkout (`npm run\n * dev:full` / `npm start` from the monorepo): the data-dir server root is a\n * production mechanism and must never shadow freshly-built workspace code.\n * Detection = any ancestor package.json declaring `workspaces` (the monorepo\n * root has one; the baked image run-dir's `npm init -y` package.json and the\n * data-dir version closures never do).\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\n/**\n * Walk up from `fromDir` looking for a package.json with a `workspaces`\n * field. Returns the directory containing it, or null when none exists.\n */\nexport function detectWorkspaceRoot(fromDir: string): string | null {\n let dir = path.resolve(fromDir)\n for (;;) {\n const pkgPath = path.join(dir, 'package.json')\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n if (\n typeof raw === 'object' &&\n raw !== null &&\n (raw as Record<string, unknown>)['workspaces'] !== undefined\n ) {\n return dir\n }\n } catch {\n // missing / unreadable package.json — keep walking\n }\n const parent = path.dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n}\n","/**\n * Shared STARTER core — the boot flow both node starters (hub + agent) run.\n * ZERO-DEP: bundled into each starter at build time; must never resolve\n * anything from node_modules at runtime.\n *\n * Single-copy boot sequence (2026-07-18 collapse — identical on both node\n * types; per-node names arrive via {@link NodeStarterOptions}):\n * 1. Workspace checkout? → defer to plain resolution (load the sibling seed\n * entry; the dev loop never runs the built starter at all).\n * 2. Apply any staged root swap (`applyServerUpdate` left a pending marker),\n * then run the one-time migration from the legacy multi-copy layout to the\n * single `current/` copy. Both are idempotent + crash-safe.\n * 3. Is the ONE canonical `current/` closure valid? → register in-process\n * resolver hooks so the node's MAIN process resolves the host-external\n * packages (@camstack/system, …) from `current/node_modules`, point the\n * forked-runner framework dir at the same copy, and load its entry.\n * 4. Otherwise → load the baked seed entry next to the starter.\n *\n * There is NO probation boot, NO auto-rollback, and NO baked-seed-adoption\n * semver compare — one copy, replaced in place, manual recovery only.\n *\n * Console usage is intentional: the starter runs before any logging exists.\n * Every line is prefixed `[starter]`.\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { planBoot } from './boot-plan.js'\nimport type { RootPackageSpec } from './root-package-spec.js'\nimport { rootPackageDir, serverRootDir, validateClosureDir } from './root-state.js'\nimport {\n applyPendingRootSwap,\n currentDir,\n currentEntryPath,\n migrateToSingleCopy,\n} from './root-single-copy.js'\nimport { detectWorkspaceRoot } from './workspace-detect.js'\n\n/**\n * Mirror of the host-external list in\n * `packages/system/src/kernel/moleculer/framework-resolver-hook.mjs` — kept\n * inline because the starter must stay zero-dep. Only these specifiers are\n * redirected to the active root; everything else resolves normally.\n */\nexport const HOST_EXTERNAL_SPECIFIERS: readonly string[] = [\n '@camstack/system',\n '@camstack/shm-ring',\n '@camstack/types',\n '@camstack/sdk',\n 'zod',\n '@trpc/server',\n '@trpc/client',\n 'sharp',\n 'node-av',\n]\n\nexport function isHostExternal(specifier: string): boolean {\n return HOST_EXTERNAL_SPECIFIERS.some(\n (name) => specifier === name || specifier.startsWith(`${name}/`),\n )\n}\n\ntype ResolveContext = { readonly parentURL?: string }\ntype NextResolve = (specifier: string, context?: ResolveContext) => unknown\ntype RegisterHooksFn = (hooks: {\n resolve: (specifier: string, context: ResolveContext, nextResolve: NextResolve) => unknown\n}) => void\n\n/**\n * Register in-process resolver hooks (Node >= 22.15 `module.registerHooks`,\n * covers BOTH require and import) that anchor host-external resolution inside\n * the single copy's node_modules. Failure-tolerant: if the anchored resolve\n * fails (package absent from the closure), fall back to the default walk.\n */\nexport function registerActiveRootResolver(activeRootDir: string): void {\n // Documented type boundary: `module.registerHooks` is not yet in the\n // bundled @types/node — feature-detect it off the untyped module surface.\n const moduleApi: Record<string, unknown> = require('node:module')\n const registerHooks = moduleApi['registerHooks']\n if (typeof registerHooks !== 'function') {\n console.warn(\n '[starter] module.registerHooks unavailable (Node < 22.15) — host-external redirect skipped',\n )\n return\n }\n const anchorURL = pathToFileURL(\n path.join(activeRootDir, 'node_modules', '__camstack_starter_anchor__.js'),\n ).href\n const hooks: Parameters<RegisterHooksFn>[0] = {\n resolve: (specifier, context, nextResolve) => {\n if (isHostExternal(specifier)) {\n try {\n return nextResolve(specifier, { ...context, parentURL: anchorURL })\n } catch {\n // Not in the active root's closure — fall through to default.\n }\n }\n return nextResolve(specifier, context)\n },\n }\n ;(registerHooks as RegisterHooksFn)(hooks)\n}\n\n/** Per-node env-var names the starter writes / reads. */\nexport interface StarterEnvNames {\n /** Boot-mode marker read by the node's update service (`workspace`/`baked`/`data-root`). */\n readonly bootMode: string\n /** Absolute path of the active single-copy dir (data-root boots only). */\n readonly activeRoot: string\n /** Active data-root version (data-root boots only). */\n readonly activeVersion: string\n /** Kill switch: `<killSwitch>=off` forces the baked seed closure. */\n readonly killSwitch: string\n}\n\nexport interface NodeStarterOptions {\n readonly spec: RootPackageSpec\n readonly envNames: StarterEnvNames\n /** The node's resolved data dir (owner-specific env/argv resolution). */\n readonly dataDir: string\n /** Absolute path of the baked seed entry sibling to the starter. */\n readonly seedEntry: string\n /** The starter's own dir (`__dirname`) — anchors workspace detection. */\n readonly starterDir: string\n /** Load an entry file (a plain CJS `require` in the real starters). */\n readonly loadEntry: (entryPath: string) => void\n /** Test seams. Defaults: `process.env`, `Date.now`, running node major. */\n readonly env?: NodeJS.ProcessEnv\n readonly now?: () => number\n readonly nodeMajor?: number\n readonly registerResolver?: (activeRootDir: string) => void\n}\n\n/** Read a package.json `version` string, or null on any failure. */\nfunction readClosureVersion(closureDir: string, spec: RootPackageSpec): string | null {\n try {\n const raw: unknown = JSON.parse(\n fs.readFileSync(path.join(rootPackageDir(closureDir, spec), 'package.json'), 'utf-8'),\n )\n if (typeof raw === 'object' && raw !== null) {\n const version = (raw as Record<string, unknown>)['version']\n if (typeof version === 'string') return version\n }\n } catch {\n /* unreadable — unknown */\n }\n return null\n}\n\n/**\n * Run the shared starter flow. Never throws for boot-plan reasons — every\n * failure path degrades to the baked seed. (A `loadEntry` throw propagates:\n * that is the caller's fatal handler's job.)\n */\nexport function runNodeStarter(options: NodeStarterOptions): void {\n const env = options.env ?? process.env\n const now = options.now ?? Date.now\n const registerResolver = options.registerResolver ?? registerActiveRootResolver\n const nodeMajor =\n options.nodeMajor ?? Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10)\n\n // ── 1. Workspace checkout → plain resolution, dev loop untouched ────────\n const disabled = env[options.envNames.killSwitch] === 'off'\n const workspaceRoot = detectWorkspaceRoot(options.starterDir)\n if (workspaceRoot !== null) {\n console.log(\n `[starter] workspace checkout detected at ${workspaceRoot} — using plain resolution`,\n )\n env[options.envNames.bootMode] = 'workspace'\n options.loadEntry(options.seedEntry)\n return\n }\n if (disabled) {\n console.log(`[starter] ${options.envNames.killSwitch}=off — booting the baked seed closure`)\n env[options.envNames.bootMode] = 'baked'\n options.loadEntry(options.seedEntry)\n return\n }\n\n const rootDir = serverRootDir(options.dataDir)\n\n // ── 2. Apply a staged swap, then migrate the legacy layout (idempotent) ──\n const swap = applyPendingRootSwap(rootDir, options.spec, nodeMajor, now, (m) => console.log(m))\n if (swap.applied) console.log(`[starter] applied staged update → ${swap.version ?? '?'}`)\n else if (swap.reason !== 'no pending swap')\n console.warn(`[starter] staged update NOT applied: ${swap.reason}`)\n\n const migration = migrateToSingleCopy(rootDir, options.spec, nodeMajor, now, (m) =>\n console.log(m),\n )\n if (migration.migrated)\n console.log(`[starter] migrated legacy layout → single copy (${migration.version ?? '?'})`)\n\n // ── 3. Boot the single copy when valid, else the baked seed ─────────────\n const activeRootDir = currentDir(rootDir)\n const invalid = validateClosureDir(activeRootDir, nodeMajor, options.spec)\n if (invalid !== null && fs.existsSync(activeRootDir)) {\n console.warn(`[starter] single copy invalid: ${invalid}`)\n }\n const plan = planBoot(invalid === null)\n\n if (plan.kind === 'current') {\n const version = readClosureVersion(activeRootDir, options.spec)\n const entry = currentEntryPath(rootDir, options.spec)\n console.log(\n `[starter] booting ${options.spec.packageName}@${version ?? '?'} from ${activeRootDir}`,\n )\n env[options.envNames.bootMode] = 'data-root'\n // `activeRoot` IS the single copy — both node entries (hub launcher / agent\n // cli) derive `CAMSTACK_FRAMEWORK_DIR` (forked-runner resolver + NODE_PATH)\n // from it, so the runners resolve `@camstack/{system,shm-ring,…}` + native\n // prebuilds from the SAME copy hub-main loaded (no separate /data/framework).\n env[options.envNames.activeRoot] = activeRootDir\n if (version !== null) env[options.envNames.activeVersion] = version\n registerResolver(activeRootDir)\n options.loadEntry(entry)\n return\n }\n\n console.log(`[starter] booting baked seed closure (${plan.reason})`)\n env[options.envNames.bootMode] = 'baked'\n options.loadEntry(options.seedEntry)\n}\n","/**\n * RootUpdateService — the shared install/update engine for a node's\n * runtime-updatable ROOT package (`@camstack/server` on the hub,\n * `@camstack/agent` on agents). The hub's `ServerUpdateService` and the\n * agent's `AgentUpdateService` are thin adapters over this one class.\n *\n * SINGLE-COPY model (2026-07-18 collapse):\n * - stage a new `<pkg>@X` closure into a same-fs `.staging-*` dir\n * (npm install), validate it (entry + version + native prebuilds),\n * - write the `.pending-root-swap.json` marker and self-restart — the\n * STARTER performs the atomic swap of the ONE `current/` copy on the next\n * boot (nothing holds `current` open then), then loads it,\n * - back the `server-management` cap methods on every node type.\n *\n * There is NO `versions/<X>` + N-1 rollback copy, NO probation boot, and NO\n * auto-rollback. Operator's accepted tradeoff: a bad update is recovered\n * manually (re-apply a known-good version, or reinstall the image seed).\n *\n * Spec: docs/superpowers/specs/2026-07-18-single-framework-copy-collapse-design.md\n */\nimport { execFile } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { promisify } from 'node:util'\nimport type {\n ServerBootMode,\n ServerPackageStatus,\n ServerUpdateActionResult,\n ServerUpdateCheckResult,\n ServerUpdateState,\n} from '@camstack/types'\nimport {\n DEV_UPLOADS_KEEP_COUNT,\n devChannelEpoch,\n devUploadVersionDir,\n devUploadsDir,\n isDevChannelVersion,\n readDevUploadManifest,\n} from './dev-uploads.js'\nimport type { RootPackageSpec } from './root-package-spec.js'\nimport { compareSemver } from './semver-compare.js'\nimport { rootEntryPath, serverRootDir, validateClosureDir } from './root-state.js'\nimport {\n findMissingNativePrebuilds,\n readPendingRootSwap,\n stagingDirPath,\n sweepTransientRootDirs,\n writePendingRootSwap,\n} from './root-single-copy.js'\nimport { detectWorkspaceRoot } from './workspace-detect.js'\n\nconst execFileAsync = promisify(execFile)\n\n/** npm registry override args — mirrors addon-package.service (module-local there). */\nfunction buildNpmRegistryArgs(registry: string | undefined): readonly string[] {\n if (registry === undefined || registry.length === 0) return []\n return ['--registry', registry, `--@camstack:registry=${registry}`]\n}\n\nexport type NpmExecFn = (\n args: readonly string[],\n opts: { readonly cwd?: string; readonly timeoutMs: number },\n) => Promise<{ stdout: string }>\n\n/**\n * Structural logger surface — satisfied by the hub's `IScopedLogger` AND the\n * agent's console logger, without a runtime dependency on `@camstack/types`.\n */\nexport interface RootUpdateLogger {\n debug(message: string, context?: { readonly meta?: Record<string, unknown> }): void\n info(message: string, context?: { readonly meta?: Record<string, unknown> }): void\n warn(message: string, context?: { readonly meta?: Record<string, unknown> }): void\n error(message: string, context?: { readonly meta?: Record<string, unknown> }): void\n}\n\n/** Per-node env-var names the update service reads (written by the starter). */\nexport interface RootUpdateEnvNames {\n /** `workspace` / `baked` / `data-root` marker set by the starter. */\n readonly bootMode: string\n /** Active data-root version (data-root boots only). */\n readonly activeVersion: string\n /** Directory of the immutable baked seed root package (image-set). */\n readonly seedDir: string\n}\n\nexport interface RootUpdateServiceOptions {\n readonly spec: RootPackageSpec\n readonly envNames: RootUpdateEnvNames\n readonly logger: RootUpdateLogger\n /** Graceful restart seam (hub: AddonPackageService.restartServer; agent: SIGTERM self). */\n readonly restartServer: (requestedBy: string) => void\n /** The node's resolved data dir (adapters own the per-node default). */\n readonly dataDir: string\n /** package.json of the RUNNING root-package copy (adapters own the default). */\n readonly runningPackageJsonPath: string\n /** Anchor for the boot-mode fallback classification (adapter `__dirname`). */\n readonly workspaceProbeDir: string\n /** Overrides for tests. */\n readonly execNpm?: NpmExecFn\n readonly env?: NodeJS.ProcessEnv\n readonly now?: () => number\n}\n\ninterface CheckCache {\n readonly latestVersion: string | null\n readonly checkedAtMs: number\n readonly error: string | null\n}\n\nconst NPM_VIEW_TIMEOUT_MS = 20_000\nconst NPM_INSTALL_TIMEOUT_MS = 15 * 60_000\nconst RESTART_REASON_PREFIX = 'server-update'\n/** Transient (`.staging-*` / `.trash-*`) dirs younger than this are never swept. */\nconst STALE_TRANSIENT_MS = 24 * 60 * 60 * 1000\n\nfunction readPackageVersion(pkgJsonPath: string): string | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))\n if (typeof raw === 'object' && raw !== null) {\n const version = (raw as Record<string, unknown>)['version']\n if (typeof version === 'string') return version\n }\n } catch {\n // missing / malformed — treated as unknown\n }\n return null\n}\n\nexport class RootUpdateService {\n private readonly spec: RootPackageSpec\n private readonly envNames: RootUpdateEnvNames\n private readonly logger: RootUpdateLogger\n private readonly restartServerFn: (requestedBy: string) => void\n private readonly dataDir: string\n private readonly execNpm: NpmExecFn\n private readonly env: NodeJS.ProcessEnv\n private readonly now: () => number\n private readonly runningPackageJsonPath: string\n private readonly workspaceProbeDir: string\n\n private checkCache: CheckCache | null = null\n private inFlight: 'idle' | 'checking' | 'staging' = 'idle'\n\n constructor(options: RootUpdateServiceOptions) {\n this.spec = options.spec\n this.envNames = options.envNames\n this.logger = options.logger\n this.restartServerFn = options.restartServer\n this.dataDir = path.resolve(options.dataDir)\n this.execNpm =\n options.execNpm ??\n (async (args, opts) => {\n const { stdout } = await execFileAsync('npm', [...args], {\n cwd: opts.cwd,\n timeout: opts.timeoutMs,\n })\n return { stdout }\n })\n this.env = options.env ?? process.env\n this.now = options.now ?? Date.now\n this.runningPackageJsonPath = options.runningPackageJsonPath\n this.workspaceProbeDir = options.workspaceProbeDir\n }\n\n // ── Status ────────────────────────────────────────────────────────────\n\n resolveBootMode(): ServerBootMode {\n const raw = this.env[this.envNames.bootMode]\n if (raw === 'workspace' || raw === 'baked' || raw === 'data-root') return raw\n // The starter didn't run (dev tsx entry, `npm start`, or a pre-starter\n // image) — classify by checkout shape.\n return detectWorkspaceRoot(this.workspaceProbeDir) !== null ? 'workspace' : 'baked'\n }\n\n private runningVersion(): string | null {\n return readPackageVersion(this.runningPackageJsonPath)\n }\n\n /** The running root package's identity — feeds registerNode / topology rows. */\n getRunningRootPackage(): { readonly name: string; readonly version: string | null } {\n return { name: this.spec.packageName, version: this.runningVersion() }\n }\n\n private seedVersion(): string | null {\n const seedDir = this.env[this.envNames.seedDir]\n if (seedDir === undefined || seedDir.length === 0) return null\n return readPackageVersion(path.join(seedDir, 'package.json'))\n }\n\n private rootDir(): string {\n return serverRootDir(this.dataDir)\n }\n\n private updateState(): ServerUpdateState {\n if (this.inFlight === 'checking') return 'checking'\n if (this.inFlight === 'staging') return 'staging'\n // A staged version awaiting the swap-on-restart shows as pending-restart.\n if (readPendingRootSwap(this.rootDir()) !== null) return 'pending-restart'\n return 'idle'\n }\n\n async getServerPackageStatus(): Promise<ServerPackageStatus> {\n const runningVersion = this.runningVersion()\n const latestVersion = this.checkCache?.latestVersion ?? null\n const pending = readPendingRootSwap(this.rootDir())\n const updateAvailable =\n latestVersion !== null &&\n runningVersion !== null &&\n compareSemver(latestVersion, runningVersion) > 0\n return {\n packageName: this.spec.packageName,\n runningVersion,\n nodeRuntimeVersion: process.versions.node,\n activeVersion: this.env[this.envNames.activeVersion] ?? runningVersion,\n // Single-copy model: no N-1 retention, no rollback record.\n previousVersion: null,\n seedVersion: this.seedVersion(),\n latestVersion,\n updateAvailable,\n bootMode: this.resolveBootMode(),\n updateState: this.updateState(),\n pendingVersion: pending?.version ?? null,\n rolledBack: null,\n stateFileCorrupt: false,\n lastCheckedAtMs: this.checkCache?.checkedAtMs ?? null,\n }\n }\n\n // ── Check ─────────────────────────────────────────────────────────────\n\n async checkServerUpdate(): Promise<ServerUpdateCheckResult> {\n const runningVersion = this.runningVersion()\n if (this.inFlight !== 'idle') {\n return {\n packageName: this.spec.packageName,\n runningVersion,\n latestVersion: this.checkCache?.latestVersion ?? null,\n updateAvailable: false,\n checkedAtMs: this.checkCache?.checkedAtMs ?? this.now(),\n error: `busy: ${this.inFlight}`,\n }\n }\n this.inFlight = 'checking'\n try {\n const registry = this.env['CAMSTACK_NPM_REGISTRY']\n const args = [\n 'view',\n `${this.spec.packageName}@latest`,\n 'version',\n ...buildNpmRegistryArgs(registry),\n ]\n const { stdout } = await this.execNpm(args, { timeoutMs: NPM_VIEW_TIMEOUT_MS })\n const latestVersion = stdout.trim().length > 0 ? stdout.trim() : null\n this.checkCache = { latestVersion, checkedAtMs: this.now(), error: null }\n const updateAvailable =\n latestVersion !== null &&\n runningVersion !== null &&\n compareSemver(latestVersion, runningVersion) > 0\n return {\n packageName: this.spec.packageName,\n runningVersion,\n latestVersion,\n updateAvailable,\n checkedAtMs: this.checkCache.checkedAtMs,\n error: null,\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.checkCache = {\n latestVersion: this.checkCache?.latestVersion ?? null,\n checkedAtMs: this.now(),\n error: message,\n }\n this.logger.warn('root package update check failed', { meta: { error: message } })\n return {\n packageName: this.spec.packageName,\n runningVersion,\n latestVersion: this.checkCache.latestVersion,\n updateAvailable: false,\n checkedAtMs: this.checkCache.checkedAtMs,\n error: message,\n }\n } finally {\n this.inFlight = 'idle'\n }\n }\n\n // ── Apply ─────────────────────────────────────────────────────────────\n\n async applyServerUpdate(input: { version?: string }): Promise<ServerUpdateActionResult> {\n const bootMode = this.resolveBootMode()\n if (bootMode === 'workspace') {\n return {\n accepted: false,\n targetVersion: input.version ?? null,\n restarting: false,\n message:\n 'Refused: running from a workspace checkout — the dev loop resolves code from the ' +\n 'workspace, a data-root update would never be loaded.',\n }\n }\n if (this.inFlight !== 'idle') {\n return {\n accepted: false,\n targetVersion: input.version ?? null,\n restarting: false,\n message: `Refused: another operation is in flight (${this.inFlight}).`,\n }\n }\n const pendingBefore = readPendingRootSwap(this.rootDir())\n if (pendingBefore !== null) {\n return {\n accepted: false,\n targetVersion: input.version ?? null,\n restarting: false,\n message: `Refused: version ${pendingBefore.version} is already staged and awaiting restart.`,\n }\n }\n\n // Resolve the target version (explicit or latest from the registry).\n let target = input.version ?? null\n if (target === null) {\n const check = await this.checkServerUpdate()\n if (check.error !== null || check.latestVersion === null) {\n return {\n accepted: false,\n targetVersion: null,\n restarting: false,\n message: `Refused: could not resolve the latest version (${check.error ?? 'no version returned'}).`,\n }\n }\n target = check.latestVersion\n }\n\n const runningVersion = this.runningVersion()\n if (target === runningVersion) {\n return {\n accepted: false,\n targetVersion: target,\n restarting: false,\n message: `Refused: ${this.spec.packageName}@${target} is already running.`,\n }\n }\n\n // Dev server-deploy channel boundary (phase 1: hub only). A `-dev.<epoch>`\n // version stages EXCLUSIVELY from tarballs previously uploaded to THIS\n // node's `server-root/dev-uploads/<version>/` — it never exists on the\n // npm registry, so a node without the uploads must fail loudly here.\n if (isDevChannelVersion(target)) {\n const uploadsDir = devUploadVersionDir(this.rootDir(), target)\n if (!fs.existsSync(uploadsDir)) {\n return {\n accepted: false,\n targetVersion: target,\n restarting: false,\n message:\n `Refused: dev-channel version ${target} has no uploaded tarballs on this node ` +\n `(${uploadsDir} is missing). Dev server deploys are hub-only in this phase — ` +\n `upload via \\`camstack deploy-server\\` against this node first.`,\n }\n }\n }\n\n this.inFlight = 'staging'\n let stagingPath: string\n try {\n stagingPath = await this.stageClosure(target)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.logger.error('root package update staging failed', {\n meta: { targetVersion: target, error: message },\n })\n return {\n accepted: false,\n targetVersion: target,\n restarting: false,\n message: `Staging failed: ${message}`,\n }\n } finally {\n this.inFlight = 'idle'\n }\n\n // Arm the swap-on-restart + schedule the graceful restart.\n writePendingRootSwap(this.rootDir(), {\n schemaVersion: 1,\n version: target,\n stagingPath,\n requestedAtMs: this.now(),\n })\n this.logger.info('root package update staged — restarting to apply', {\n meta: { targetVersion: target, fromVersion: runningVersion },\n })\n this.restartServerFn(`${RESTART_REASON_PREFIX}: ${this.spec.packageName}@${target}`)\n return {\n accepted: true,\n targetVersion: target,\n restarting: true,\n message: `Staged ${this.spec.packageName}@${target} — restarting to apply (single automatic reboot).`,\n }\n }\n\n /**\n * npm-install the target closure into a SAME-FS `.staging-*` dir, then\n * validate it (entry + version + native prebuilds). Returns the staging dir\n * path for the pending-root-swap marker; the STARTER swaps it into `current`\n * on the next boot. Throws (cleaning up the staging dir) on any failure so\n * `applyServerUpdate` reports a staging error and never arms a bad swap.\n */\n private async stageClosure(target: string): Promise<string> {\n const rootDir = this.rootDir()\n fs.mkdirSync(rootDir, { recursive: true })\n const stagingDir = stagingDirPath(rootDir, target, process.pid, this.now())\n fs.mkdirSync(stagingDir, { recursive: true })\n\n try {\n // Dev server-deploy channel: when tarballs for `target` were uploaded to\n // `server-root/dev-uploads/<target>/`, stage from THEM instead of the\n // registry — a synthetic package.json depends on the root tgz via\n // `file:` and pins every other uploaded package through `overrides`.\n const devDir = devUploadVersionDir(rootDir, target)\n const registry = this.env['CAMSTACK_NPM_REGISTRY']\n const installArgs = ['install', '--omit=dev', '--no-audit', '--no-fund', '--loglevel=error']\n if (fs.existsSync(devDir)) {\n fs.writeFileSync(\n path.join(stagingDir, 'package.json'),\n JSON.stringify(this.buildDevUploadsPackageJson(devDir, target), null, 2),\n 'utf-8',\n )\n await this.execNpm([...installArgs, ...buildNpmRegistryArgs(registry)], {\n cwd: stagingDir,\n timeoutMs: NPM_INSTALL_TIMEOUT_MS,\n })\n } else {\n fs.writeFileSync(\n path.join(stagingDir, 'package.json'),\n JSON.stringify({ name: 'camstack-node-root', private: true }, null, 2),\n 'utf-8',\n )\n await this.execNpm(\n [...installArgs, `${this.spec.packageName}@${target}`, ...buildNpmRegistryArgs(registry)],\n { cwd: stagingDir, timeoutMs: NPM_INSTALL_TIMEOUT_MS },\n )\n }\n\n const entry = rootEntryPath(stagingDir, this.spec)\n if (!fs.existsSync(entry)) {\n throw new Error(`staged closure is missing the root entry (${entry})`)\n }\n const nodeMajor = Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10)\n const invalid = validateClosureDir(stagingDir, nodeMajor, this.spec, target)\n if (invalid !== null) {\n throw new Error(`staged closure invalid: ${invalid}`)\n }\n // Native prebuild assertion — a runner that can't load node-av/sharp/\n // better-sqlite3 breaks decode/snapshot/db. Abort BEFORE arming the swap.\n const missingNatives = findMissingNativePrebuilds(stagingDir)\n if (missingNatives.length > 0) {\n throw new Error(\n `staged closure missing native prebuilds: ${missingNatives.join(', ')} — ` +\n 'refusing to arm a swap that would break the forked runners',\n )\n }\n return stagingDir\n } catch (err) {\n await fs.promises.rm(stagingDir, { recursive: true, force: true }).catch(() => undefined)\n throw err\n }\n }\n\n /**\n * Synthetic staging package.json for the dev server-deploy channel:\n * `dependencies` carries the root package as a `file:` ref to its uploaded\n * tgz; `overrides` maps EVERY OTHER uploaded `@camstack/<name>` to its tgz.\n * Fail-closed: a missing/invalid manifest, a manifest for a different\n * version, a missing root package, or a listed-but-absent tgz all throw.\n */\n private buildDevUploadsPackageJson(\n devDir: string,\n target: string,\n ): {\n readonly name: string\n readonly private: true\n readonly dependencies: Readonly<Record<string, string>>\n readonly overrides?: Readonly<Record<string, string>>\n } {\n const manifest = readDevUploadManifest(devDir)\n if (manifest === null) {\n throw new Error(`dev-uploads manifest missing or invalid in ${devDir}`)\n }\n if (manifest.version !== target) {\n throw new Error(\n `dev-uploads manifest version mismatch: dir is for ${target}, manifest says ${manifest.version}`,\n )\n }\n const rootTgz = manifest.packages[this.spec.packageName]\n if (rootTgz === undefined) {\n throw new Error(\n `dev-uploads for ${target} do not include the root package ${this.spec.packageName}`,\n )\n }\n const fileRef = (filename: string): string => {\n const abs = path.join(devDir, filename)\n if (!fs.existsSync(abs)) {\n throw new Error(`dev-uploads tarball listed in the manifest is missing: ${abs}`)\n }\n return `file:${abs}`\n }\n const overrides: Record<string, string> = {}\n for (const [pkgName, filename] of Object.entries(manifest.packages)) {\n if (pkgName === this.spec.packageName) continue\n overrides[pkgName] = fileRef(filename)\n }\n return {\n name: 'camstack-node-root',\n private: true,\n dependencies: { [this.spec.packageName]: fileRef(rootTgz) },\n ...(Object.keys(overrides).length > 0 ? { overrides } : {}),\n }\n }\n\n // ── Rollback (removed — single-copy has no N-1) ────────────────────────\n\n /**\n * Rollback is NOT supported in the single-copy model — there is no retained\n * N-1 copy to revert to (operator's accepted tradeoff). Recovery is manual:\n * re-apply a known-good version via {@link applyServerUpdate}, or reinstall\n * the image seed. Kept on the surface so the cap contract is unchanged.\n */\n async rollbackServerUpdate(): Promise<ServerUpdateActionResult> {\n return {\n accepted: false,\n targetVersion: null,\n restarting: false,\n message:\n 'Rollback is not supported in the single-copy model (no retained previous version). ' +\n 'Recover by re-applying a known-good version via applyServerUpdate, or reinstall the image seed.',\n }\n }\n\n // ── Plain restart ─────────────────────────────────────────────────────\n\n /**\n * Plain process restart — no version change. Refuse while a stage is in\n * flight (a concurrent npm install must not be interrupted) or while a\n * version is already staged awaiting restart (a naive bounce would boot the\n * OLD version and orphan the pending swap — the operator should apply\n * instead).\n */\n restartNode(): ServerUpdateActionResult {\n const runningVersion = this.runningVersion()\n if (this.inFlight !== 'idle') {\n return {\n accepted: false,\n targetVersion: runningVersion,\n restarting: false,\n message: `Refused: another operation is in flight (${this.inFlight}).`,\n }\n }\n if (readPendingRootSwap(this.rootDir()) !== null) {\n return {\n accepted: false,\n targetVersion: runningVersion,\n restarting: false,\n message:\n 'Refused: a version is staged and awaiting restart — apply it instead of a plain restart.',\n }\n }\n this.logger.info('plain node restart requested', { meta: { runningVersion } })\n this.restartServerFn(`${RESTART_REASON_PREFIX}: manual restart`)\n return {\n accepted: true,\n targetVersion: runningVersion,\n restarting: true,\n message: 'Restarting the node — the supervisor relaunches it on the same version.',\n }\n }\n\n // ── Boot health confirmation ──────────────────────────────────────────\n\n /**\n * Called by the post-boot path once the node is READY. In the single-copy\n * model there is no pending version to promote (the swap already happened in\n * the starter). This becomes a best-effort GC of orphaned transient dirs\n * (`.staging-*` / `.trash-*` left by a crash mid-swap) + the dev-uploads\n * sweep. Kept returning the legacy `{ promoted }` shape for the callers.\n */\n confirmBootHealthy(): { promoted: string | null } {\n sweepTransientRootDirs(this.rootDir(), this.now(), STALE_TRANSIENT_MS)\n this.sweepDevUploads(DEV_UPLOADS_KEEP_COUNT)\n return { promoted: null }\n }\n\n /**\n * Promote-time sweep of `server-root/dev-uploads/`: keep only the\n * `keepCount` most recent entries (ordered by the self-describing\n * `-dev.<epochSeconds>` suffix; non-dev-shaped residue counts as oldest).\n */\n private sweepDevUploads(keepCount: number): void {\n const dir = devUploadsDir(this.rootDir())\n let entries: string[]\n try {\n entries = fs.readdirSync(dir)\n } catch {\n return // no dev-uploads dir — nothing to sweep\n }\n const graveyard = path.join(dir, '.sweeping')\n // Best-effort collect residue left behind by an earlier sweep first.\n try {\n for (const residue of fs.readdirSync(graveyard)) {\n try {\n fs.rmSync(path.join(graveyard, residue), { recursive: true, force: true })\n } catch {\n /* still held open — retried on the next promote */\n }\n }\n } catch {\n /* graveyard absent — nothing to collect */\n }\n\n const versions = entries.filter((name) => !name.startsWith('.'))\n const byNewestFirst = [...versions].sort(\n (a, b) => (devChannelEpoch(b) ?? -1) - (devChannelEpoch(a) ?? -1),\n )\n const doomed = byNewestFirst.slice(keepCount)\n if (doomed.length === 0) return\n fs.mkdirSync(graveyard, { recursive: true })\n for (const entry of doomed) {\n const aside = path.join(graveyard, `${entry}-${this.now()}`)\n try {\n fs.renameSync(path.join(dir, entry), aside)\n } catch (err) {\n this.logger.warn('failed to move dev-uploads entry aside for sweep', {\n meta: { entry, error: err instanceof Error ? err.message : String(err) },\n })\n continue\n }\n try {\n fs.rmSync(aside, { recursive: true, force: true })\n this.logger.debug('swept dev-uploads entry', { meta: { entry } })\n } catch {\n /* held open — the aside copy is collected on the next promote */\n }\n }\n }\n}\n","/**\n * AGENT STARTER — the tiny, immutable, dependency-free entry baked into the\n * agent image (and, in phase 3, spawned by the Electron shell). Everything\n * above it (the agent CLI, framework, addons) is runtime-updatable; the\n * starter itself can NEVER self-update — a new image is the only way to\n * change it, which is why it must stay deliberately dumb.\n *\n * The boot flow is the SHARED node-root core (`runNodeStarter`, bundled into\n * this file by tsup — see tsup.config.ts): workspace defer → apply a staged\n * root swap + migrate the legacy layout to the single `current/` copy → the\n * host-external resolver hooks anchored in the active root → load the single\n * copy (or the baked seed). This file only binds the agent's names:\n * `@camstack/agent`, `dist/cli.js`, the `CAMSTACK_AGENT_*` env markers and the\n * `CAMSTACK_DATA_DIR` / `--data` data dir.\n *\n * Layout mirror of the hub (single-copy model):\n * <agentDataDir>/server-root/current/node_modules/@camstack/agent/dist/cli.js\n *\n * Console usage is intentional: the starter runs before any logging exists.\n */\nimport { existsSync } from 'node:fs'\nimport * as path from 'node:path'\nimport { AGENT_ROOT_SPEC, runNodeStarter } from '@camstack/node-root'\n\n/**\n * Resolve the agent data dir the way the CLI will (`CAMSTACK_DATA_DIR` env\n * wins, then `--data`/`-d` argv, then `./camstack-data`). The starter runs\n * BEFORE the CLI parses argv, so it mirrors the flag inline — otherwise an\n * operator running `camstack-agent --data /x` would get the server-root in a\n * different data dir than the agent itself.\n */\nfunction resolveAgentDataDir(argv: readonly string[], env: NodeJS.ProcessEnv): string {\n const fromEnv = env['CAMSTACK_DATA_DIR']\n if (fromEnv !== undefined && fromEnv.length > 0) return path.resolve(fromEnv)\n for (let i = 2; i < argv.length - 1; i++) {\n if (argv[i] === '--data' || argv[i] === '-d') {\n const next = argv[i + 1]\n if (next !== undefined && !next.startsWith('-')) return path.resolve(next)\n }\n }\n return path.resolve('camstack-data')\n}\n\n/**\n * Restart-policy dependency warning (security review #3). `server-management`\n * apply/rollback = graceful exit + supervisor relaunch. Without an\n * always-relaunching restart policy (docker: `unless-stopped`/`always`) a node\n * that exits to apply an update simply STAYS DOWN. No code can enforce the\n * policy — make the dependency visible at boot. Diagnostic only.\n */\nfunction warnRestartPolicyDependency(): void {\n if (!existsSync('/.dockerenv')) return\n console.warn(\n '[starter] container detected — server-management apply/rollback exit this ' +\n 'process for the supervisor to relaunch. Ensure the container restart policy is ' +\n \"'unless-stopped' or 'always', otherwise an update/rollback leaves the node down.\",\n )\n}\n\nfunction start(): void {\n warnRestartPolicyDependency()\n\n const seedPackageDir = path.resolve(__dirname, '..')\n if (process.env['CAMSTACK_SEED_AGENT_DIR'] === undefined) {\n process.env['CAMSTACK_SEED_AGENT_DIR'] = seedPackageDir\n }\n\n runNodeStarter({\n spec: AGENT_ROOT_SPEC,\n envNames: {\n bootMode: 'CAMSTACK_AGENT_BOOT_MODE',\n activeRoot: 'CAMSTACK_AGENT_ACTIVE_ROOT',\n activeVersion: 'CAMSTACK_AGENT_ACTIVE_VERSION',\n killSwitch: 'CAMSTACK_AGENT_ROOT',\n },\n dataDir: resolveAgentDataDir(process.argv, process.env),\n seedEntry: path.join(__dirname, 'cli.js'),\n starterDir: __dirname,\n // The CLI is CJS (tsup build) — a plain require keeps the starter\n // synchronous; the CLI reads the SAME process.argv it always did.\n loadEntry: (entryPath) => require(entryPath),\n })\n}\n\ntry {\n start()\n} catch (err) {\n console.error('[starter] FATAL:', err)\n process.exit(1)\n}\n"],"mappings":"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","names":["AGENT_ROOT_SPEC","runNodeStarter","module","__toESM","path","fs","path2"]}
|
|
1
|
+
{"version":3,"sources":["../../node-root/src/index.ts","../../node-root/src/dev-uploads.ts","../../node-root/src/root-package-spec.ts","../../node-root/src/root-state.ts","../../node-root/src/root-single-copy.ts","../../node-root/src/semver-compare.ts","../../node-root/src/boot-plan.ts","../../node-root/src/workspace-detect.ts","../../node-root/src/starter-core.ts","../../node-root/src/root-update-service.ts","../src/starter.ts"],"sourcesContent":["/**\n * @camstack/node-root — the shared, zero-runtime-dep node-root boot core.\n *\n * PRIVATE workspace package: it is BUNDLED at build time into the hub's\n * `dist/server-root/index.js` (tsup) and into the agent's tsup output —\n * never published, never resolved from node_modules at runtime. Both node\n * starters and both update services are thin per-node adapters over these\n * modules, so the boot-plan / probation / rollback semantics can never\n * drift between the hub and the agent.\n *\n * Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md\n */\nexport * from './dev-uploads.js'\nexport * from './root-package-spec.js'\nexport * from './root-state.js'\nexport * from './root-single-copy.js'\nexport * from './boot-plan.js'\nexport * from './semver-compare.js'\nexport * from './workspace-detect.js'\nexport * from './starter-core.js'\nexport * from './root-update-service.js'\n","/**\n * Dev server-deploy channel — layout + manifest for locally-uploaded root\n * package closures (ZERO-DEP: node:fs/node:path only, same constraint as\n * root-state.ts — the baked starter bundles this module).\n *\n * `camstack deploy-server` packs every workspace package in the server's\n * `@camstack/*` closure at one uniform dev version\n * (`<base>-dev.<epochSeconds>`) and uploads the tarballs to\n * `POST /api/server-upload`. The hub stores them under:\n *\n * <dataDir>/server-root/dev-uploads/<version>/\n * <flattened-pkg-name>.tgz one per uploaded package\n * manifest.json { version, packages: { name → filename } }\n *\n * `RootUpdateService.stageAndActivate` detects the dir and stages from these\n * tarballs (synthetic package.json with `file:` refs + `overrides`) instead\n * of the npm registry. Everything downstream — probation boot, promote,\n * rollback — is version-string driven and unchanged; the `-dev.` suffix is\n * self-describing.\n *\n * Spec: docs/superpowers/specs — dev server-deploy channel (2026-07-13).\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\nexport const DEV_UPLOADS_DIRNAME = 'dev-uploads'\nexport const DEV_UPLOAD_MANIFEST_FILE = 'manifest.json'\n\n/** Number of dev-uploads entries retained by the promote-time sweep. */\nexport const DEV_UPLOADS_KEEP_COUNT = 3\n\n/**\n * Dev-channel version shape: `<semver-base>-dev.<epochSeconds>`. The suffix\n * is sortable, self-describing, and can never collide with a published\n * registry version. The upload route and the staging branch BOTH gate on it.\n */\nconst DEV_CHANNEL_VERSION_RE = /-dev\\.\\d+$/\n\nexport function isDevChannelVersion(version: string): boolean {\n return DEV_CHANNEL_VERSION_RE.test(version)\n}\n\n/**\n * The `<epochSeconds>` ordering key of a dev-channel version (or dir name).\n * Returns null for anything that is not dev-channel shaped — the sweep\n * treats those as oldest so malformed residue is collected first.\n */\nexport function devChannelEpoch(version: string): number | null {\n const match = /-dev\\.(\\d+)$/.exec(version)\n if (match === null) return null\n const epoch = Number.parseInt(match[1] ?? '', 10)\n return Number.isNaN(epoch) ? null : epoch\n}\n\n/** `<dataDir>/server-root/dev-uploads` */\nexport function devUploadsDir(rootDir: string): string {\n return path.join(rootDir, DEV_UPLOADS_DIRNAME)\n}\n\n/** `<dataDir>/server-root/dev-uploads/<version>` */\nexport function devUploadVersionDir(rootDir: string, version: string): string {\n return path.join(devUploadsDir(rootDir), version)\n}\n\n// ---------------------------------------------------------------------------\n// Manifest IO\n// ---------------------------------------------------------------------------\n\n/**\n * Written by the hub's `/api/server-upload` route after validating every\n * tarball; read by the staging branch. Maps each uploaded package NAME to\n * its tgz FILENAME inside the version dir — staging never has to re-derive\n * package names from flattened filenames.\n */\nexport interface DevUploadManifest {\n readonly version: string\n readonly packages: Readonly<Record<string, string>>\n}\n\nexport function devUploadManifestPath(versionDirPath: string): string {\n return path.join(versionDirPath, DEV_UPLOAD_MANIFEST_FILE)\n}\n\nfunction isDevUploadManifest(v: unknown): v is DevUploadManifest {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n if (typeof m['version'] !== 'string') return false\n const packages = m['packages']\n if (typeof packages !== 'object' || packages === null || Array.isArray(packages)) return false\n return Object.values(packages).every((filename) => typeof filename === 'string')\n}\n\n/** Read + validate the manifest. Returns null when missing/corrupt. */\nexport function readDevUploadManifest(versionDirPath: string): DevUploadManifest | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(devUploadManifestPath(versionDirPath), 'utf-8'))\n return isDevUploadManifest(raw) ? raw : null\n } catch {\n return null\n }\n}\n\n/** Write the manifest atomically (tmp sibling + rename). Creates the dir. */\nexport function writeDevUploadManifest(versionDirPath: string, manifest: DevUploadManifest): void {\n fs.mkdirSync(versionDirPath, { recursive: true })\n const target = devUploadManifestPath(versionDirPath)\n const tmp = `${target}.tmp`\n fs.writeFileSync(tmp, JSON.stringify(manifest, null, 2), 'utf-8')\n fs.renameSync(tmp, target)\n}\n","/**\n * Root-package spec — the per-node-type parameters of the shared node-root\n * boot machinery. Everything else (the `server-root/` data-dir layout, the\n * `state.json` schema, the probation/rollback boot plan, the update service)\n * is IDENTICAL between the hub and the agent; only the root package's name\n * and its loadable entry differ.\n *\n * Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md\n */\n\nexport interface RootPackageSpec {\n /** The npm name of the node's root package. */\n readonly packageName: string\n /**\n * Path segments of the loadable entry INSIDE the root package dir\n * (`node_modules/<packageName>/<...entryRelPath>`).\n */\n readonly entryRelPath: readonly string[]\n}\n\n/** The hub's root package — the starter loads its launcher. */\nexport const HUB_ROOT_SPEC: RootPackageSpec = {\n packageName: '@camstack/server',\n entryRelPath: ['dist', 'launcher.js'],\n}\n\n/** The agent's root package — the starter loads its CLI entry. */\nexport const AGENT_ROOT_SPEC: RootPackageSpec = {\n packageName: '@camstack/agent',\n entryRelPath: ['dist', 'cli.js'],\n}\n","/**\n * Data-dir server-root layout + state file — ZERO-DEP (node:fs/node:path only).\n *\n * Loaded by each node's baked STARTER before any node_modules resolution is\n * trusted, so this module must never import from `@camstack/*` (runtime) or\n * any npm package. The hub's `ServerUpdateService` and the agent's update\n * service consume the same module — one source of truth for the layout and\n * the state shape. The layout is IDENTICAL on both node types (the dir is\n * named `server-root/` everywhere); only the root package name + entry differ\n * and arrive via {@link RootPackageSpec}.\n *\n * Layout (`<dataDir>/server-root/`):\n * versions/<semver>/ npm-installed closure for one version\n * node_modules/<pkg>/<entry> ← the loadable entry (spec-defined)\n * state.json pointer file (ServerRootState, atomic)\n *\n * A POINTER FILE (state.json) is used instead of `current`/`previous`\n * symlinks: atomic via tmp+rename, works on every fs, and carries the\n * probation/rollback bookkeeping in the same durable record.\n *\n * Spec: docs/superpowers/specs/2026-07-12-runtime-updatable-node-packages-design.md\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { RootPackageSpec } from './root-package-spec.js'\n\nexport const SERVER_ROOT_DIRNAME = 'server-root'\nexport const SERVER_ROOT_STATE_FILE = 'state.json'\n/**\n * Restart-intent marker filename. Written by the update service moments before\n * a graceful self-restart (apply/rollback), then read + consumed by an\n * external process supervisor so it can tell an INTENTIONAL restart-for-update\n * apart from a crash — a crash-backoff policy must never penalise an update\n * restart. Lives in the server-root dir next to `state.json`.\n *\n * (Phase 3 — the Electron shell supervisor reads it; the docker restart policy\n * relaunches unconditionally so the marker is a harmless no-op there.)\n */\nexport const RESTART_INTENT_FILE = '.restart-intent'\n\n// ---------------------------------------------------------------------------\n// State shape\n// ---------------------------------------------------------------------------\n\nexport interface ServerRootPendingBoot {\n /** The freshly-staged version awaiting its probation boot. */\n readonly version: string\n /** The version that was current when the update was requested (null = baked). */\n readonly fromVersion: string | null\n readonly requestedAtMs: number\n /** 0 = not yet booted; >=1 = a probation boot started and never confirmed. */\n readonly bootAttempts: number\n}\n\nexport interface ServerRootRollbackInfo {\n readonly fromVersion: string\n /** null = rolled back all the way to the baked seed. */\n readonly toVersion: string | null\n readonly atMs: number\n readonly reason: string\n}\n\nexport interface ServerRootState {\n readonly schemaVersion: 1\n /** The confirmed-healthy active version (null = boot from the baked seed). */\n readonly currentVersion: string | null\n /** N-1 kept for rollback (its versions/ dir is retained). */\n readonly previousVersion: string | null\n readonly pendingBoot: ServerRootPendingBoot | null\n /** Set when the most recent update attempt was rolled back. */\n readonly rolledBack: ServerRootRollbackInfo | null\n}\n\nexport function emptyServerRootState(): ServerRootState {\n return {\n schemaVersion: 1,\n currentVersion: null,\n previousVersion: null,\n pendingBoot: null,\n rolledBack: null,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Layout helpers\n// ---------------------------------------------------------------------------\n\nexport function serverRootDir(dataDir: string): string {\n return path.join(dataDir, SERVER_ROOT_DIRNAME)\n}\n\nexport function versionsDir(rootDir: string): string {\n return path.join(rootDir, 'versions')\n}\n\nexport function versionDir(rootDir: string, version: string): string {\n return path.join(versionsDir(rootDir), version)\n}\n\n/** `versions/<v>/node_modules/<packageName>` — the installed root package dir. */\nexport function rootPackageDir(versionDirPath: string, spec: RootPackageSpec): string {\n return path.join(versionDirPath, 'node_modules', ...spec.packageName.split('/'))\n}\n\n/** The loadable entry inside the installed root package dir. */\nexport function rootEntryPath(versionDirPath: string, spec: RootPackageSpec): string {\n return path.join(rootPackageDir(versionDirPath, spec), ...spec.entryRelPath)\n}\n\nexport function stateFilePath(rootDir: string): string {\n return path.join(rootDir, SERVER_ROOT_STATE_FILE)\n}\n\n// ---------------------------------------------------------------------------\n// State IO (atomic, corruption-tolerant)\n// ---------------------------------------------------------------------------\n\nfunction isNullableString(v: unknown): v is string | null {\n return v === null || typeof v === 'string'\n}\n\nfunction isPendingBoot(v: unknown): v is ServerRootPendingBoot {\n if (typeof v !== 'object' || v === null) return false\n const p = v as Record<string, unknown>\n return (\n typeof p['version'] === 'string' &&\n isNullableString(p['fromVersion']) &&\n typeof p['requestedAtMs'] === 'number' &&\n typeof p['bootAttempts'] === 'number'\n )\n}\n\nfunction isRollbackInfo(v: unknown): v is ServerRootRollbackInfo {\n if (typeof v !== 'object' || v === null) return false\n const r = v as Record<string, unknown>\n return (\n typeof r['fromVersion'] === 'string' &&\n isNullableString(r['toVersion']) &&\n typeof r['atMs'] === 'number' &&\n typeof r['reason'] === 'string'\n )\n}\n\nexport function isServerRootState(v: unknown): v is ServerRootState {\n if (typeof v !== 'object' || v === null) return false\n const s = v as Record<string, unknown>\n if (s['schemaVersion'] !== 1) return false\n if (!isNullableString(s['currentVersion'])) return false\n if (!isNullableString(s['previousVersion'])) return false\n if (s['pendingBoot'] !== null && !isPendingBoot(s['pendingBoot'])) return false\n if (s['rolledBack'] !== null && !isRollbackInfo(s['rolledBack'])) return false\n return true\n}\n\n/** Read + validate the state file. Returns null when missing/corrupt/mismatched. */\nexport function readServerRootState(rootDir: string): ServerRootState | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(stateFilePath(rootDir), 'utf-8'))\n return isServerRootState(raw) ? raw : null\n } catch {\n return null\n }\n}\n\n/** Write the state file atomically (tmp sibling + rename). Creates rootDir. */\nexport function writeServerRootState(rootDir: string, state: ServerRootState): void {\n fs.mkdirSync(rootDir, { recursive: true })\n const target = stateFilePath(rootDir)\n const tmp = `${target}.tmp`\n fs.writeFileSync(tmp, JSON.stringify(state, null, 2), 'utf-8')\n fs.renameSync(tmp, target)\n}\n\n// ---------------------------------------------------------------------------\n// Restart-intent marker (supervisor: update-restart vs crash discrimination)\n// ---------------------------------------------------------------------------\n\n/**\n * Payload of the restart-intent marker. `requestedAtMs` lets a reader ignore a\n * STALE marker (e.g. one left behind by a hard kill between write and\n * relaunch) via a freshness window instead of mis-classifying a much-later\n * crash as an update restart. `reason` carries the human `requestedBy` string.\n */\nexport interface RestartIntentMarker {\n readonly requestedAtMs: number\n readonly reason: string\n}\n\nexport function restartIntentMarkerPath(rootDir: string): string {\n return path.join(rootDir, RESTART_INTENT_FILE)\n}\n\nfunction isRestartIntentMarker(v: unknown): v is RestartIntentMarker {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n return typeof m['requestedAtMs'] === 'number' && typeof m['reason'] === 'string'\n}\n\n/** Write the marker atomically (tmp sibling + rename). Creates rootDir. */\nexport function writeRestartIntentMarker(rootDir: string, marker: RestartIntentMarker): void {\n fs.mkdirSync(rootDir, { recursive: true })\n const target = restartIntentMarkerPath(rootDir)\n const tmp = `${target}.tmp`\n fs.writeFileSync(tmp, JSON.stringify(marker, null, 2), 'utf-8')\n fs.renameSync(tmp, target)\n}\n\n/** Read + validate the marker. Returns null when missing/corrupt. */\nexport function readRestartIntentMarker(rootDir: string): RestartIntentMarker | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(restartIntentMarkerPath(rootDir), 'utf-8'))\n return isRestartIntentMarker(raw) ? raw : null\n } catch {\n return null\n }\n}\n\n/** Delete the marker if present (idempotent — a missing file is not an error). */\nexport function clearRestartIntentMarker(rootDir: string): void {\n try {\n fs.rmSync(restartIntentMarkerPath(rootDir), { force: true })\n } catch {\n /* best-effort */\n }\n}\n\n// ---------------------------------------------------------------------------\n// Version-dir validation\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the minimum required Node major from an `engines.node` range like\n * `>=22`, `>= 22.12`, `^24.0.0`, `22.x`. Returns null when unparseable\n * (validation is then permissive — a bogus range must not brick boot).\n */\nexport function minNodeMajorOf(enginesNode: string): number | null {\n const match = /(\\d+)/.exec(enginesNode)\n if (match === null) return null\n const major = Number.parseInt(match[1] ?? '', 10)\n return Number.isNaN(major) ? null : major\n}\n\n/**\n * Validate that `closureDir` holds a loadable root-package closure for `spec`\n * (its installed package lives at `closureDir/node_modules/<packageName>`).\n * Returns null when valid, else a human-readable reason. Checks:\n * 1. the entry file exists,\n * 2. the package.json parses with the right `name` (and `version` when\n * `expectedVersion` is given),\n * 3. `engines.node` (when parseable) is satisfied by the running runtime —\n * the starter refuses a native-ABI mismatch with a clear error instead\n * of segfaulting (design risk: node bump vs native prebuilds).\n *\n * Shared by the single-copy `current/` model (`expectedVersion` omitted — the\n * version is whatever the closure carries) and the legacy `versions/<v>/`\n * model (`expectedVersion` set — the dir name must match the package.json).\n */\nexport function validateClosureDir(\n closureDir: string,\n nodeMajor: number,\n spec: RootPackageSpec,\n expectedVersion?: string,\n): string | null {\n const entry = rootEntryPath(closureDir, spec)\n if (!fs.existsSync(entry)) {\n return `root entry missing: ${entry}`\n }\n\n const pkgJsonPath = path.join(rootPackageDir(closureDir, spec), 'package.json')\n let pkg: Record<string, unknown>\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n return `package.json malformed: ${pkgJsonPath}`\n }\n pkg = raw as Record<string, unknown>\n } catch {\n return `package.json unreadable: ${pkgJsonPath}`\n }\n\n if (pkg['name'] !== spec.packageName) {\n return `package name mismatch: expected ${spec.packageName}, got ${String(pkg['name'])}`\n }\n if (expectedVersion !== undefined && pkg['version'] !== expectedVersion) {\n return `package version mismatch: expected ${expectedVersion}, package.json says ${String(pkg['version'])}`\n }\n\n const engines = pkg['engines']\n if (typeof engines === 'object' && engines !== null) {\n const enginesNode = (engines as Record<string, unknown>)['node']\n if (typeof enginesNode === 'string') {\n const minMajor = minNodeMajorOf(enginesNode)\n if (minMajor !== null && nodeMajor < minMajor) {\n return `engines.node \"${enginesNode}\" requires Node >= ${minMajor}, runtime is ${nodeMajor}`\n }\n }\n }\n\n return null\n}\n\n/**\n * Validate that `versions/<version>` holds a loadable root-package closure for\n * `spec` (legacy multi-copy model — the dir name must match the package\n * version). Thin wrapper over {@link validateClosureDir}. Returns null when\n * valid, else a human-readable reason.\n */\nexport function validateVersionDir(\n rootDir: string,\n version: string,\n nodeMajor: number,\n spec: RootPackageSpec,\n): string | null {\n return validateClosureDir(versionDir(rootDir, version), nodeMajor, spec, version)\n}\n","/**\n * Single-copy framework layout — the ONE physical copy of a node's runtime\n * root closure. ZERO-DEP (node:fs/node:path only): loaded by the baked STARTER\n * before any node_modules resolution is trusted, so it must never import from\n * `@camstack/*` (runtime) or any npm package.\n *\n * Replaces the legacy multi-copy model (`versions/<X>` + an N-1 rollback copy +\n * a separate `/data/framework` copy + a `/data/addons/@camstack/system` copy).\n * There is now exactly ONE closure:\n *\n * <dataDir>/server-root/current/ the ONE canonical closure\n * node_modules/<packageName>/<entry> ← the loadable entry\n * <dataDir>/server-root/.pending-root-swap.json a staged update awaiting boot\n * <dataDir>/server-root/.staging-* transient npm-install dir\n * <dataDir>/server-root/.trash-* transient old-copy graveyard\n *\n * hub-main, the forked addon-runners, and the framework BUILTINS all resolve\n * `@camstack/{system,types,sdk,shm-ring}` from `current/node_modules`; there is\n * no drift and no seed-vs-data-root divergence. `applyServerUpdate` stages a\n * new closure into `.staging-*`, writes the pending-root-swap marker and\n * restarts; the STARTER performs the atomic swap of `current` on the next boot\n * (nothing holds `current` open at that point) — mirroring the trusted\n * launcher-framework-swap pattern. NO auto-rollback: a bad update is recovered\n * manually (re-apply a known-good version, or reinstall the image seed).\n *\n * Spec: docs/superpowers/specs/2026-07-18-single-framework-copy-collapse-design.md\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { RootPackageSpec } from './root-package-spec.js'\nimport {\n readServerRootState,\n rootEntryPath,\n rootPackageDir,\n stateFilePath,\n validateClosureDir,\n versionDir,\n versionsDir,\n} from './root-state.js'\nimport { compareSemver } from './semver-compare.js'\n\n// ---------------------------------------------------------------------------\n// Layout\n// ---------------------------------------------------------------------------\n\n/** Name of the single canonical closure dir under `server-root/`. */\nexport const CURRENT_DIRNAME = 'current'\n/** Marker written by `applyServerUpdate`, consumed by the starter's swap. */\nexport const PENDING_ROOT_SWAP_FILE = '.pending-root-swap.json'\n\n/**\n * Legacy `/data/framework` swap markers (the retired per-package framework\n * mechanism). Cleared during migration so a stale marker can never swap a\n * package INTO the single copy on the first collapse boot. They live in the\n * DATA dir (one level up from `server-root/`), not in `rootDir`.\n */\nconst LEGACY_FRAMEWORK_MARKERS: readonly string[] = [\n '.pending-framework-swap.json',\n '.framework-swap-confirm.json',\n]\n\n/** The single canonical closure dir for `rootDir` (= `server-root/`). */\nexport function currentDir(rootDir: string): string {\n return path.join(rootDir, CURRENT_DIRNAME)\n}\n\n/** Absolute path of the loadable entry inside the single copy. */\nexport function currentEntryPath(rootDir: string, spec: RootPackageSpec): string {\n return rootEntryPath(currentDir(rootDir), spec)\n}\n\n/** A fresh, same-fs staging dir for an npm install (sibling of `current`). */\nexport function stagingDirPath(rootDir: string, target: string, pid: number, now: number): string {\n return path.join(rootDir, `.staging-${target}-${pid}-${now}`)\n}\n\n// ---------------------------------------------------------------------------\n// Pending-root-swap marker (atomic, corruption-tolerant)\n// ---------------------------------------------------------------------------\n\nexport interface PendingRootSwap {\n readonly schemaVersion: 1\n /** The staged version awaiting its atomic swap into `current` on next boot. */\n readonly version: string\n /** Absolute path of the validated `.staging-*` closure to swap in. */\n readonly stagingPath: string\n readonly requestedAtMs: number\n}\n\nexport function pendingRootSwapPath(rootDir: string): string {\n return path.join(rootDir, PENDING_ROOT_SWAP_FILE)\n}\n\nfunction isPendingRootSwap(v: unknown): v is PendingRootSwap {\n if (typeof v !== 'object' || v === null) return false\n const m = v as Record<string, unknown>\n return (\n m['schemaVersion'] === 1 &&\n typeof m['version'] === 'string' &&\n typeof m['stagingPath'] === 'string' &&\n typeof m['requestedAtMs'] === 'number'\n )\n}\n\n/** Read + validate the pending-root-swap marker. Null when missing/corrupt. */\nexport function readPendingRootSwap(rootDir: string): PendingRootSwap | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(pendingRootSwapPath(rootDir), 'utf-8'))\n return isPendingRootSwap(raw) ? raw : null\n } catch {\n return null\n }\n}\n\n/** Write the marker atomically (tmp sibling + rename). Creates rootDir. */\nexport function writePendingRootSwap(rootDir: string, marker: PendingRootSwap): void {\n fs.mkdirSync(rootDir, { recursive: true })\n const target = pendingRootSwapPath(rootDir)\n const tmp = `${target}.tmp`\n fs.writeFileSync(tmp, JSON.stringify(marker, null, 2), 'utf-8')\n fs.renameSync(tmp, target)\n}\n\n/** Delete the marker if present (idempotent). */\nexport function clearPendingRootSwap(rootDir: string): void {\n try {\n fs.rmSync(pendingRootSwapPath(rootDir), { force: true })\n } catch {\n /* best-effort */\n }\n}\n\n// ---------------------------------------------------------------------------\n// Native prebuild assertion\n// ---------------------------------------------------------------------------\n\n/**\n * Native packages whose prebuilt `.node` bindings MUST resolve from the single\n * copy — a runner that can't load them breaks decode / snapshot / the SQLite\n * store. The closure carries them as transitive deps of the root package.\n */\nexport const REQUIRED_NATIVE_PACKAGES: readonly string[] = ['better-sqlite3', 'sharp', 'node-av']\n\n/**\n * Where each native package's `.node` binding lives inside `node_modules`. The\n * prebuild often sits in a platform-suffixed SIBLING scope (sharp → `@img/…`,\n * node-av → `@seydx/…`), so we search the package dir AND its scope root.\n */\nconst NATIVE_SEARCH_DIRS: Readonly<Record<string, readonly string[]>> = {\n 'better-sqlite3': ['better-sqlite3'],\n sharp: ['sharp', '@img'],\n 'node-av': ['node-av', '@seydx'],\n}\n\n/** Bounded recursive scan for ANY `*.node` file under `dir`. */\nfunction hasDotNode(dir: string, maxDepth: number): boolean {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n return false\n }\n for (const entry of entries) {\n if (entry.isFile() && entry.name.endsWith('.node')) return true\n }\n if (maxDepth <= 0) return false\n for (const entry of entries) {\n if (entry.isDirectory() && hasDotNode(path.join(dir, entry.name), maxDepth - 1)) return true\n }\n return false\n}\n\n/**\n * Return the required native packages whose `.node` binding could NOT be found\n * under `<closureDir>/node_modules`. Empty ⇒ the closure carries every\n * required native prebuild. Used to ABORT a swap into a closure that would\n * brick the forked runners.\n */\nexport function findMissingNativePrebuilds(closureDir: string): string[] {\n const nm = path.join(closureDir, 'node_modules')\n const missing: string[] = []\n for (const pkg of REQUIRED_NATIVE_PACKAGES) {\n const searchDirs = NATIVE_SEARCH_DIRS[pkg] ?? [pkg]\n const found = searchDirs.some((rel) => hasDotNode(path.join(nm, ...rel.split('/')), 4))\n if (!found) missing.push(pkg)\n }\n return missing\n}\n\n// ---------------------------------------------------------------------------\n// Swap + migration\n// ---------------------------------------------------------------------------\n\ntype LogFn = (message: string) => void\n\nfunction rmrf(target: string): void {\n try {\n fs.rmSync(target, { recursive: true, force: true })\n } catch {\n /* best-effort */\n }\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\nexport interface ApplyRootSwapResult {\n readonly applied: boolean\n readonly version: string | null\n readonly reason: string\n}\n\n/**\n * Perform a staged root swap if a pending-root-swap marker is present. Runs in\n * the STARTER, BEFORE the closure is loaded, so nothing holds `current` open.\n *\n * Fail-CLOSED + crash-safe:\n * 1. Validate the staged closure (entry + version + native prebuilds). Any\n * failure discards the staging dir + the marker and leaves `current`\n * untouched (the operator's manual-recovery net) — never a partial swap.\n * 2. Swap: rename `current` aside to `.trash-*`, then rename `staging` →\n * `current`. If the second rename fails, restore `current` from the trash.\n * No N-1 retention — the trash is deleted immediately.\n *\n * Idempotent: a no-marker call is a no-op; a crash mid-swap self-heals on the\n * next boot (staging still present → retried; already-swapped → marker's\n * staging path is gone → the marker is discarded and the new `current` stands).\n */\nexport function applyPendingRootSwap(\n rootDir: string,\n spec: RootPackageSpec,\n nodeMajor: number,\n now: () => number,\n log: LogFn,\n): ApplyRootSwapResult {\n const marker = readPendingRootSwap(rootDir)\n if (marker === null) return { applied: false, version: null, reason: 'no pending swap' }\n\n const staging = marker.stagingPath\n const dest = currentDir(rootDir)\n\n const invalid = validateClosureDir(staging, nodeMajor, spec, marker.version)\n if (invalid !== null) {\n // Staging gone but `current` is already the target ⇒ a prior swap\n // completed and only the marker survived a crash. Accept + clear.\n if (\n !fs.existsSync(staging) &&\n validateClosureDir(dest, nodeMajor, spec, marker.version) === null\n ) {\n clearPendingRootSwap(rootDir)\n log(`[single-copy] pending swap ${marker.version} already applied — cleared marker`)\n return { applied: true, version: marker.version, reason: 'already applied' }\n }\n log(`[single-copy] staged swap ${marker.version} invalid — discarding (${invalid})`)\n rmrf(staging)\n clearPendingRootSwap(rootDir)\n return { applied: false, version: null, reason: invalid }\n }\n\n const missing = findMissingNativePrebuilds(staging)\n if (missing.length > 0) {\n log(\n `[single-copy] staged swap ${marker.version} missing native prebuilds: ${missing.join(', ')} — discarding`,\n )\n rmrf(staging)\n clearPendingRootSwap(rootDir)\n return {\n applied: false,\n version: null,\n reason: `missing native prebuilds: ${missing.join(', ')}`,\n }\n }\n\n let trash: string | null = null\n try {\n if (fs.existsSync(dest)) {\n trash = path.join(rootDir, `.trash-${now()}-${process.pid}`)\n fs.renameSync(dest, trash)\n }\n try {\n fs.renameSync(staging, dest)\n } catch (err) {\n if (trash !== null && !fs.existsSync(dest)) {\n try {\n fs.renameSync(trash, dest)\n } catch {\n /* leave it for the confirm-boot sweep */\n }\n }\n throw err\n }\n } catch (err) {\n log(`[single-copy] swap of ${marker.version} FAILED — current preserved (${errMsg(err)})`)\n return { applied: false, version: null, reason: errMsg(err) }\n }\n\n if (trash !== null) rmrf(trash)\n clearPendingRootSwap(rootDir)\n log(`[single-copy] applied staged root swap → ${marker.version}`)\n return { applied: true, version: marker.version, reason: 'applied' }\n}\n\nexport interface MigrateResult {\n readonly migrated: boolean\n readonly version: string | null\n readonly reason: string\n}\n\n/** Read a package.json `version` string from a closure dir, or null. */\nfunction readClosureVersion(closureDir: string, spec: RootPackageSpec): string | null {\n try {\n const raw: unknown = JSON.parse(\n fs.readFileSync(path.join(rootPackageDir(closureDir, spec), 'package.json'), 'utf-8'),\n )\n if (typeof raw === 'object' && raw !== null) {\n const version = (raw as Record<string, unknown>)['version']\n if (typeof version === 'string') return version\n }\n } catch {\n /* unreadable — unknown */\n }\n return null\n}\n\n/**\n * True when `versions/<version>` is a fully loadable closure that ALSO carries\n * every required native prebuild — the bar an in-flight OLD-format pending\n * update must clear before it may be adopted as the single copy. A half-staged\n * pending (bad entry, or missing `sharp`/`node-av`/`better-sqlite3`) must never\n * be promoted, or it would brick the forked runners with no rollback net.\n */\nfunction isAdoptablePendingClosure(\n rootDir: string,\n version: string,\n nodeMajor: number,\n spec: RootPackageSpec,\n): boolean {\n const dir = versionDir(rootDir, version)\n return (\n validateClosureDir(dir, nodeMajor, spec, version) === null &&\n findMissingNativePrebuilds(dir).length === 0\n )\n}\n\n/**\n * One-time migration from the legacy multi-copy layout to the single copy.\n * Idempotent + safe to run on every boot: when a valid `current` already\n * exists it is a no-op (unless superseded by an in-flight pending — below).\n *\n * On a hub that predates the collapse, `current` is absent but\n * `versions/<active>` exists (recorded by the legacy `state.json`, or\n * discoverable by scanning `versions/`). Migration ADOPTS a legacy version's\n * closure as the single copy (`versions/<X>` → `current`), then removes the\n * legacy `versions/` tree, the legacy `state.json`, and the legacy\n * `/data/framework` swap markers — leaving non-framework addons in\n * `/data/addons` untouched. When no valid legacy closure exists (fresh\n * install) it is a no-op and the caller boots the baked seed.\n *\n * IN-FLIGHT PENDING (#17). A PRE-collapse `root-update-service` staged an\n * update as `versions/<pendingBoot.version>` + `state.json.pendingBoot` but\n * restarted before promoting it (the OLD mechanism had no single copy to swap\n * into). The AGENTS still run that mechanism, so the collapse migration MUST\n * recognise an in-flight pending: an adoptable `versions/<pendingBoot.version>`\n * (valid closure WITH native prebuilds) is preferred over the recorded active\n * version, and it even supersedes an already-present but STRICTLY OLDER\n * `current` (the edge where a seed placed an older copy beside the staged\n * pending). Without this the update strands on the old version — the live-hub\n * \"stuck on current, promote manually\" symptom the agents would otherwise hit\n * on rollout. Idempotent: once adopted, `state.json`/`versions/` are gone and\n * the next boot is a plain no-op.\n */\nexport function migrateToSingleCopy(\n rootDir: string,\n spec: RootPackageSpec,\n nodeMajor: number,\n now: () => number,\n log: LogFn,\n): MigrateResult {\n const dest = currentDir(rootDir)\n const legacyState = readServerRootState(rootDir)\n\n // An in-flight OLD-format pending update must win over the recorded active\n // version (and over a strictly-older existing `current`).\n const pendingVersion = legacyState?.pendingBoot?.version ?? null\n const pendingAdoptable =\n pendingVersion !== null && isAdoptablePendingClosure(rootDir, pendingVersion, nodeMajor, spec)\n\n const currentValid = fs.existsSync(dest) && validateClosureDir(dest, nodeMajor, spec) === null\n if (currentValid) {\n // A valid `current` stands UNLESS an adoptable pending update is strictly\n // newer than it. Steady state (no pending, or pending ≤ current) is a no-op.\n const currentVersion = readClosureVersion(dest, spec)\n const pendingSupersedes =\n pendingAdoptable &&\n pendingVersion !== null &&\n (currentVersion === null || compareSemver(pendingVersion, currentVersion) > 0)\n if (!pendingSupersedes) {\n return { migrated: false, version: null, reason: 'current already present' }\n }\n log(\n `[single-copy] in-flight pending ${pendingVersion} supersedes older current ${currentVersion ?? '?'}`,\n )\n }\n\n // Candidate legacy versions in PRIORITY order:\n // 1. an adoptable in-flight pending update (must win — see #17 above),\n // 2. what state.json recorded as active, then N-1,\n // 3. whatever else is on disk under versions/ (state missing/corrupt).\n const candidates: string[] = []\n if (pendingAdoptable && pendingVersion !== null) candidates.push(pendingVersion)\n if (legacyState !== null) {\n for (const v of [\n legacyState.currentVersion,\n legacyState.pendingBoot?.version ?? null,\n legacyState.previousVersion,\n ]) {\n if (typeof v === 'string' && !candidates.includes(v)) candidates.push(v)\n }\n }\n try {\n for (const entry of fs.readdirSync(versionsDir(rootDir))) {\n if (!entry.startsWith('.') && !candidates.includes(entry)) candidates.push(entry)\n }\n } catch {\n /* no versions dir */\n }\n\n const source = candidates.find(\n (v) => validateClosureDir(versionDir(rootDir, v), nodeMajor, spec, v) === null,\n )\n if (source === undefined) {\n return { migrated: false, version: null, reason: 'no valid legacy versions/<X> to adopt' }\n }\n\n // An invalid leftover `current` (partial migration) must not block the rename.\n if (fs.existsSync(dest)) {\n const aside = path.join(rootDir, `.trash-${now()}-${process.pid}-stale-current`)\n try {\n fs.renameSync(dest, aside)\n } catch {\n rmrf(dest)\n }\n }\n\n try {\n fs.renameSync(versionDir(rootDir, source), dest)\n } catch (err) {\n log(`[single-copy] migration rename of versions/${source} failed (${errMsg(err)})`)\n return { migrated: false, version: null, reason: errMsg(err) }\n }\n\n // Retire the legacy machinery (best-effort — none of these can brick boot).\n rmrf(versionsDir(rootDir))\n rmrf(stateFilePath(rootDir))\n const dataDir = path.dirname(rootDir)\n for (const marker of LEGACY_FRAMEWORK_MARKERS) rmrf(path.join(dataDir, marker))\n log(`[single-copy] migrated legacy versions/${source} → current`)\n return { migrated: true, version: source, reason: 'adopted legacy version' }\n}\n\n/**\n * Best-effort sweep of orphaned transient dirs (`.staging-*` / `.trash-*`)\n * older than `staleMs`. Called post-boot once the node is confirmed healthy —\n * a concurrent `applyServerUpdate` may be mid-install into a FRESH `.staging-*`\n * dir, so only dirs whose mtime is older than the window are collected.\n */\nexport function sweepTransientRootDirs(rootDir: string, now: number, staleMs: number): void {\n let entries: string[]\n try {\n entries = fs.readdirSync(rootDir)\n } catch {\n return\n }\n for (const entry of entries) {\n if (!entry.startsWith('.staging-') && !entry.startsWith('.trash-')) continue\n const full = path.join(rootDir, entry)\n try {\n if (now - fs.statSync(full).mtimeMs < staleMs) continue\n } catch {\n continue\n }\n rmrf(full)\n }\n}\n","/**\n * Minimal semver comparison — zero-dep (used by the starter, which must never\n * import from node_modules). Handles `MAJOR.MINOR.PATCH[-prerelease]`:\n * numeric segment compare, missing segments = 0, prerelease < its release,\n * prereleases of the same base compare lexically. Malformed segments compare\n * as 0 (best effort — never throws).\n */\n\ntype SemverParts = {\n readonly nums: readonly number[]\n readonly prerelease: string | null\n}\n\nfunction parse(version: string): SemverParts {\n const dashIdx = version.indexOf('-')\n const base = dashIdx === -1 ? version : version.slice(0, dashIdx)\n const prerelease = dashIdx === -1 ? null : version.slice(dashIdx + 1)\n const nums = base.split('.').map((seg) => {\n const n = Number.parseInt(seg, 10)\n return Number.isNaN(n) ? 0 : n\n })\n return { nums, prerelease }\n}\n\n/** Returns -1 when a < b, 0 when equal, 1 when a > b. */\nexport function compareSemver(a: string, b: string): -1 | 0 | 1 {\n const pa = parse(a)\n const pb = parse(b)\n const len = Math.max(pa.nums.length, pb.nums.length)\n for (let i = 0; i < len; i++) {\n const na = pa.nums[i] ?? 0\n const nb = pb.nums[i] ?? 0\n if (na < nb) return -1\n if (na > nb) return 1\n }\n if (pa.prerelease === null && pb.prerelease === null) return 0\n if (pa.prerelease === null) return 1\n if (pb.prerelease === null) return -1\n if (pa.prerelease < pb.prerelease) return -1\n if (pa.prerelease > pb.prerelease) return 1\n return 0\n}\n","/**\n * Pure boot-plan decision for the server-root starter — ZERO-DEP.\n *\n * Single-copy model (2026-07-18 collapse): the swap + migration have already\n * run by the time this is consulted, so the decision reduces to \"is the ONE\n * canonical `current/` closure present and valid?\". If yes → boot it; if no\n * (fresh install, or a corrupt/absent copy) → boot the baked image seed.\n *\n * The probation / auto-rollback / N-1 / baked-seed-adoption machinery of the\n * legacy multi-copy model is GONE: there is no second copy to compare against\n * or roll back to (operator's accepted tradeoff — manual recovery only).\n *\n * Spec: docs/superpowers/specs/2026-07-18-single-framework-copy-collapse-design.md\n */\n\nexport type BootPlan =\n | { readonly kind: 'current'; readonly reason: string }\n | { readonly kind: 'baked'; readonly reason: string }\n\n/**\n * Decide the boot target from a single boolean: whether the canonical\n * `current/` closure validated. Never throws.\n */\nexport function planBoot(currentValid: boolean): BootPlan {\n if (currentValid) {\n return { kind: 'current', reason: 'single-copy closure present and valid' }\n }\n return { kind: 'baked', reason: 'no valid single-copy closure — booting the baked seed' }\n}\n","/**\n * Workspace-checkout detection for the starter — ZERO-DEP.\n *\n * The starter must defer to plain resolution in a dev checkout (`npm run\n * dev:full` / `npm start` from the monorepo): the data-dir server root is a\n * production mechanism and must never shadow freshly-built workspace code.\n * Detection = any ancestor package.json declaring `workspaces` (the monorepo\n * root has one; the baked image run-dir's `npm init -y` package.json and the\n * data-dir version closures never do).\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\n/**\n * Walk up from `fromDir` looking for a package.json with a `workspaces`\n * field. Returns the directory containing it, or null when none exists.\n */\nexport function detectWorkspaceRoot(fromDir: string): string | null {\n let dir = path.resolve(fromDir)\n for (;;) {\n const pkgPath = path.join(dir, 'package.json')\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n if (\n typeof raw === 'object' &&\n raw !== null &&\n (raw as Record<string, unknown>)['workspaces'] !== undefined\n ) {\n return dir\n }\n } catch {\n // missing / unreadable package.json — keep walking\n }\n const parent = path.dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n}\n","/**\n * Shared STARTER core — the boot flow both node starters (hub + agent) run.\n * ZERO-DEP: bundled into each starter at build time; must never resolve\n * anything from node_modules at runtime.\n *\n * Single-copy boot sequence (2026-07-18 collapse — identical on both node\n * types; per-node names arrive via {@link NodeStarterOptions}):\n * 1. Workspace checkout? → defer to plain resolution (load the sibling seed\n * entry; the dev loop never runs the built starter at all).\n * 2. Apply any staged root swap (`applyServerUpdate` left a pending marker),\n * then run the one-time migration from the legacy multi-copy layout to the\n * single `current/` copy. Both are idempotent + crash-safe.\n * 3. Is the ONE canonical `current/` closure valid? → register in-process\n * resolver hooks so the node's MAIN process resolves the host-external\n * packages (@camstack/system, …) from `current/node_modules`, point the\n * forked-runner framework dir at the same copy, and load its entry.\n * 4. Otherwise → load the baked seed entry next to the starter.\n *\n * There is NO probation boot, NO auto-rollback, and NO baked-seed-adoption\n * semver compare — one copy, replaced in place, manual recovery only.\n *\n * Console usage is intentional: the starter runs before any logging exists.\n * Every line is prefixed `[starter]`.\n */\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { planBoot } from './boot-plan.js'\nimport type { RootPackageSpec } from './root-package-spec.js'\nimport { rootPackageDir, serverRootDir, validateClosureDir } from './root-state.js'\nimport {\n applyPendingRootSwap,\n currentDir,\n currentEntryPath,\n migrateToSingleCopy,\n} from './root-single-copy.js'\nimport { detectWorkspaceRoot } from './workspace-detect.js'\n\n/**\n * Mirror of the host-external list in\n * `packages/system/src/kernel/moleculer/framework-resolver-hook.mjs` — kept\n * inline because the starter must stay zero-dep. Only these specifiers are\n * redirected to the active root; everything else resolves normally.\n */\nexport const HOST_EXTERNAL_SPECIFIERS: readonly string[] = [\n '@camstack/system',\n '@camstack/shm-ring',\n '@camstack/types',\n '@camstack/sdk',\n 'zod',\n '@trpc/server',\n '@trpc/client',\n 'sharp',\n 'node-av',\n]\n\nexport function isHostExternal(specifier: string): boolean {\n return HOST_EXTERNAL_SPECIFIERS.some(\n (name) => specifier === name || specifier.startsWith(`${name}/`),\n )\n}\n\ntype ResolveContext = { readonly parentURL?: string }\ntype NextResolve = (specifier: string, context?: ResolveContext) => unknown\ntype RegisterHooksFn = (hooks: {\n resolve: (specifier: string, context: ResolveContext, nextResolve: NextResolve) => unknown\n}) => void\n\n/**\n * Register in-process resolver hooks (Node >= 22.15 `module.registerHooks`,\n * covers BOTH require and import) that anchor host-external resolution inside\n * the single copy's node_modules. Failure-tolerant: if the anchored resolve\n * fails (package absent from the closure), fall back to the default walk.\n */\nexport function registerActiveRootResolver(activeRootDir: string): void {\n // Documented type boundary: `module.registerHooks` is not yet in the\n // bundled @types/node — feature-detect it off the untyped module surface.\n const moduleApi: Record<string, unknown> = require('node:module')\n const registerHooks = moduleApi['registerHooks']\n if (typeof registerHooks !== 'function') {\n console.warn(\n '[starter] module.registerHooks unavailable (Node < 22.15) — host-external redirect skipped',\n )\n return\n }\n const anchorURL = pathToFileURL(\n path.join(activeRootDir, 'node_modules', '__camstack_starter_anchor__.js'),\n ).href\n const hooks: Parameters<RegisterHooksFn>[0] = {\n resolve: (specifier, context, nextResolve) => {\n if (isHostExternal(specifier)) {\n try {\n return nextResolve(specifier, { ...context, parentURL: anchorURL })\n } catch {\n // Not in the active root's closure — fall through to default.\n }\n }\n return nextResolve(specifier, context)\n },\n }\n ;(registerHooks as RegisterHooksFn)(hooks)\n}\n\n/** Per-node env-var names the starter writes / reads. */\nexport interface StarterEnvNames {\n /** Boot-mode marker read by the node's update service (`workspace`/`baked`/`data-root`). */\n readonly bootMode: string\n /** Absolute path of the active single-copy dir (data-root boots only). */\n readonly activeRoot: string\n /** Active data-root version (data-root boots only). */\n readonly activeVersion: string\n /** Kill switch: `<killSwitch>=off` forces the baked seed closure. */\n readonly killSwitch: string\n}\n\nexport interface NodeStarterOptions {\n readonly spec: RootPackageSpec\n readonly envNames: StarterEnvNames\n /** The node's resolved data dir (owner-specific env/argv resolution). */\n readonly dataDir: string\n /** Absolute path of the baked seed entry sibling to the starter. */\n readonly seedEntry: string\n /** The starter's own dir (`__dirname`) — anchors workspace detection. */\n readonly starterDir: string\n /** Load an entry file (a plain CJS `require` in the real starters). */\n readonly loadEntry: (entryPath: string) => void\n /** Test seams. Defaults: `process.env`, `Date.now`, running node major. */\n readonly env?: NodeJS.ProcessEnv\n readonly now?: () => number\n readonly nodeMajor?: number\n readonly registerResolver?: (activeRootDir: string) => void\n}\n\n/** Read a package.json `version` string, or null on any failure. */\nfunction readClosureVersion(closureDir: string, spec: RootPackageSpec): string | null {\n try {\n const raw: unknown = JSON.parse(\n fs.readFileSync(path.join(rootPackageDir(closureDir, spec), 'package.json'), 'utf-8'),\n )\n if (typeof raw === 'object' && raw !== null) {\n const version = (raw as Record<string, unknown>)['version']\n if (typeof version === 'string') return version\n }\n } catch {\n /* unreadable — unknown */\n }\n return null\n}\n\n/**\n * Run the shared starter flow. Never throws for boot-plan reasons — every\n * failure path degrades to the baked seed. (A `loadEntry` throw propagates:\n * that is the caller's fatal handler's job.)\n */\nexport function runNodeStarter(options: NodeStarterOptions): void {\n const env = options.env ?? process.env\n const now = options.now ?? Date.now\n const registerResolver = options.registerResolver ?? registerActiveRootResolver\n const nodeMajor =\n options.nodeMajor ?? Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10)\n\n // ── 1. Workspace checkout → plain resolution, dev loop untouched ────────\n const disabled = env[options.envNames.killSwitch] === 'off'\n const workspaceRoot = detectWorkspaceRoot(options.starterDir)\n if (workspaceRoot !== null) {\n console.log(\n `[starter] workspace checkout detected at ${workspaceRoot} — using plain resolution`,\n )\n env[options.envNames.bootMode] = 'workspace'\n options.loadEntry(options.seedEntry)\n return\n }\n if (disabled) {\n console.log(`[starter] ${options.envNames.killSwitch}=off — booting the baked seed closure`)\n env[options.envNames.bootMode] = 'baked'\n options.loadEntry(options.seedEntry)\n return\n }\n\n const rootDir = serverRootDir(options.dataDir)\n\n // ── 2. Apply a staged swap, then migrate the legacy layout (idempotent) ──\n const swap = applyPendingRootSwap(rootDir, options.spec, nodeMajor, now, (m) => console.log(m))\n if (swap.applied) console.log(`[starter] applied staged update → ${swap.version ?? '?'}`)\n else if (swap.reason !== 'no pending swap')\n console.warn(`[starter] staged update NOT applied: ${swap.reason}`)\n\n const migration = migrateToSingleCopy(rootDir, options.spec, nodeMajor, now, (m) =>\n console.log(m),\n )\n if (migration.migrated)\n console.log(`[starter] migrated legacy layout → single copy (${migration.version ?? '?'})`)\n\n // ── 3. Boot the single copy when valid, else the baked seed ─────────────\n const activeRootDir = currentDir(rootDir)\n const invalid = validateClosureDir(activeRootDir, nodeMajor, options.spec)\n if (invalid !== null && fs.existsSync(activeRootDir)) {\n console.warn(`[starter] single copy invalid: ${invalid}`)\n }\n const plan = planBoot(invalid === null)\n\n if (plan.kind === 'current') {\n const version = readClosureVersion(activeRootDir, options.spec)\n const entry = currentEntryPath(rootDir, options.spec)\n console.log(\n `[starter] booting ${options.spec.packageName}@${version ?? '?'} from ${activeRootDir}`,\n )\n env[options.envNames.bootMode] = 'data-root'\n // `activeRoot` IS the single copy — both node entries (hub launcher / agent\n // cli) derive `CAMSTACK_FRAMEWORK_DIR` (forked-runner resolver + NODE_PATH)\n // from it, so the runners resolve `@camstack/{system,shm-ring,…}` + native\n // prebuilds from the SAME copy hub-main loaded (no separate /data/framework).\n env[options.envNames.activeRoot] = activeRootDir\n if (version !== null) env[options.envNames.activeVersion] = version\n registerResolver(activeRootDir)\n options.loadEntry(entry)\n return\n }\n\n console.log(`[starter] booting baked seed closure (${plan.reason})`)\n env[options.envNames.bootMode] = 'baked'\n options.loadEntry(options.seedEntry)\n}\n","/**\n * RootUpdateService — the shared install/update engine for a node's\n * runtime-updatable ROOT package (`@camstack/server` on the hub,\n * `@camstack/agent` on agents). The hub's `ServerUpdateService` and the\n * agent's `AgentUpdateService` are thin adapters over this one class.\n *\n * SINGLE-COPY model (2026-07-18 collapse):\n * - stage a new `<pkg>@X` closure into a same-fs `.staging-*` dir\n * (npm install), validate it (entry + version + native prebuilds),\n * - write the `.pending-root-swap.json` marker and self-restart — the\n * STARTER performs the atomic swap of the ONE `current/` copy on the next\n * boot (nothing holds `current` open then), then loads it,\n * - back the `server-management` cap methods on every node type.\n *\n * There is NO `versions/<X>` + N-1 rollback copy, NO probation boot, and NO\n * auto-rollback. Operator's accepted tradeoff: a bad update is recovered\n * manually (re-apply a known-good version, or reinstall the image seed).\n *\n * Spec: docs/superpowers/specs/2026-07-18-single-framework-copy-collapse-design.md\n */\nimport { execFile } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { promisify } from 'node:util'\nimport type {\n ServerBootMode,\n ServerPackageStatus,\n ServerUpdateActionResult,\n ServerUpdateCheckResult,\n ServerUpdateState,\n} from '@camstack/types'\nimport {\n DEV_UPLOADS_KEEP_COUNT,\n devChannelEpoch,\n devUploadVersionDir,\n devUploadsDir,\n isDevChannelVersion,\n readDevUploadManifest,\n} from './dev-uploads.js'\nimport type { RootPackageSpec } from './root-package-spec.js'\nimport { compareSemver } from './semver-compare.js'\nimport { rootEntryPath, serverRootDir, validateClosureDir } from './root-state.js'\nimport {\n findMissingNativePrebuilds,\n readPendingRootSwap,\n stagingDirPath,\n sweepTransientRootDirs,\n writePendingRootSwap,\n} from './root-single-copy.js'\nimport { detectWorkspaceRoot } from './workspace-detect.js'\n\nconst execFileAsync = promisify(execFile)\n\n/** npm registry override args — mirrors addon-package.service (module-local there). */\nfunction buildNpmRegistryArgs(registry: string | undefined): readonly string[] {\n if (registry === undefined || registry.length === 0) return []\n return ['--registry', registry, `--@camstack:registry=${registry}`]\n}\n\nexport type NpmExecFn = (\n args: readonly string[],\n opts: { readonly cwd?: string; readonly timeoutMs: number },\n) => Promise<{ stdout: string }>\n\n/**\n * Structural logger surface — satisfied by the hub's `IScopedLogger` AND the\n * agent's console logger, without a runtime dependency on `@camstack/types`.\n */\nexport interface RootUpdateLogger {\n debug(message: string, context?: { readonly meta?: Record<string, unknown> }): void\n info(message: string, context?: { readonly meta?: Record<string, unknown> }): void\n warn(message: string, context?: { readonly meta?: Record<string, unknown> }): void\n error(message: string, context?: { readonly meta?: Record<string, unknown> }): void\n}\n\n/** Per-node env-var names the update service reads (written by the starter). */\nexport interface RootUpdateEnvNames {\n /** `workspace` / `baked` / `data-root` marker set by the starter. */\n readonly bootMode: string\n /** Active data-root version (data-root boots only). */\n readonly activeVersion: string\n /** Directory of the immutable baked seed root package (image-set). */\n readonly seedDir: string\n}\n\nexport interface RootUpdateServiceOptions {\n readonly spec: RootPackageSpec\n readonly envNames: RootUpdateEnvNames\n readonly logger: RootUpdateLogger\n /** Graceful restart seam (hub: AddonPackageService.restartServer; agent: SIGTERM self). */\n readonly restartServer: (requestedBy: string) => void\n /** The node's resolved data dir (adapters own the per-node default). */\n readonly dataDir: string\n /** package.json of the RUNNING root-package copy (adapters own the default). */\n readonly runningPackageJsonPath: string\n /** Anchor for the boot-mode fallback classification (adapter `__dirname`). */\n readonly workspaceProbeDir: string\n /** Overrides for tests. */\n readonly execNpm?: NpmExecFn\n readonly env?: NodeJS.ProcessEnv\n readonly now?: () => number\n}\n\ninterface CheckCache {\n readonly latestVersion: string | null\n readonly checkedAtMs: number\n readonly error: string | null\n}\n\nconst NPM_VIEW_TIMEOUT_MS = 20_000\nconst NPM_INSTALL_TIMEOUT_MS = 15 * 60_000\nconst RESTART_REASON_PREFIX = 'server-update'\n/** Transient (`.staging-*` / `.trash-*`) dirs younger than this are never swept. */\nconst STALE_TRANSIENT_MS = 24 * 60 * 60 * 1000\n\nfunction readPackageVersion(pkgJsonPath: string): string | null {\n try {\n const raw: unknown = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))\n if (typeof raw === 'object' && raw !== null) {\n const version = (raw as Record<string, unknown>)['version']\n if (typeof version === 'string') return version\n }\n } catch {\n // missing / malformed — treated as unknown\n }\n return null\n}\n\nexport class RootUpdateService {\n private readonly spec: RootPackageSpec\n private readonly envNames: RootUpdateEnvNames\n private readonly logger: RootUpdateLogger\n private readonly restartServerFn: (requestedBy: string) => void\n private readonly dataDir: string\n private readonly execNpm: NpmExecFn\n private readonly env: NodeJS.ProcessEnv\n private readonly now: () => number\n private readonly runningPackageJsonPath: string\n private readonly workspaceProbeDir: string\n\n private checkCache: CheckCache | null = null\n private inFlight: 'idle' | 'checking' | 'staging' = 'idle'\n\n constructor(options: RootUpdateServiceOptions) {\n this.spec = options.spec\n this.envNames = options.envNames\n this.logger = options.logger\n this.restartServerFn = options.restartServer\n this.dataDir = path.resolve(options.dataDir)\n this.execNpm =\n options.execNpm ??\n (async (args, opts) => {\n const { stdout } = await execFileAsync('npm', [...args], {\n cwd: opts.cwd,\n timeout: opts.timeoutMs,\n })\n return { stdout }\n })\n this.env = options.env ?? process.env\n this.now = options.now ?? Date.now\n this.runningPackageJsonPath = options.runningPackageJsonPath\n this.workspaceProbeDir = options.workspaceProbeDir\n }\n\n // ── Status ────────────────────────────────────────────────────────────\n\n resolveBootMode(): ServerBootMode {\n const raw = this.env[this.envNames.bootMode]\n if (raw === 'workspace' || raw === 'baked' || raw === 'data-root') return raw\n // The starter didn't run (dev tsx entry, `npm start`, or a pre-starter\n // image) — classify by checkout shape.\n return detectWorkspaceRoot(this.workspaceProbeDir) !== null ? 'workspace' : 'baked'\n }\n\n private runningVersion(): string | null {\n return readPackageVersion(this.runningPackageJsonPath)\n }\n\n /** The running root package's identity — feeds registerNode / topology rows. */\n getRunningRootPackage(): { readonly name: string; readonly version: string | null } {\n return { name: this.spec.packageName, version: this.runningVersion() }\n }\n\n private seedVersion(): string | null {\n const seedDir = this.env[this.envNames.seedDir]\n if (seedDir === undefined || seedDir.length === 0) return null\n return readPackageVersion(path.join(seedDir, 'package.json'))\n }\n\n private rootDir(): string {\n return serverRootDir(this.dataDir)\n }\n\n private updateState(): ServerUpdateState {\n if (this.inFlight === 'checking') return 'checking'\n if (this.inFlight === 'staging') return 'staging'\n // A staged version awaiting the swap-on-restart shows as pending-restart.\n if (readPendingRootSwap(this.rootDir()) !== null) return 'pending-restart'\n return 'idle'\n }\n\n async getServerPackageStatus(): Promise<ServerPackageStatus> {\n const runningVersion = this.runningVersion()\n const latestVersion = this.checkCache?.latestVersion ?? null\n const pending = readPendingRootSwap(this.rootDir())\n const updateAvailable =\n latestVersion !== null &&\n runningVersion !== null &&\n compareSemver(latestVersion, runningVersion) > 0\n return {\n packageName: this.spec.packageName,\n runningVersion,\n nodeRuntimeVersion: process.versions.node,\n activeVersion: this.env[this.envNames.activeVersion] ?? runningVersion,\n // Single-copy model: no N-1 retention, no rollback record.\n previousVersion: null,\n seedVersion: this.seedVersion(),\n latestVersion,\n updateAvailable,\n bootMode: this.resolveBootMode(),\n updateState: this.updateState(),\n pendingVersion: pending?.version ?? null,\n rolledBack: null,\n stateFileCorrupt: false,\n lastCheckedAtMs: this.checkCache?.checkedAtMs ?? null,\n }\n }\n\n // ── Check ─────────────────────────────────────────────────────────────\n\n async checkServerUpdate(): Promise<ServerUpdateCheckResult> {\n const runningVersion = this.runningVersion()\n if (this.inFlight !== 'idle') {\n return {\n packageName: this.spec.packageName,\n runningVersion,\n latestVersion: this.checkCache?.latestVersion ?? null,\n updateAvailable: false,\n checkedAtMs: this.checkCache?.checkedAtMs ?? this.now(),\n error: `busy: ${this.inFlight}`,\n }\n }\n this.inFlight = 'checking'\n try {\n const registry = this.env['CAMSTACK_NPM_REGISTRY']\n const args = [\n 'view',\n `${this.spec.packageName}@latest`,\n 'version',\n ...buildNpmRegistryArgs(registry),\n ]\n const { stdout } = await this.execNpm(args, { timeoutMs: NPM_VIEW_TIMEOUT_MS })\n const latestVersion = stdout.trim().length > 0 ? stdout.trim() : null\n this.checkCache = { latestVersion, checkedAtMs: this.now(), error: null }\n const updateAvailable =\n latestVersion !== null &&\n runningVersion !== null &&\n compareSemver(latestVersion, runningVersion) > 0\n return {\n packageName: this.spec.packageName,\n runningVersion,\n latestVersion,\n updateAvailable,\n checkedAtMs: this.checkCache.checkedAtMs,\n error: null,\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.checkCache = {\n latestVersion: this.checkCache?.latestVersion ?? null,\n checkedAtMs: this.now(),\n error: message,\n }\n this.logger.warn('root package update check failed', { meta: { error: message } })\n return {\n packageName: this.spec.packageName,\n runningVersion,\n latestVersion: this.checkCache.latestVersion,\n updateAvailable: false,\n checkedAtMs: this.checkCache.checkedAtMs,\n error: message,\n }\n } finally {\n this.inFlight = 'idle'\n }\n }\n\n // ── Apply ─────────────────────────────────────────────────────────────\n\n async applyServerUpdate(input: { version?: string }): Promise<ServerUpdateActionResult> {\n const bootMode = this.resolveBootMode()\n if (bootMode === 'workspace') {\n return {\n accepted: false,\n targetVersion: input.version ?? null,\n restarting: false,\n message:\n 'Refused: running from a workspace checkout — the dev loop resolves code from the ' +\n 'workspace, a data-root update would never be loaded.',\n }\n }\n if (this.inFlight !== 'idle') {\n return {\n accepted: false,\n targetVersion: input.version ?? null,\n restarting: false,\n message: `Refused: another operation is in flight (${this.inFlight}).`,\n }\n }\n const pendingBefore = readPendingRootSwap(this.rootDir())\n if (pendingBefore !== null) {\n return {\n accepted: false,\n targetVersion: input.version ?? null,\n restarting: false,\n message: `Refused: version ${pendingBefore.version} is already staged and awaiting restart.`,\n }\n }\n\n // Resolve the target version (explicit or latest from the registry).\n let target = input.version ?? null\n if (target === null) {\n const check = await this.checkServerUpdate()\n if (check.error !== null || check.latestVersion === null) {\n return {\n accepted: false,\n targetVersion: null,\n restarting: false,\n message: `Refused: could not resolve the latest version (${check.error ?? 'no version returned'}).`,\n }\n }\n target = check.latestVersion\n }\n\n const runningVersion = this.runningVersion()\n if (target === runningVersion) {\n return {\n accepted: false,\n targetVersion: target,\n restarting: false,\n message: `Refused: ${this.spec.packageName}@${target} is already running.`,\n }\n }\n\n // Dev server-deploy channel boundary (phase 1: hub only). A `-dev.<epoch>`\n // version stages EXCLUSIVELY from tarballs previously uploaded to THIS\n // node's `server-root/dev-uploads/<version>/` — it never exists on the\n // npm registry, so a node without the uploads must fail loudly here.\n if (isDevChannelVersion(target)) {\n const uploadsDir = devUploadVersionDir(this.rootDir(), target)\n if (!fs.existsSync(uploadsDir)) {\n return {\n accepted: false,\n targetVersion: target,\n restarting: false,\n message:\n `Refused: dev-channel version ${target} has no uploaded tarballs on this node ` +\n `(${uploadsDir} is missing). Dev server deploys are hub-only in this phase — ` +\n `upload via \\`camstack deploy-server\\` against this node first.`,\n }\n }\n }\n\n this.inFlight = 'staging'\n let stagingPath: string\n try {\n stagingPath = await this.stageClosure(target)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.logger.error('root package update staging failed', {\n meta: { targetVersion: target, error: message },\n })\n return {\n accepted: false,\n targetVersion: target,\n restarting: false,\n message: `Staging failed: ${message}`,\n }\n } finally {\n this.inFlight = 'idle'\n }\n\n // Arm the swap-on-restart + schedule the graceful restart.\n writePendingRootSwap(this.rootDir(), {\n schemaVersion: 1,\n version: target,\n stagingPath,\n requestedAtMs: this.now(),\n })\n this.logger.info('root package update staged — restarting to apply', {\n meta: { targetVersion: target, fromVersion: runningVersion },\n })\n this.restartServerFn(`${RESTART_REASON_PREFIX}: ${this.spec.packageName}@${target}`)\n return {\n accepted: true,\n targetVersion: target,\n restarting: true,\n message: `Staged ${this.spec.packageName}@${target} — restarting to apply (single automatic reboot).`,\n }\n }\n\n /**\n * npm-install the target closure into a SAME-FS `.staging-*` dir, then\n * validate it (entry + version + native prebuilds). Returns the staging dir\n * path for the pending-root-swap marker; the STARTER swaps it into `current`\n * on the next boot. Throws (cleaning up the staging dir) on any failure so\n * `applyServerUpdate` reports a staging error and never arms a bad swap.\n */\n private async stageClosure(target: string): Promise<string> {\n const rootDir = this.rootDir()\n fs.mkdirSync(rootDir, { recursive: true })\n const stagingDir = stagingDirPath(rootDir, target, process.pid, this.now())\n fs.mkdirSync(stagingDir, { recursive: true })\n\n try {\n // Dev server-deploy channel: when tarballs for `target` were uploaded to\n // `server-root/dev-uploads/<target>/`, stage from THEM instead of the\n // registry — a synthetic package.json depends on the root tgz via\n // `file:` and pins every other uploaded package through `overrides`.\n const devDir = devUploadVersionDir(rootDir, target)\n const registry = this.env['CAMSTACK_NPM_REGISTRY']\n const installArgs = ['install', '--omit=dev', '--no-audit', '--no-fund', '--loglevel=error']\n if (fs.existsSync(devDir)) {\n fs.writeFileSync(\n path.join(stagingDir, 'package.json'),\n JSON.stringify(this.buildDevUploadsPackageJson(devDir, target), null, 2),\n 'utf-8',\n )\n await this.execNpm([...installArgs, ...buildNpmRegistryArgs(registry)], {\n cwd: stagingDir,\n timeoutMs: NPM_INSTALL_TIMEOUT_MS,\n })\n } else {\n fs.writeFileSync(\n path.join(stagingDir, 'package.json'),\n JSON.stringify({ name: 'camstack-node-root', private: true }, null, 2),\n 'utf-8',\n )\n await this.execNpm(\n [...installArgs, `${this.spec.packageName}@${target}`, ...buildNpmRegistryArgs(registry)],\n { cwd: stagingDir, timeoutMs: NPM_INSTALL_TIMEOUT_MS },\n )\n }\n\n const entry = rootEntryPath(stagingDir, this.spec)\n if (!fs.existsSync(entry)) {\n throw new Error(`staged closure is missing the root entry (${entry})`)\n }\n const nodeMajor = Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10)\n const invalid = validateClosureDir(stagingDir, nodeMajor, this.spec, target)\n if (invalid !== null) {\n throw new Error(`staged closure invalid: ${invalid}`)\n }\n // Native prebuild assertion — a runner that can't load node-av/sharp/\n // better-sqlite3 breaks decode/snapshot/db. Abort BEFORE arming the swap.\n const missingNatives = findMissingNativePrebuilds(stagingDir)\n if (missingNatives.length > 0) {\n throw new Error(\n `staged closure missing native prebuilds: ${missingNatives.join(', ')} — ` +\n 'refusing to arm a swap that would break the forked runners',\n )\n }\n return stagingDir\n } catch (err) {\n await fs.promises.rm(stagingDir, { recursive: true, force: true }).catch(() => undefined)\n throw err\n }\n }\n\n /**\n * Synthetic staging package.json for the dev server-deploy channel:\n * `dependencies` carries the root package as a `file:` ref to its uploaded\n * tgz; `overrides` maps EVERY OTHER uploaded `@camstack/<name>` to its tgz.\n * Fail-closed: a missing/invalid manifest, a manifest for a different\n * version, a missing root package, or a listed-but-absent tgz all throw.\n */\n private buildDevUploadsPackageJson(\n devDir: string,\n target: string,\n ): {\n readonly name: string\n readonly private: true\n readonly dependencies: Readonly<Record<string, string>>\n readonly overrides?: Readonly<Record<string, string>>\n } {\n const manifest = readDevUploadManifest(devDir)\n if (manifest === null) {\n throw new Error(`dev-uploads manifest missing or invalid in ${devDir}`)\n }\n if (manifest.version !== target) {\n throw new Error(\n `dev-uploads manifest version mismatch: dir is for ${target}, manifest says ${manifest.version}`,\n )\n }\n const rootTgz = manifest.packages[this.spec.packageName]\n if (rootTgz === undefined) {\n throw new Error(\n `dev-uploads for ${target} do not include the root package ${this.spec.packageName}`,\n )\n }\n const fileRef = (filename: string): string => {\n const abs = path.join(devDir, filename)\n if (!fs.existsSync(abs)) {\n throw new Error(`dev-uploads tarball listed in the manifest is missing: ${abs}`)\n }\n return `file:${abs}`\n }\n const overrides: Record<string, string> = {}\n for (const [pkgName, filename] of Object.entries(manifest.packages)) {\n if (pkgName === this.spec.packageName) continue\n overrides[pkgName] = fileRef(filename)\n }\n return {\n name: 'camstack-node-root',\n private: true,\n dependencies: { [this.spec.packageName]: fileRef(rootTgz) },\n ...(Object.keys(overrides).length > 0 ? { overrides } : {}),\n }\n }\n\n // ── Rollback (removed — single-copy has no N-1) ────────────────────────\n\n /**\n * Rollback is NOT supported in the single-copy model — there is no retained\n * N-1 copy to revert to (operator's accepted tradeoff). Recovery is manual:\n * re-apply a known-good version via {@link applyServerUpdate}, or reinstall\n * the image seed. Kept on the surface so the cap contract is unchanged.\n */\n async rollbackServerUpdate(): Promise<ServerUpdateActionResult> {\n return {\n accepted: false,\n targetVersion: null,\n restarting: false,\n message:\n 'Rollback is not supported in the single-copy model (no retained previous version). ' +\n 'Recover by re-applying a known-good version via applyServerUpdate, or reinstall the image seed.',\n }\n }\n\n // ── Plain restart ─────────────────────────────────────────────────────\n\n /**\n * Plain process restart — no version change. Refuse while a stage is in\n * flight (a concurrent npm install must not be interrupted) or while a\n * version is already staged awaiting restart (a naive bounce would boot the\n * OLD version and orphan the pending swap — the operator should apply\n * instead).\n */\n restartNode(): ServerUpdateActionResult {\n const runningVersion = this.runningVersion()\n if (this.inFlight !== 'idle') {\n return {\n accepted: false,\n targetVersion: runningVersion,\n restarting: false,\n message: `Refused: another operation is in flight (${this.inFlight}).`,\n }\n }\n if (readPendingRootSwap(this.rootDir()) !== null) {\n return {\n accepted: false,\n targetVersion: runningVersion,\n restarting: false,\n message:\n 'Refused: a version is staged and awaiting restart — apply it instead of a plain restart.',\n }\n }\n this.logger.info('plain node restart requested', { meta: { runningVersion } })\n this.restartServerFn(`${RESTART_REASON_PREFIX}: manual restart`)\n return {\n accepted: true,\n targetVersion: runningVersion,\n restarting: true,\n message: 'Restarting the node — the supervisor relaunches it on the same version.',\n }\n }\n\n // ── Boot health confirmation ──────────────────────────────────────────\n\n /**\n * Called by the post-boot path once the node is READY. In the single-copy\n * model there is no pending version to promote (the swap already happened in\n * the starter). This becomes a best-effort GC of orphaned transient dirs\n * (`.staging-*` / `.trash-*` left by a crash mid-swap) + the dev-uploads\n * sweep. Kept returning the legacy `{ promoted }` shape for the callers.\n */\n confirmBootHealthy(): { promoted: string | null } {\n sweepTransientRootDirs(this.rootDir(), this.now(), STALE_TRANSIENT_MS)\n this.sweepDevUploads(DEV_UPLOADS_KEEP_COUNT)\n return { promoted: null }\n }\n\n /**\n * Promote-time sweep of `server-root/dev-uploads/`: keep only the\n * `keepCount` most recent entries (ordered by the self-describing\n * `-dev.<epochSeconds>` suffix; non-dev-shaped residue counts as oldest).\n */\n private sweepDevUploads(keepCount: number): void {\n const dir = devUploadsDir(this.rootDir())\n let entries: string[]\n try {\n entries = fs.readdirSync(dir)\n } catch {\n return // no dev-uploads dir — nothing to sweep\n }\n const graveyard = path.join(dir, '.sweeping')\n // Best-effort collect residue left behind by an earlier sweep first.\n try {\n for (const residue of fs.readdirSync(graveyard)) {\n try {\n fs.rmSync(path.join(graveyard, residue), { recursive: true, force: true })\n } catch {\n /* still held open — retried on the next promote */\n }\n }\n } catch {\n /* graveyard absent — nothing to collect */\n }\n\n const versions = entries.filter((name) => !name.startsWith('.'))\n const byNewestFirst = [...versions].sort(\n (a, b) => (devChannelEpoch(b) ?? -1) - (devChannelEpoch(a) ?? -1),\n )\n const doomed = byNewestFirst.slice(keepCount)\n if (doomed.length === 0) return\n fs.mkdirSync(graveyard, { recursive: true })\n for (const entry of doomed) {\n const aside = path.join(graveyard, `${entry}-${this.now()}`)\n try {\n fs.renameSync(path.join(dir, entry), aside)\n } catch (err) {\n this.logger.warn('failed to move dev-uploads entry aside for sweep', {\n meta: { entry, error: err instanceof Error ? err.message : String(err) },\n })\n continue\n }\n try {\n fs.rmSync(aside, { recursive: true, force: true })\n this.logger.debug('swept dev-uploads entry', { meta: { entry } })\n } catch {\n /* held open — the aside copy is collected on the next promote */\n }\n }\n }\n}\n","/**\n * AGENT STARTER — the tiny, immutable, dependency-free entry baked into the\n * agent image (and, in phase 3, spawned by the Electron shell). Everything\n * above it (the agent CLI, framework, addons) is runtime-updatable; the\n * starter itself can NEVER self-update — a new image is the only way to\n * change it, which is why it must stay deliberately dumb.\n *\n * The boot flow is the SHARED node-root core (`runNodeStarter`, bundled into\n * this file by tsup — see tsup.config.ts): workspace defer → apply a staged\n * root swap + migrate the legacy layout to the single `current/` copy → the\n * host-external resolver hooks anchored in the active root → load the single\n * copy (or the baked seed). This file only binds the agent's names:\n * `@camstack/agent`, `dist/cli.js`, the `CAMSTACK_AGENT_*` env markers and the\n * `CAMSTACK_DATA_DIR` / `--data` data dir.\n *\n * Layout mirror of the hub (single-copy model):\n * <agentDataDir>/server-root/current/node_modules/@camstack/agent/dist/cli.js\n *\n * Console usage is intentional: the starter runs before any logging exists.\n */\nimport { existsSync } from 'node:fs'\nimport * as path from 'node:path'\nimport { AGENT_ROOT_SPEC, runNodeStarter } from '@camstack/node-root'\n\n/**\n * Resolve the agent data dir the way the CLI will (`CAMSTACK_DATA_DIR` env\n * wins, then `--data`/`-d` argv, then `./camstack-data`). The starter runs\n * BEFORE the CLI parses argv, so it mirrors the flag inline — otherwise an\n * operator running `camstack-agent --data /x` would get the server-root in a\n * different data dir than the agent itself.\n */\nfunction resolveAgentDataDir(argv: readonly string[], env: NodeJS.ProcessEnv): string {\n const fromEnv = env['CAMSTACK_DATA_DIR']\n if (fromEnv !== undefined && fromEnv.length > 0) return path.resolve(fromEnv)\n for (let i = 2; i < argv.length - 1; i++) {\n if (argv[i] === '--data' || argv[i] === '-d') {\n const next = argv[i + 1]\n if (next !== undefined && !next.startsWith('-')) return path.resolve(next)\n }\n }\n return path.resolve('camstack-data')\n}\n\n/**\n * Restart-policy dependency warning (security review #3). `server-management`\n * apply/rollback = graceful exit + supervisor relaunch. Without an\n * always-relaunching restart policy (docker: `unless-stopped`/`always`) a node\n * that exits to apply an update simply STAYS DOWN. No code can enforce the\n * policy — make the dependency visible at boot. Diagnostic only.\n */\nfunction warnRestartPolicyDependency(): void {\n if (!existsSync('/.dockerenv')) return\n console.warn(\n '[starter] container detected — server-management apply/rollback exit this ' +\n 'process for the supervisor to relaunch. Ensure the container restart policy is ' +\n \"'unless-stopped' or 'always', otherwise an update/rollback leaves the node down.\",\n )\n}\n\nfunction start(): void {\n warnRestartPolicyDependency()\n\n const seedPackageDir = path.resolve(__dirname, '..')\n if (process.env['CAMSTACK_SEED_AGENT_DIR'] === undefined) {\n process.env['CAMSTACK_SEED_AGENT_DIR'] = seedPackageDir\n }\n\n runNodeStarter({\n spec: AGENT_ROOT_SPEC,\n envNames: {\n bootMode: 'CAMSTACK_AGENT_BOOT_MODE',\n activeRoot: 'CAMSTACK_AGENT_ACTIVE_ROOT',\n activeVersion: 'CAMSTACK_AGENT_ACTIVE_VERSION',\n killSwitch: 'CAMSTACK_AGENT_ROOT',\n },\n dataDir: resolveAgentDataDir(process.argv, process.env),\n seedEntry: path.join(__dirname, 'cli.js'),\n starterDir: __dirname,\n // The CLI is CJS (tsup build) — a plain require keeps the starter\n // synchronous; the CLI reads the SAME process.argv it always did.\n loadEntry: (entryPath) => require(entryPath),\n })\n}\n\ntry {\n start()\n} catch (err) {\n console.error('[starter] FATAL:', err)\n process.exit(1)\n}\n"],"mappings":"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","names":["AGENT_ROOT_SPEC","runNodeStarter","module","__toESM","path","fs","path2","readClosureVersion"]}
|
package/dist/starter.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/agent",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.59",
|
|
4
4
|
"description": "CamStack remote agent — standalone Moleculer node for distributed addon execution",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"start": "node dist/cli.js"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@camstack/system": "1.1.
|
|
37
|
-
"@camstack/types": "1.1.
|
|
36
|
+
"@camstack/system": "1.1.58",
|
|
37
|
+
"@camstack/types": "1.1.52",
|
|
38
38
|
"@camstack/sdk": "1.1.30",
|
|
39
39
|
"@camstack/shm-ring": "1.0.29",
|
|
40
|
-
"@camstack/addon-pipeline": "1.1.
|
|
40
|
+
"@camstack/addon-pipeline": "1.1.65",
|
|
41
41
|
"@camstack/addon-decoder-nodeav": "1.1.18",
|
|
42
42
|
"@camstack/addon-agent-ui": "1.1.29",
|
|
43
43
|
"fastify": "^5",
|