@tempo-ai/visual-review 0.0.114 → 0.0.115
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/bin.js +1 -1
- package/dist/{chunk-RY7ZWRV5.js → chunk-BXSN22AO.js} +133 -27
- package/dist/chunk-BXSN22AO.js.map +1 -0
- package/dist/{cli-BJVATWX4.js → cli-HEA4IYKH.js} +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-RY7ZWRV5.js.map +0 -1
- /package/dist/{cli-BJVATWX4.js.map → cli-HEA4IYKH.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../../canvas-capture-headless/src/capture.ts","../../../modules/iframe-projection/combined/projection-pending.ts","../../../modules/component-render-pool/direct-render.ts","../../../modules/frame-pool/capture-determinism.ts","../../../modules/frame-pool/settle-script.ts","../../canvas-capture-headless/src/navigation-errors.ts","../../canvas-capture-headless/src/determinism.ts","../../canvas-capture-headless/src/ensure-chromium.ts","../../canvas-capture-headless/src/fingerprint.ts","../../canvas-capture-headless/src/html-document.ts","../../canvas-capture-headless/src/page-scripts.ts","../../canvas-capture-headless/src/render-profile.ts","../../canvas-capture-headless/src/composite.ts","../../canvas-capture-headless/src/devserver.ts","../../tempo-devserver/abortable-shell-command.ts","../../tempo-devserver/app-devserver.ts","../../../modules/abortable-wait/index.ts","../../tempo-devserver/bundled-node.ts","../../tempo-devserver/debug-log.ts","../../tempo-devserver/dev-lock-conflict.ts","../../tempo-devserver/shell-dialect.ts","../../tempo-devserver/hint-classifier.ts","../../tempo-devserver/port-listener.ts","../../tempo-devserver/rotating-file-log.ts","../../tempo-devserver/devserver-watchdog-source.ts","../../tempo-devserver/atomic-write.ts","../../tempo-devserver/http-url-source.ts","../../tempo-devserver/dev-server-pool.ts","../../tempo-devserver/dev-server-key.ts","../../tempo-devserver/dev-server-ownership-registry.ts","../../tempo-devserver/install-environment-fallback.ts","../../tempo-devserver/tempo-devserver.ts","../../../modules/file-watcher/provider.ts","../../../modules/file-watcher/watcher.ts","../../../modules/file-watcher/ignore.ts","../../../modules/file-watcher/polling-backend.ts","../../tempo-devserver/install-feedback.ts","../../tempo-sdk/package.json","../../tempo-devserver/canvas-host-capability.ts","../../tempo-devserver/canvas-host-recovery-metro-source.ts","../../tempo-devserver/canvas-host-recovery-source.ts","../../tempo-devserver/canvas-registry.ts","../../tempo-devserver/errors.ts","../../canvas-pages/page-parser.ts","../../canvas-pages/home-marker.ts","../../tempo-sdk/canvas/parser.ts","../../tempo-devserver/process-hint-slots.ts","../../tempo-devserver/package-manager.ts","../../tempo-devserver/project-node-version.ts","../../tempo-devserver/sdk-upgrade-plan.ts","../../tempo-devserver/tempo-sdk-dev-source.ts","../../tempo-devserver/read-status.ts","../../canvas-capture-headless/src/enumerate.ts","../../canvas-capture-headless/src/timing.ts","../src/enumerate.ts","../src/fingerprint.ts","../src/ingest-client.ts"],"sourcesContent":["import { execFileSync, spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, mkdtemp, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\n\nimport {\n buildEnvFingerprint,\n type CapturedStoryboard,\n formatDevserverReloadSignal,\n resolveCanvasHostLayout,\n startCaptureSession,\n startDevserverSession,\n TimingReport,\n} from \"@tempo-modules/canvas-capture-headless\";\n\nimport { DEFAULT_VISUAL_REVIEW_INGEST_URL } from \"./constants\";\nimport { enumerateTrackedStoryboards } from \"./enumerate\";\nimport { scaleIgnoreRegionsToDevicePx } from \"./fingerprint\";\nimport { githubPullRequestContext } from \"./github-actions\";\nimport {\n type BaselineClaimIdentity,\n type BaselineClaimResult,\n VisualReviewHttpError,\n VisualReviewIngestClient,\n} from \"./ingest-client\";\nimport { resolveIngestCredential } from \"./oidc\";\nexport { VISUAL_REVIEW_OIDC_AUDIENCE } from \"./oidc\";\nimport type { RunManifest } from \"./types\";\n\n/**\n * `visual-review capture` — the CI capture runner (TEM-1775 §3.2, §5.4).\n *\n * CI invocation — customers and Tempo's own dogfood workflow run the SAME\n * published command; trigger, head SHA, branch, merge-base and repository\n * are inferred from GitHub's event payload and the standard GITHUB_*\n * environment:\n *\n * npx --yes @tempo-ai/visual-review@latest capture --parallel 2\n *\n * Local, Convex-free mode (PR2's acceptance harness) writes PNGs + a\n * manifest + the timing report to disk:\n *\n * pnpm --filter @tempo-ai/visual-review capture -- --out /tmp/vrt\n *\n * Import-closure discipline (§5.4): this CLI only reaches\n * `@tempo-modules/tempo-devserver`, `tempo-sdk`'s parser, playwright, and\n * pngjs — the closure the future npm bundle will carry.\n */\n\ntype CliArgs = {\n repoRoot: string;\n tempoDir: string;\n /** `--tempo-dir` was given explicitly. Otherwise the canvas host is\n * discovered from tempo.config.json (`resolveCanvasHostLayout`), so a\n * legacy root-level config and `paths.canvases` are honored exactly as\n * the Tempo app resolves them. */\n tempoDirExplicit: boolean;\n out?: string;\n upload: boolean;\n url?: string;\n trigger: \"pr\" | \"main\" | \"manual\";\n prNumber?: number;\n headSha?: string;\n branch?: string;\n baseSha?: string;\n baseDepth: number;\n githubRepo?: string;\n repoKey?: string;\n parallel: number;\n settleTimeoutMs?: number;\n baselineClaimToken?: string;\n expectedEnvFingerprint?: string;\n};\n\n/** `--repo-root` default: the enclosing git worktree, NOT process.cwd() —\n * `pnpm --filter` runs package scripts from the package directory, which\n * would silently enumerate 0 canvases. */\nfunction inferRepoRoot(): string {\n try {\n return execFileSync(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n encoding: \"utf-8\",\n windowsHide: true,\n // Outside a checkout git prints `fatal: not a git repository`; the\n // fallback below is the answer, not a warning to surface.\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n })\n .toString()\n .trim();\n } catch {\n return process.cwd();\n }\n}\n\nfunction parseArgs(argv: string[]): CliArgs {\n const eventName = process.env.GITHUB_EVENT_NAME;\n const pullRequest = githubPullRequestContext();\n const inferredTrigger =\n eventName === \"pull_request\"\n ? \"pr\"\n : eventName === \"push\"\n ? \"main\"\n : \"manual\";\n const args: CliArgs = {\n repoRoot: inferRepoRoot(),\n tempoDir: \"\",\n tempoDirExplicit: false,\n // The published CLI is intentionally near-argumentless in Actions. Local\n // capture remains Convex-free unless --upload is explicit.\n upload:\n process.env.GITHUB_ACTIONS === \"true\" && inferredTrigger !== \"manual\",\n trigger: inferredTrigger,\n baseDepth: 50,\n prNumber: pullRequest?.number,\n headSha: pullRequest?.headSha ?? process.env.GITHUB_SHA,\n branch:\n pullRequest?.headRef ??\n process.env.GITHUB_HEAD_REF ??\n process.env.GITHUB_REF_NAME,\n githubRepo: process.env.GITHUB_REPOSITORY,\n // Reused pages make six workers faster than twelve: each worker pays one\n // cold React/module-graph boot, while later storyboard switches are warm.\n parallel: 6,\n };\n for (let index = 0; index < argv.length; index += 1) {\n const flag = argv[index];\n const next = () => {\n index += 1;\n const value = argv[index];\n if (value === undefined) throw new Error(`${flag} needs a value`);\n return value;\n };\n switch (flag) {\n case \"capture\":\n break; // subcommand name — accepted for npx-style symmetry\n case \"--repo-root\":\n args.repoRoot = path.resolve(next());\n break;\n case \"--tempo-dir\":\n args.tempoDir = path.resolve(next());\n args.tempoDirExplicit = true;\n break;\n case \"--out\":\n args.out = path.resolve(next());\n break;\n case \"--upload\":\n args.upload = true;\n break;\n case \"--no-upload\":\n args.upload = false;\n break;\n case \"--url\":\n args.url = next();\n break;\n case \"--trigger\":\n args.trigger = next() as CliArgs[\"trigger\"];\n break;\n case \"--pr\":\n args.prNumber = Number(next());\n break;\n case \"--head\":\n args.headSha = next();\n break;\n case \"--branch\":\n args.branch = next();\n break;\n case \"--base\":\n args.baseSha = next();\n break;\n case \"--base-depth\":\n args.baseDepth = Number(next());\n break;\n case \"--github-repo\":\n args.githubRepo = next();\n break;\n case \"--repo-key\":\n args.repoKey = next();\n break;\n case \"--parallel\":\n args.parallel = Number(next());\n break;\n case \"--settle-timeout\":\n args.settleTimeoutMs = Number(next());\n break;\n case \"--baseline-claim-token\":\n args.baselineClaimToken = next();\n break;\n case \"--expected-env-fingerprint\":\n args.expectedEnvFingerprint = next();\n break;\n default:\n throw new Error(`unknown flag ${flag}`);\n }\n }\n if (!args.tempoDir) args.tempoDir = path.join(args.repoRoot, \"tempo\");\n if (![\"pr\", \"main\", \"manual\"].includes(args.trigger)) {\n throw new Error(`--trigger must be pr|main|manual, got ${args.trigger}`);\n }\n return args;\n}\n\n/** A PR's event payload gives us the target branch, while the checked-out\n * history gives us its actual merge-base. The latter is the baseline key; the\n * payload SHA is a safe fallback for shallow/nonstandard Actions checkouts. */\nfunction inferPrBase(args: CliArgs): void {\n if (args.trigger !== \"pr\" || args.baseSha) return;\n const pullRequest = githubPullRequestContext();\n const baseRef = pullRequest?.baseRef ?? process.env.GITHUB_BASE_REF;\n if (baseRef) {\n try {\n args.baseSha = git(\n args.repoRoot,\n \"merge-base\",\n `origin/${baseRef}`,\n args.headSha ?? \"HEAD\",\n );\n } catch {\n // The next fallback preserves a useful, deterministic run on CI\n // providers that expose no remote branch ref after checkout.\n }\n }\n args.baseSha ??= pullRequest?.baseSha;\n}\n\nfunction git(repoRoot: string, ...gitArgs: string[]): string {\n return execFileSync(\"git\", gitArgs, {\n cwd: repoRoot,\n encoding: \"utf-8\",\n windowsHide: true,\n })\n .toString()\n .trim();\n}\n\n/** `host/owner/repo`, matching Convex's normalizeRepoUrl (lowercased). */\nfunction deriveRepoKey(args: CliArgs): string {\n if (args.repoKey) return args.repoKey.toLowerCase();\n if (args.githubRepo) return `github.com/${args.githubRepo}`.toLowerCase();\n const remote = git(args.repoRoot, \"remote\", \"get-url\", \"origin\");\n const match =\n remote.match(/^[\\w.-]+@([\\w.-]+):([\\w./-]+?)(?:\\.git)?$/) ??\n remote.match(/^[\\w+.-]+:\\/\\/(?:[^@]+@)?([\\w.-]+)\\/([\\w./-]+?)(?:\\.git)?$/);\n if (!match) throw new Error(`cannot derive repo key from remote \"${remote}\"`);\n return `${match[1]}/${match[2]}`.toLowerCase();\n}\n\nconst key = (b: { canvasPath: string; storyboardId: string }) =>\n `${b.canvasPath}#${b.storyboardId}`;\n\nconst BASELINE_WAIT_TIMEOUT_MS = 20 * 60_000;\nconst BASELINE_POLL_INTERVAL_MS = 5_000;\nconst BASELINE_HEARTBEAT_INTERVAL_MS = 30_000;\nconst BASELINE_MIN_RETRY_MS = 500;\n\nasync function detectEnvFingerprint(): Promise<string> {\n const { chromium } = await import(\"playwright\");\n const browser = await chromium.launch();\n try {\n return buildEnvFingerprint({\n os: process.platform,\n browserVersion: browser.version(),\n }).fingerprint;\n } finally {\n await browser.close().catch(() => {});\n }\n}\n\nexport async function resolveOrClaimBaseline(options: {\n ingest: VisualReviewIngestClient;\n repoKey: string;\n commitSha: string;\n envFingerprint: string;\n ownerToken: string;\n log: (line: string) => void;\n waitTimeoutMs?: number;\n pollIntervalMs?: number;\n now?: () => number;\n sleep?: (ms: number) => Promise<void>;\n}): Promise<BaselineClaimResult> {\n const now = options.now ?? Date.now;\n const deadline = now() + (options.waitTimeoutMs ?? BASELINE_WAIT_TIMEOUT_MS);\n for (;;) {\n const result = await options.ingest.claimBaseline({\n repoKey: options.repoKey,\n commitSha: options.commitSha,\n envFingerprint: options.envFingerprint,\n ownerToken: options.ownerToken,\n });\n if (result.status !== \"capturing\") return result;\n if (now() >= deadline) {\n throw new Error(\n `timed out waiting for baseline ${result.commitSha.slice(0, 12)}`,\n );\n }\n options.log(\n `baseline ${result.commitSha.slice(0, 12)} is being captured; waiting`,\n );\n const untilLeaseExpiry = Math.max(\n BASELINE_MIN_RETRY_MS,\n result.leaseExpiresAt - now() + BASELINE_MIN_RETRY_MS,\n );\n await (\n options.sleep ??\n ((ms) => new Promise((resolve) => setTimeout(resolve, ms)))\n )(\n Math.min(\n options.pollIntervalMs ?? BASELINE_POLL_INTERVAL_MS,\n untilLeaseExpiry,\n ),\n );\n }\n}\n\nfunction startClaimHeartbeat(options: {\n ingest: VisualReviewIngestClient;\n claim: BaselineClaimIdentity;\n leaseExpiresAt: number;\n log: (line: string) => void;\n}): { stop: () => void; assertOwned: () => void } {\n let renewing = false;\n let stopped = false;\n let ownershipError: Error | null = null;\n let leaseExpiresAt = options.leaseExpiresAt;\n let timer: ReturnType<typeof setTimeout> | null = null;\n const schedule = () => {\n if (stopped || ownershipError) return;\n const remaining = leaseExpiresAt - Date.now();\n const delay = Math.max(\n BASELINE_MIN_RETRY_MS,\n Math.min(BASELINE_HEARTBEAT_INTERVAL_MS, Math.floor(remaining / 3)),\n );\n timer = setTimeout(renew, delay);\n timer.unref();\n };\n const renew = () => {\n if (renewing || ownershipError) return;\n renewing = true;\n void options.ingest\n .renewBaselineClaim(options.claim)\n .then((result) => {\n leaseExpiresAt = result.leaseExpiresAt;\n })\n .catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n if (error instanceof VisualReviewHttpError && error.status === 409) {\n ownershipError = new Error(\n `lost baseline claim for ${options.claim.commitSha.slice(0, 12)}`,\n );\n } else if (Date.now() >= leaseExpiresAt) {\n ownershipError = new Error(\n `baseline claim expired for ${options.claim.commitSha.slice(0, 12)} after heartbeat failures`,\n );\n } else {\n options.log(\n `baseline claim heartbeat failed transiently: ${message}`,\n );\n }\n })\n .finally(() => {\n renewing = false;\n schedule();\n });\n };\n schedule();\n return {\n stop: () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n },\n assertOwned: () => {\n if (ownershipError) throw ownershipError;\n },\n };\n}\n\n/**\n * Argv for the on-demand baseline child, which re-enters THIS executable.\n *\n * The leading `capture` token is unconditional. Under npx `process.argv[1]`\n * is the `node_modules/.bin/tempo-visual-review` symlink (node never\n * realpaths argv[1]), so the entry's basename cannot say whether the\n * dispatcher or the bare capture module is running. `parseArgs` accepts the\n * token as a no-op, so both entries take the same argv.\n */\n/**\n * The parent's explicit `--tempo-dir` as a path INSIDE its repo root, for\n * re-rooting in the baseline worktree; undefined when the host was\n * discovered (the child discovers its own commit's layout) or when the\n * explicit dir lies outside the repo (nothing in the worktree corresponds\n * to it — the child falls back to discovery rather than joining `..` onto\n * a temporary checkout).\n */\nexport function explicitTempoDirRelative(args: {\n repoRoot: string;\n tempoDir: string;\n tempoDirExplicit: boolean;\n}): string | undefined {\n if (!args.tempoDirExplicit) return undefined;\n const relative = path.relative(args.repoRoot, args.tempoDir);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) return undefined;\n return relative;\n}\n\nexport function buildBaselineCaptureArgs(input: {\n worktreeRoot: string;\n commitSha: string;\n parallel: number;\n claimToken: string;\n envFingerprint: string;\n url?: string;\n githubRepo?: string;\n repoKey?: string;\n settleTimeoutMs?: number;\n /** The parent's EXPLICIT `--tempo-dir`, relative to its repo root, so the\n * baseline worktree captures the same host. Omitted when the parent\n * discovered the host itself — the child discovers its own commit's. */\n tempoDirRelative?: string;\n}): string[] {\n const args = [\n \"capture\",\n \"--repo-root\",\n input.worktreeRoot,\n \"--upload\",\n \"--trigger\",\n \"main\",\n \"--head\",\n input.commitSha,\n \"--branch\",\n `on-demand/${input.commitSha.slice(0, 12)}`,\n \"--parallel\",\n String(input.parallel),\n \"--baseline-claim-token\",\n input.claimToken,\n \"--expected-env-fingerprint\",\n input.envFingerprint,\n ];\n if (input.url) args.push(\"--url\", input.url);\n if (input.githubRepo) args.push(\"--github-repo\", input.githubRepo);\n if (input.tempoDirRelative !== undefined) {\n args.push(\n \"--tempo-dir\",\n path.join(input.worktreeRoot, input.tempoDirRelative || \".\"),\n );\n }\n if (input.repoKey) args.push(\"--repo-key\", input.repoKey);\n if (input.settleTimeoutMs !== undefined) {\n args.push(\"--settle-timeout\", String(input.settleTimeoutMs));\n }\n return args;\n}\n\nasync function runBaselineCaptureInWorktree(options: {\n args: CliArgs;\n commitSha: string;\n envFingerprint: string;\n claimToken: string;\n log: (line: string) => void;\n}): Promise<void> {\n const tempParent = await mkdtemp(\n path.join(process.env.RUNNER_TEMP || tmpdir(), \"tempo-vrt-baseline-\"),\n );\n const worktreeRoot = path.join(tempParent, \"repo\");\n try {\n options.log(\n `capturing missing baseline ${options.commitSha.slice(0, 12)} on demand`,\n );\n execFileSync(\n \"git\",\n [\"worktree\", \"add\", \"--detach\", worktreeRoot, options.commitSha],\n { cwd: options.args.repoRoot, stdio: \"inherit\", windowsHide: true },\n );\n const entry = process.argv[1];\n if (!entry)\n throw new Error(\"cannot locate the visual-review CLI entrypoint\");\n const childArgs = [\n entry,\n ...buildBaselineCaptureArgs({\n worktreeRoot,\n commitSha: options.commitSha,\n parallel: options.args.parallel,\n claimToken: options.claimToken,\n envFingerprint: options.envFingerprint,\n url: options.args.url,\n githubRepo: options.args.githubRepo,\n repoKey: options.args.repoKey,\n settleTimeoutMs: options.args.settleTimeoutMs,\n tempoDirRelative: explicitTempoDirRelative(options.args),\n }),\n ];\n await new Promise<void>((resolve, reject) => {\n const child = spawn(process.execPath, childArgs, {\n cwd: process.cwd(),\n env: { ...process.env, GITHUB_OUTPUT: \"\" },\n stdio: \"inherit\",\n windowsHide: true,\n });\n child.once(\"error\", reject);\n child.once(\"exit\", (code, signal) => {\n if (code === 0) resolve();\n else {\n reject(\n new Error(\n `baseline capture exited ${code ?? `from signal ${signal ?? \"unknown\"}`}`,\n ),\n );\n }\n });\n });\n } finally {\n try {\n execFileSync(\"git\", [\"worktree\", \"remove\", \"--force\", worktreeRoot], {\n cwd: options.args.repoRoot,\n stdio: \"ignore\",\n windowsHide: true,\n });\n } catch {\n // The worktree may not have been created; the temp-directory cleanup is\n // still safe and git prunes stale registrations on its next maintenance.\n }\n await rm(tempParent, { recursive: true, force: true });\n }\n}\n\nexport async function main(argv = process.argv.slice(2)): Promise<number> {\n const args = parseArgs(argv);\n inferPrBase(args);\n const timing = new TimingReport();\n const log = (line: string) => console.log(`[visual-review] ${line}`);\n\n const headSha = args.headSha ?? git(args.repoRoot, \"rev-parse\", \"HEAD\");\n // `--show-current` prints an EMPTY string on a detached HEAD (every CI\n // checkout) — `||`, not `??`.\n const branch =\n args.branch || git(args.repoRoot, \"branch\", \"--show-current\") || \"detached\";\n // Nearest-first ancestor chain from the merge-base: the finalize mutation\n // walks it to find the nearest captured baseline (§3.4).\n const baseCandidates = args.baseSha\n ? git(\n args.repoRoot,\n \"rev-list\",\n \"--first-parent\",\n `--max-count=${args.baseDepth}`,\n args.baseSha,\n )\n .split(\"\\n\")\n .filter(Boolean)\n : [];\n if (args.trigger === \"pr\" && baseCandidates.length === 0) {\n throw new Error(\"--trigger pr requires --base <merge-base sha>\");\n }\n\n // The canvas host as the Tempo app resolves it: canonical `tempo/`, a\n // legacy root-level tempo.config.json, and `paths.canvases` — the same\n // resolution the setup doctor uses, so \"tracked\" is one set everywhere.\n const layout = await resolveCanvasHostLayout({\n repoRoot: args.repoRoot,\n tempoDir: args.tempoDirExplicit ? args.tempoDir : undefined,\n });\n args.tempoDir = layout.tempoDir;\n const pagesDir = layout.pagesDir;\n const enumeration = await timing.phase(\"enumerate\", () =>\n enumerateTrackedStoryboards({ tempoDir: args.tempoDir, pagesDir }),\n );\n if (enumeration.errors.length > 0) {\n for (const error of enumeration.errors) {\n console.error(\n `[visual-review] parse error: ${error.filePath}: ${error.message}`,\n );\n }\n // AST truth is the manifest — a structurally unreadable canvas would\n // silently shrink the tracked set, so it fails the run instead.\n return 1;\n }\n log(\n `${enumeration.tracked.length} tracked storyboard(s) of ${enumeration.totalStoryboards} across ${enumeration.totalCanvases} canvases` +\n (enumeration.paused.length > 0\n ? ` (${enumeration.paused.length} paused via skip)`\n : \"\"),\n );\n\n const ingest =\n args.upload &&\n new VisualReviewIngestClient(\n args.url ??\n process.env.VISUAL_REVIEW_INGEST_URL ??\n DEFAULT_VISUAL_REVIEW_INGEST_URL,\n resolveIngestCredential(),\n );\n\n const repoKey = deriveRepoKey(args);\n let envFingerprint = \"\";\n let ownedClaim: BaselineClaimIdentity | null = null;\n let ownedBaselineClaim: BaselineClaimIdentity | null = null;\n let ownedClaimLeaseExpiresAt = 0;\n let heartbeat: ReturnType<typeof startClaimHeartbeat> | null = null;\n let reusedCandidate: Pick<RunManifest, \"expected\" | \"snapshots\"> | null =\n null;\n const announcedOwnerToken = process.env.VISUAL_REVIEW_CAPTURE_OWNER_TOKEN;\n let terminating = false;\n\n const releaseCurrentClaims = async (reason: string) => {\n if (!ingest) return;\n const claims: Array<\n | BaselineClaimIdentity\n | Omit<BaselineClaimIdentity, \"envFingerprint\">\n | null\n > = [ownedClaim, ownedBaselineClaim];\n if (\n announcedOwnerToken &&\n !claims.some(\n (claim) =>\n claim?.commitSha === headSha &&\n claim.ownerToken === announcedOwnerToken,\n )\n ) {\n claims.push({\n repoKey,\n commitSha: headSha,\n ownerToken: announcedOwnerToken,\n });\n }\n await Promise.all(\n claims\n .filter((claim) => claim !== null)\n .map(async (claim) => {\n await ingest\n .failBaselineClaim({ ...claim, error: reason })\n .catch((releaseError) => {\n log(\n `failed to release baseline claim for ${claim.commitSha.slice(0, 12)}: ${releaseError instanceof Error ? releaseError.message : releaseError}`,\n );\n });\n }),\n );\n };\n const terminate = (signal: \"SIGINT\" | \"SIGTERM\") => {\n if (terminating) return;\n terminating = true;\n heartbeat?.stop();\n void releaseCurrentClaims(`capture interrupted by ${signal}`).finally(() =>\n process.exit(signal === \"SIGTERM\" ? 143 : 130),\n );\n };\n const onSigint = () => terminate(\"SIGINT\");\n const onSigterm = () => terminate(\"SIGTERM\");\n if (ingest) {\n process.once(\"SIGINT\", onSigint);\n process.once(\"SIGTERM\", onSigterm);\n }\n\n try {\n if (ingest && (args.trigger === \"pr\" || args.trigger === \"main\")) {\n envFingerprint = await timing.phase(\"environment-fingerprint\", () =>\n detectEnvFingerprint(),\n );\n if (\n args.expectedEnvFingerprint &&\n args.expectedEnvFingerprint !== envFingerprint\n ) {\n throw new Error(\n `baseline capture environment changed: expected ${args.expectedEnvFingerprint}, got ${envFingerprint}`,\n );\n }\n\n const ownerToken =\n args.baselineClaimToken ??\n process.env.VISUAL_REVIEW_CAPTURE_OWNER_TOKEN ??\n randomUUID();\n if (args.trigger === \"main\") {\n const baseline = await resolveOrClaimBaseline({\n ingest,\n repoKey,\n commitSha: headSha,\n envFingerprint,\n ownerToken,\n log,\n });\n if (baseline.status === \"ready\") {\n log(`baseline ${baseline.commitSha.slice(0, 12)} is already ready`);\n return 0;\n }\n if (baseline.status === \"claimed\") {\n ownedClaim = {\n repoKey,\n commitSha: baseline.commitSha,\n envFingerprint,\n ownerToken,\n };\n ownedClaimLeaseExpiresAt = baseline.leaseExpiresAt;\n } else {\n log(\n `baseline claims unavailable${baseline.status === \"unsupported\" && baseline.reason ? ` (${baseline.reason})` : \"\"}; continuing without a lease`,\n );\n }\n } else {\n // A PR run's head is intrinsically reusable by a downstream/stacked\n // PR. Announce that production before rendering so downstream runs\n // wait instead of generating the same baseline themselves.\n let headClaim = await ingest.claimBaseline({\n repoKey,\n commitSha: headSha,\n envFingerprint,\n ownerToken,\n });\n if (headClaim.status === \"capturing\") {\n headClaim = await resolveOrClaimBaseline({\n ingest,\n repoKey,\n commitSha: headSha,\n envFingerprint,\n ownerToken,\n log,\n });\n }\n if (headClaim.status === \"claimed\") {\n ownedClaim = {\n repoKey,\n commitSha: headSha,\n envFingerprint,\n ownerToken,\n };\n ownedClaimLeaseExpiresAt = headClaim.leaseExpiresAt;\n } else if (headClaim.status === \"ready\") {\n log(`reusing existing capture for PR head ${headSha.slice(0, 12)}`);\n reusedCandidate = await ingest.baselineManifest(\n repoKey,\n headSha,\n envFingerprint,\n );\n } else {\n log(\n `baseline claims unavailable${headClaim.status === \"unsupported\" && headClaim.reason ? ` (${headClaim.reason})` : \"\"}; using legacy PR capture`,\n );\n }\n }\n\n if (ownedClaim) {\n heartbeat = startClaimHeartbeat({\n ingest,\n claim: ownedClaim,\n leaseExpiresAt: ownedClaimLeaseExpiresAt,\n log,\n });\n }\n\n if (args.trigger === \"pr\") {\n const baseCommit = baseCandidates[0];\n if (!baseCommit) {\n throw new Error(\"--trigger pr requires --base <merge-base sha>\");\n }\n const baseline = await resolveOrClaimBaseline({\n ingest,\n repoKey,\n commitSha: baseCommit,\n envFingerprint,\n ownerToken,\n log,\n });\n if (baseline.status === \"claimed\") {\n ownedBaselineClaim = {\n repoKey,\n commitSha: baseline.commitSha,\n envFingerprint,\n ownerToken,\n };\n try {\n await runBaselineCaptureInWorktree({\n args,\n commitSha: baseline.commitSha,\n envFingerprint,\n claimToken: ownerToken,\n log,\n });\n } catch (error) {\n // The child normally releases its own lease. This also covers a\n // failure before it starts (for example, worktree creation), so\n // another PR can retry immediately instead of waiting for expiry.\n await ingest\n .failBaselineClaim({\n ...ownedBaselineClaim,\n error: error instanceof Error ? error.message : String(error),\n })\n .catch(() => {});\n ownedBaselineClaim = null;\n throw error;\n }\n ownedBaselineClaim = null;\n if (ownedClaim?.commitSha === baseline.commitSha) {\n heartbeat?.stop();\n heartbeat = null;\n ownedClaim = null;\n }\n } else if (baseline.status === \"ready\") {\n log(`using baseline ${baseline.commitSha.slice(0, 12)}`);\n } else {\n log(\n `on-demand baselines unavailable${baseline.status === \"unsupported\" && baseline.reason ? ` (${baseline.reason})` : \"\"}; using nearest stored baseline`,\n );\n }\n }\n }\n\n let captured: CapturedStoryboard[] = [];\n // The fingerprint must be real even when NOTHING is tracked: a PR that\n // removes/untracks every storyboard still has to find the baseline\n // manifest under the same fingerprint so every board reports `removed`\n // and the gate blocks. An empty fingerprint would read as a fresh epoch —\n // i.e. deleting all tracked storyboards would sail through green.\n if (ingest && enumeration.tracked.length === 0 && !envFingerprint) {\n envFingerprint = await detectEnvFingerprint();\n }\n\n if (enumeration.tracked.length > 0 && !reusedCandidate) {\n const devserver = await timing.phase(\"devserver-boot\", () =>\n startDevserverSession({\n tempoDir: args.tempoDir,\n hostCanvasPaths: [\n ...new Set(enumeration.tracked.map((b) => b.hostCanvasPath)),\n ],\n log,\n // Only the lines that explain a board failing mid-capture: dep\n // optimizer waves (each ends in a full-reload of every page), the\n // scan failure that disables pre-bundling, HMR/recovery reloads.\n onOutput: (line) => {\n const signal = formatDevserverReloadSignal(line);\n if (signal) log(signal);\n },\n }),\n );\n try {\n const session = await startCaptureSession({\n hostUrl: devserver.hostUrl,\n parallelPages: args.parallel,\n timing,\n settleTimeoutMs: args.settleTimeoutMs,\n log,\n });\n try {\n if (envFingerprint && session.envFingerprint !== envFingerprint) {\n throw new Error(\n `capture environment changed after claiming baseline: expected ${envFingerprint}, got ${session.envFingerprint}`,\n );\n }\n envFingerprint = session.envFingerprint;\n captured = await timing.phase(\"capture\", () =>\n session.captureBoards(enumeration.tracked),\n );\n } finally {\n await session.dispose();\n }\n } finally {\n await timing.phase(\"devserver-shutdown\", () => devserver.dispose());\n }\n }\n\n const manifest: RunManifest = {\n repoKey,\n githubRepo: args.githubRepo,\n headSha,\n branch,\n trigger: args.trigger,\n prNumber: args.prNumber,\n baseCandidates,\n baselineClaimToken: ownedClaim?.ownerToken ?? args.baselineClaimToken,\n envFingerprint,\n expected: reusedCandidate?.expected ?? [\n ...enumeration.tracked.map((board) => ({\n canvasPath: board.canvasPath,\n storyboardId: board.storyboardId,\n name: board.name,\n ...board.layout,\n threshold: board.threshold,\n ignore: scaleIgnoreRegionsToDevicePx(board.ignore),\n })),\n // Paused boards ride the manifest so the server knows they still\n // exist — pausing must not read as deletion (a blocking `removed`).\n ...enumeration.paused.map((board) => ({\n canvasPath: board.canvasPath,\n storyboardId: board.storyboardId,\n name: board.name,\n ...board.layout,\n skip: true,\n })),\n ],\n snapshots:\n reusedCandidate?.snapshots ??\n captured.map((result) => ({\n canvasPath: result.canvasPath,\n storyboardId: result.storyboardId,\n pngHash: result.pngHash,\n htmlHash: result.htmlHash,\n status: result.status,\n error: result.error,\n })),\n };\n\n const renderErrors = captured.filter((r) => r.status !== \"ok\");\n if (renderErrors.length > 0) {\n for (const result of renderErrors) {\n log(\n `⚠ ${key(result)}: ${result.status}${result.error ? ` — ${result.error}` : \"\"}`,\n );\n }\n }\n\n if (args.out) {\n const out = args.out;\n await timing.phase(\"write-out\", async () => {\n await mkdir(out, { recursive: true });\n for (const result of captured) {\n if (!result.pngBytes) continue;\n const fileName = `${key(result).replace(/[^\\w.-]+/g, \"_\")}.png`;\n await writeFile(path.join(out, fileName), result.pngBytes);\n }\n await writeFile(\n path.join(out, \"manifest.json\"),\n JSON.stringify(manifest, null, 2),\n );\n });\n }\n\n let finalizeResult: Awaited<\n ReturnType<VisualReviewIngestClient[\"finalize\"]>\n > | null = null;\n if (ingest) {\n heartbeat?.assertOwned();\n // Hash-negotiated delta upload: an unchanged storyboard across 1,000\n // commits crosses the wire zero times.\n const blobsByHash = new Map<\n string,\n { bytes: Uint8Array; width: number; height: number }\n >();\n for (const result of captured) {\n const board = enumeration.tracked.find((b) => key(b) === key(result));\n if (result.pngHash && result.pngBytes) {\n blobsByHash.set(result.pngHash, {\n bytes: result.pngBytes,\n width: Math.round((board?.layout.width ?? 0) * 2),\n height: Math.round((board?.layout.height ?? 0) * 2),\n });\n }\n if (result.htmlHash && result.htmlBytes) {\n // Dimensions 0 mark an HTML blob in the shared content store.\n blobsByHash.set(result.htmlHash, {\n bytes: result.htmlBytes,\n width: 0,\n height: 0,\n });\n }\n }\n const missing = await timing.phase(\"negotiate\", () =>\n ingest.negotiate([...blobsByHash.keys()]),\n );\n log(`uploading ${missing.length} of ${blobsByHash.size} blob(s)`);\n await timing.phase(\"upload\", async () => {\n for (const hash of missing) {\n const blob = blobsByHash.get(hash);\n if (!blob) throw new Error(`negotiate returned unknown hash ${hash}`);\n await ingest.uploadBlob(hash, blob.bytes, blob);\n }\n });\n finalizeResult = await timing.phase(\"finalize\", () =>\n ingest.finalize(manifest),\n );\n log(\n `run ${finalizeResult.state}${finalizeResult.deduped ? \" (deduped)\" : \"\"}`,\n );\n log(`report: ${finalizeResult.reportUrl}`);\n log(`status: ${finalizeResult.statusUrl}`);\n if (args.out) {\n await writeFile(\n path.join(args.out, \"run.json\"),\n JSON.stringify(finalizeResult, null, 2),\n );\n }\n // The workflow's follow-up job reads these outputs.\n if (process.env.GITHUB_OUTPUT) {\n await writeFile(\n process.env.GITHUB_OUTPUT,\n `report_key=${finalizeResult.reportKey}\\nreport_url=${finalizeResult.reportUrl}\\nstatus_url=${finalizeResult.statusUrl}\\n`,\n { flag: \"a\" },\n );\n }\n }\n\n console.log(timing.format());\n return 0;\n } catch (error) {\n await releaseCurrentClaims(\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n } finally {\n heartbeat?.stop();\n process.removeListener(\"SIGINT\", onSigint);\n process.removeListener(\"SIGTERM\", onSigterm);\n }\n}\n\nconst isDirectRun =\n process.argv[1] !== undefined &&\n (process.argv[1].endsWith(\"cli.ts\") || process.argv[1].endsWith(\"cli.js\"));\nif (isDirectRun) {\n // Hard-exit rather than letting the event loop drain: the devserver\n // supervisor leaves live handles behind (child process plumbing, file\n // watchers) that would keep the process alive until the CI job timeout.\n // Everything durable (manifest, uploads, GITHUB_OUTPUT) is awaited inside\n // main() before this runs.\n main()\n .then((code) => {\n process.exit(code);\n })\n .catch((error) => {\n console.error(\n `[visual-review] ${error instanceof Error ? error.message : error}`,\n );\n process.exit(1);\n });\n}\n","import { createHash } from \"node:crypto\";\n\nimport {\n buildCanvasStoryboardRenderId,\n buildDirectRenderIdleUrl,\n buildDirectRenderUrl as buildSharedDirectRenderUrl,\n buildStoryboardRenderSelector,\n} from \"@modules/component-render-pool/direct-render\";\nimport type { Browser, BrowserContext, Page, Request } from \"playwright\";\nimport { PNG } from \"pngjs\";\n\nimport {\n buildFreezeInitScript,\n CHROMIUM_LAUNCH_ARGS,\n FREEZE_MOTION_CSS,\n settlePage,\n} from \"./determinism\";\nimport { isNavigationInterruptedError } from \"./navigation-errors\";\nimport {\n autoInstallDisabled,\n installChromium,\n isMissingBrowserError,\n} from \"./ensure-chromium\";\nimport { buildEnvFingerprint, DEVICE_SCALE_FACTOR } from \"./fingerprint\";\nimport { buildStoryboardHtmlDocument } from \"./html-document\";\nimport {\n assessPageStylesInPage,\n extractStoryboardDomInPage,\n inlinePortableHtmlInPage,\n} from \"./page-scripts\";\nimport {\n installRenderProfileInPage,\n readRenderProfileInPage,\n} from \"./render-profile\";\nimport type {\n CaptureBoard,\n CapturedStoryboard,\n CaptureStatus,\n CaptureTimingSink,\n StoryboardArtifacts,\n StoryboardCaptureOptions,\n StoryboardCaptureTarget,\n StoryboardHtmlCapture,\n StoryboardPngCapture,\n} from \"./types\";\n\n/**\n * The headless capture engine (TEM-1775 §3.2, extracted for TEM-2079):\n * drives N parallel Playwright pages through the canvas host's\n * DIRECT-RENDER URL contract:\n *\n * /tempo-host?canvasPath=<module key>&storyboardId=<id>&width=&height=\n *\n * which mounts exactly one storyboard at (0,0) at its declared size — no\n * canvas zoom, no sibling storyboards, no pan state. TEM-1109's serial\n * constraint is an Electron-frame-pool artifact; independent pages don't\n * share a host window, so pages parallelize freely.\n *\n * The contract itself — URL grammar, render ids, the render-root selector,\n * the idle hop — lives in `@modules/component-render-pool/direct-render`,\n * shared with the Tempo app's in-app capture lane, which drives the same\n * host over an offscreen frame instead of a Playwright page.\n */\n\n/** Mirrors `getCanvasStoryboardRenderId` in tempo-sdk/canvas/canvas.tsx —\n * kept out of tempo-sdk because importing that module drags the React\n * runtime into this Node library. The storyboard-smoke e2e pins the two\n * stay in sync. */\nexport function storyboardRenderId(\n hostCanvasPath: string,\n storyboardId: string,\n): string {\n return buildCanvasStoryboardRenderId(hostCanvasPath, storyboardId);\n}\n\n/** The direct-render URL for one storyboard at a given CSS-px size. */\nexport function buildDirectRenderUrl(\n hostUrl: string,\n target: StoryboardCaptureTarget,\n): string {\n return buildSharedDirectRenderUrl(hostUrl, {\n canvasPath: target.hostCanvasPath,\n storyboardId: target.storyboardId,\n width: target.width,\n height: target.height,\n });\n}\n\nexport function sha256Hex(bytes: Uint8Array): string {\n return createHash(\"sha256\").update(bytes).digest(\"hex\");\n}\n\n/** A capture that painted nothing: fully transparent or fully white. A\n * legitimately solid storyboard in any other color still passes. */\nexport function looksBlank(pngBytes: Uint8Array): boolean {\n let png: PNG;\n try {\n png = PNG.sync.read(Buffer.from(pngBytes));\n } catch {\n return true;\n }\n for (let index = 0; index < png.data.length; index += 4) {\n const alpha = png.data[index + 3];\n if (alpha === 0) continue;\n if (\n png.data[index] !== 255 ||\n png.data[index + 1] !== 255 ||\n png.data[index + 2] !== 255\n ) {\n return false;\n }\n }\n return true;\n}\n\nexport { isNavigationInterruptedError } from \"./navigation-errors\";\n\n/** The page or its context is gone (crash, close) while the browser may\n * still be alive: the worker cannot be reused, but the board can be\n * retried on a fresh context. */\nexport function isPageGoneError(message: string): boolean {\n return /Target closed|Target page, context or browser has been closed|Page closed|Target crashed/.test(\n message,\n );\n}\n\n/** The browser process itself is gone. Nothing in this session can recover\n * — a fresh context would fail the same way — so the failure is reported\n * at once instead of being retried. */\nexport function isBrowserGoneError(message: string): boolean {\n // Capitalized on purpose: Playwright's page-level \"Target page, context or\n // browser has been closed\" must keep classifying as page-gone.\n return /Browser has been closed|Browser closed|browser has disconnected|Browser process crashed/.test(\n message,\n );\n}\n\n/**\n * Errors thrown mid-board are remembered here when the document was\n * replaced during that board, so the thrower's caller learns about the\n * reload even when the message does not say so (a settle timeout under a\n * reload loop reads \"Timeout exceeded\"). A WeakSet, not a property: thrown\n * values may be frozen, and mutating one would mask the original failure.\n */\nconst navigatedErrors = new WeakSet<object>();\n\nfunction markNavigated(error: unknown, navigated: boolean): void {\n if (navigated && error !== null && typeof error === \"object\") {\n navigatedErrors.add(error);\n }\n}\n\nfunction readNavigatedMark(error: unknown): boolean {\n return (\n error !== null && typeof error === \"object\" && navigatedErrors.has(error)\n );\n}\n\n/**\n * What the engine has observed on a worker's main frame. Compared before\n * and after a board to decide whether the document was replaced under it.\n */\nexport type DocumentCounters = {\n /** Cross-document navigations STARTED: main-frame navigation requests,\n * redirect hops folded into the request they continue. */\n navigationRequests: number;\n /** Main-frame cross-document commits from CDP's Page.frameNavigated.\n * History updates have their own navigatedWithinDocument event;\n * DOMContentLoaded would come too late when a script stalls. */\n documentCommits: number;\n};\n\n/**\n * Whether the document was replaced between two counter snapshots, given\n * how many cross-document navigations the board itself was supposed to\n * cause (1 for a worker's initial goto, 0 for an in-place switch). Pure so\n * the boundary cases can be pinned: a reload whose REQUEST predates the\n * snapshot but whose commit lands during the board shows up only on the\n * frame side; a reload that interrupts the initial goto before it commits\n * shows up only on the request side.\n */\nexport function documentReplaced(\n start: DocumentCounters,\n now: DocumentCounters,\n expectedNavigations: number,\n): boolean {\n return (\n now.navigationRequests > start.navigationRequests + expectedNavigations ||\n now.documentCommits > start.documentCommits + expectedNavigations\n );\n}\n\n/** How many times one board may be attempted in a bulk capture. Reloads come\n * in short bursts (each optimizer wave re-bundles and reloads every page),\n * so a single retry can land inside the next wave; three attempts covers\n * a burst without letting a genuinely broken board stall the run. */\nexport const BOARD_ATTEMPT_LIMIT = 3;\n\nexport type BoardAttempt = {\n status: CaptureStatus;\n error?: string;\n /** The main-frame document was replaced (a reload) between the start of\n * the board and the end of its screenshot. */\n navigated: boolean;\n};\n\nexport type BoardRetryDecision = \"accept\" | \"retry\" | \"retry-fresh-worker\";\n\n/** The bulk-capture retry policy, pure so it can be pinned by tests. Only\n * reload-shaped outcomes retry: a deterministic render failure (a\n * storyboard that throws, a target that never attaches) is accepted at\n * once so it costs one settle timeout, not three. */\nexport function decideBoardRetry(\n attempt: BoardAttempt,\n attemptIndex: number,\n attemptLimit: number = BOARD_ATTEMPT_LIMIT,\n): BoardRetryDecision {\n if (attemptIndex >= attemptLimit - 1) return \"accept\";\n if (attempt.status === \"render-error\") {\n const message = attempt.error ?? \"\";\n if (isBrowserGoneError(message)) return \"accept\";\n if (isPageGoneError(message)) return \"retry-fresh-worker\";\n if (attempt.navigated || isNavigationInterruptedError(message)) {\n return \"retry\";\n }\n return \"accept\";\n }\n // `ok` / `empty` / `skipped`: only a capture the reload raced is\n // suspect — its settle window may have watched the pre-reload document\n // and its pixels the post-reload one.\n return attempt.navigated ? \"retry\" : \"accept\";\n}\n\n/** The error text for a capture that crossed documents on its last\n * permitted attempt: never published, because its settle evidence and its\n * pixels may describe different documents. */\nexport function navigatedRejectionError(attempts: number): string {\n return `document reloaded during every attempt (${attempts}); the capture never saw one stable document`;\n}\n\n/** Turns an accepted attempt into the board's published result: a capture\n * that still crossed documents becomes a render-error without bytes; a\n * retried failure keeps its attempt count in the message. */\nexport function finalizeAcceptedAttempt(\n attempt: BoardAttempt & { result: CapturedStoryboard },\n attempts: number,\n): CapturedStoryboard {\n const { result } = attempt;\n if (attempt.navigated && result.status !== \"render-error\") {\n return {\n canvasPath: result.canvasPath,\n storyboardId: result.storyboardId,\n status: \"render-error\",\n error: navigatedRejectionError(attempts),\n timings: result.timings,\n };\n }\n if (result.status === \"render-error\" && attempts > 1) {\n return { ...result, error: `after ${attempts} attempts: ${result.error}` };\n }\n return result;\n}\n\n/**\n * Reload-storm detection. Healthy optimizer bursts are wave-shaped: one wave\n * costs at most one re-capture per worker, then boards succeed again. A\n * devserver that never stops reloading (a re-optimize loop, a restart loop)\n * makes EVERY board need re-captures instead; once the last `window`\n * completed boards all did, the run is aborted with an infrastructure\n * error rather than spending three settle timeouts per remaining board on\n * results that would all be render-errors anyway.\n */\nexport function reloadStormWindowFor(parallelPages: number): number {\n return Math.max(8, 4 * Math.max(1, Math.floor(parallelPages)));\n}\n\n/** `recent` is oldest-first; a storm needs a FULL window of boards that\n * each needed at least one reload re-capture. */\nexport function isReloadStorm(\n recent: readonly boolean[],\n window: number,\n): boolean {\n if (recent.length < window) return false;\n return recent.slice(-window).every(Boolean);\n}\n\nexport class CaptureReloadStormError extends Error {\n override readonly name = \"CaptureReloadStormError\";\n}\n\ntype RetryReason = \"document reloaded mid-capture\" | \"page closed mid-capture\";\n\nfunction describeRetryReason(attempt: BoardAttempt): RetryReason {\n if (\n attempt.status === \"render-error\" &&\n isPageGoneError(attempt.error ?? \"\")\n ) {\n return \"page closed mid-capture\";\n }\n return \"document reloaded mid-capture\";\n}\n\n// Bulk workers start idle. Each target sets its viewport before mounting.\nconst INITIAL_VIEWPORT = { width: 1600, height: 1200 };\n\nexport type StoryboardArtifactRequest = StoryboardCaptureTarget &\n StoryboardCaptureOptions & {\n /** Which artifacts to produce in the single render pass. Capturing\n * both rides one navigation + settle, so the HTML is serialized off\n * the same render the PNG shows. Default `[\"png\"]`. */\n artifacts?: Array<\"png\" | \"html\">;\n /** Inline devserver-only refs (stylesheets, images, fonts) into the\n * HTML document so it survives the devserver — the share-snapshot\n * contract. Default true. */\n portableHtml?: boolean;\n };\n\nexport type CaptureSession = {\n browserVersion: string;\n envFingerprint: string;\n /** Bulk capture with worker-pool parallelism and per-board error rows.\n * Never throws per board; throws `CaptureReloadStormError` when the\n * devserver reloads continuously (see `reloadStormWindowFor`) and a\n * plain Error when the browser itself is gone. */\n captureBoards: (\n boards: CaptureBoard[],\n options?: { label?: string },\n ) => Promise<CapturedStoryboard[]>;\n /** Capture one storyboard's PNG. Throws on render failure. */\n captureStoryboard: (\n request: StoryboardCaptureTarget & StoryboardCaptureOptions,\n ) => Promise<StoryboardPngCapture>;\n /** Capture one storyboard's standalone HTML document. Throws on render\n * failure. */\n captureStoryboardHtml: (\n request: StoryboardCaptureTarget &\n StoryboardCaptureOptions & { portable?: boolean },\n ) => Promise<StoryboardHtmlCapture>;\n /** Capture PNG and/or HTML in one render pass. Throws on render failure. */\n captureStoryboardArtifacts: (\n request: StoryboardArtifactRequest,\n ) => Promise<StoryboardArtifacts>;\n dispose: () => Promise<void>;\n};\n\nexport async function startCaptureSession(options: {\n hostUrl: string;\n parallelPages?: number;\n timing?: CaptureTimingSink;\n settleTimeoutMs?: number;\n log?: (line: string) => void;\n}): Promise<CaptureSession> {\n const log = options.log ?? (() => {});\n const parallelPages = options.parallelPages ?? 1;\n // Lazy so importing the module never requires playwright (an optional\n // peer) — only actually starting a capture session does.\n const { chromium } = await import(\"playwright\");\n let browser: Browser;\n try {\n browser = await chromium.launch({ args: CHROMIUM_LAUNCH_ARGS });\n } catch (err) {\n // Missing browser binary → download it and retry once, so captures\n // work zero-setup in cloud sandboxes. Any other launch failure (and\n // any environment that opted out) surfaces unchanged.\n if (!isMissingBrowserError(err) || autoInstallDisabled()) throw err;\n log(\n \"[capture] Chromium is not installed — downloading it now (one-time, ~2 min)\",\n );\n await installChromium(log);\n browser = await chromium.launch({ args: CHROMIUM_LAUNCH_ARGS });\n }\n const { fingerprint } = buildEnvFingerprint({\n os: process.platform,\n browserVersion: browser.version(),\n });\n\n const browserGoneError = (cause: string): Error =>\n new Error(`capture browser is gone; cannot open a fresh page: ${cause}`);\n\n type CaptureWorker = {\n context: BrowserContext;\n page: Page;\n contextMs: number;\n contextClaimed: boolean;\n hasNavigated: boolean;\n currentSelector?: string;\n counters: DocumentCounters;\n /** Documents that reached DOMContentLoaded — the moment a reloaded\n * document has a <head> to re-arm the freeze CSS into. */\n documentsLoaded: number;\n /** Main-frame navigations requested but not yet loaded or failed. */\n navigationsInFlight: number;\n dispose(): Promise<void>;\n };\n\n const createCaptureWorker = async (\n capture: StoryboardCaptureOptions,\n ): Promise<CaptureWorker> => {\n const contextStart = Date.now();\n const context: BrowserContext = await browser.newContext({\n deviceScaleFactor: capture.deviceScaleFactor ?? DEVICE_SCALE_FACTOR,\n viewport: capture.viewport ?? INITIAL_VIEWPORT,\n reducedMotion: \"reduce\",\n colorScheme: \"light\",\n timezoneId: \"UTC\",\n locale: \"en-US\",\n serviceWorkers: \"block\",\n });\n await context.addInitScript(buildFreezeInitScript());\n await context.addInitScript(installRenderProfileInPage);\n const page = await context.newPage();\n const documentEvents = await context.newCDPSession(page);\n const worker: CaptureWorker = {\n context,\n page,\n contextMs: Date.now() - contextStart,\n contextClaimed: false,\n hasNavigated: false,\n counters: {\n navigationRequests: 0,\n documentCommits: 0,\n },\n documentsLoaded: 0,\n navigationsInFlight: 0,\n async dispose() {\n await documentEvents.detach().catch(() => {});\n await page.close().catch(() => {});\n await context.close().catch(() => {});\n },\n };\n const isMainFrameNavigation = (request: Request): boolean => {\n try {\n return (\n request.isNavigationRequest() && request.frame() === page.mainFrame()\n );\n } catch {\n // `request.frame()` throws for requests without a frame (workers);\n // none of those can replace the document.\n return false;\n }\n };\n page.on(\"request\", (request) => {\n if (!isMainFrameNavigation(request)) return;\n // A redirect hop continues the navigation it was redirected from —\n // one document replacement, however many hops.\n if (request.redirectedFrom()) return;\n worker.counters.navigationRequests += 1;\n worker.navigationsInFlight += 1;\n });\n page.on(\"requestfailed\", (request) => {\n if (!isMainFrameNavigation(request)) return;\n worker.navigationsInFlight = Math.max(0, worker.navigationsInFlight - 1);\n });\n // Playwright's framenavigated also fires for history.replaceState,\n // pushState, and hash changes made by app routers. Chromium's document\n // event excludes all of those without having to intercept app code or\n // guess which navigations the capture engine caused itself.\n documentEvents.on(\"Page.frameNavigated\", ({ frame }) => {\n if (!frame.parentId) worker.counters.documentCommits += 1;\n });\n await documentEvents.send(\"Page.enable\");\n page.on(\"domcontentloaded\", () => {\n worker.documentsLoaded += 1;\n // A loaded document ends every navigation that was racing for the\n // frame; any it superseded never gets its own document.\n worker.navigationsInFlight = 0;\n // The freeze CSS is a <style> tag in the FIRST document (added right\n // after the initial goto). A reload throws that document away, so\n // every later document re-arms it — otherwise the boards captured\n // after an unexpected reload render with live transitions and a\n // blinking caret.\n if (worker.hasNavigated) {\n page.addStyleTag({ content: FREEZE_MOTION_CSS }).catch(() => {});\n }\n });\n options.timing?.recordBoard(\"context\", worker.contextMs);\n return worker;\n };\n\n const sleep = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n /** After a reload raced a board: let every in-flight navigation land and\n * hold until no further navigation starts for a short quiet window —\n * optimizer reloads arrive in bursts, and re-capturing into the next\n * wave would just fail again. Bounded; never throws. */\n const awaitDocumentQuiet = async (\n worker: CaptureWorker,\n timeoutMs: number,\n ): Promise<void> => {\n const quietMs = 500;\n const deadline = Date.now() + Math.min(timeoutMs, 10_000);\n const settleLoad = async () => {\n // `waitForLoadState` answers for the CURRENT document, so a\n // navigation that has been requested but has not loaded yet is\n // invisible to it — drain those first.\n while (worker.navigationsInFlight > 0 && Date.now() < deadline) {\n await sleep(50);\n }\n await worker.page\n .waitForLoadState(\"load\", {\n timeout: Math.max(1, deadline - Date.now()),\n })\n .catch(() => {});\n };\n await settleLoad();\n let requests = worker.counters.navigationRequests;\n let quietSince = Date.now();\n while (Date.now() < deadline) {\n await sleep(100);\n if (\n worker.counters.navigationRequests !== requests ||\n worker.navigationsInFlight > 0\n ) {\n requests = worker.counters.navigationRequests;\n quietSince = Date.now();\n await settleLoad();\n } else if (Date.now() - quietSince >= quietMs) {\n return;\n }\n }\n };\n\n /** Bulk capture workers keep one document and React root alive. Between\n * storyboards they briefly clear the direct target, then update the URL and\n * dispatch popstate so CanvasHost switches the active storyboard in-place.\n * Standalone capture calls still own and dispose a one-shot worker.\n *\n * Resolves with `navigated: true` when the document was replaced between\n * the start of the board and the end of `work` — the caller decides\n * whether such a capture is trustworthy. */\n const withSettledStoryboard = async <T>(\n target: StoryboardCaptureTarget,\n capture: StoryboardCaptureOptions,\n /** Mutated as each phase completes, so a mid-flight failure still\n * reports the phases that did run. */\n timings: { contextMs: number; navigateMs: number; settleMs: number },\n work: (page: Page, selector: string) => Promise<T>,\n reusableWorker?: CaptureWorker,\n ): Promise<{ value: T; navigated: boolean }> => {\n const renderId = storyboardRenderId(\n target.hostCanvasPath,\n target.storyboardId,\n );\n const selector = buildStoryboardRenderSelector(renderId);\n const viewport = capture.viewport ?? {\n width: Math.ceil(target.width),\n height: Math.ceil(target.height),\n };\n const ownsWorker = reusableWorker === undefined;\n const worker =\n reusableWorker ?? (await createCaptureWorker({ ...capture, viewport }));\n const { page } = worker;\n if (!worker.contextClaimed) {\n timings.contextMs = worker.contextMs;\n worker.contextClaimed = true;\n }\n // Snapshotted BEFORE the board's own navigation so a reload whose\n // request predates the board but whose document commits during it, or\n // one that interrupts the initial goto itself, still counts. The\n // initial goto is the one navigation a fresh worker is supposed to\n // see; an in-place switch is a same-document update and expects none.\n const countersAtStart: DocumentCounters = { ...worker.counters };\n const expectedNavigations = worker.hasNavigated ? 0 : 1;\n const navigatedSinceStart = (): boolean =>\n documentReplaced(countersAtStart, worker.counters, expectedNavigations);\n try {\n const navStart = Date.now();\n try {\n // The direct-render URL sizes the element, not the browser. Resize\n // before mounting so media queries, viewport units, fixed controls,\n // and mount-time window measurements use this storyboard's size.\n const currentViewport = page.viewportSize();\n if (\n currentViewport?.width !== viewport.width ||\n currentViewport?.height !== viewport.height\n ) {\n await page.setViewportSize(viewport);\n }\n const targetUrl = buildDirectRenderUrl(options.hostUrl, target);\n if (!worker.hasNavigated) {\n await page.goto(targetUrl, { waitUntil: \"domcontentloaded\" });\n await page.addStyleTag({ content: FREEZE_MOTION_CSS });\n worker.hasNavigated = true;\n } else {\n // A normal target-to-target update unmounts the previous storyboard\n // and mounts the next in one React commit. Only an exact-target\n // retry needs an explicit idle hop to force a clean remount.\n if (worker.currentSelector === selector) {\n await page.evaluate((url) => {\n history.replaceState(history.state, \"\", url);\n window.dispatchEvent(\n new PopStateEvent(\"popstate\", { state: history.state }),\n );\n }, buildDirectRenderIdleUrl(options.hostUrl));\n await page.waitForSelector(worker.currentSelector, {\n state: \"detached\",\n timeout:\n capture.settleTimeoutMs ?? options.settleTimeoutMs ?? 30_000,\n });\n }\n await page.evaluate(\n ({ url, nextSelector }) => {\n const captureWindow = window as Window & {\n __tempoCaptureRenderProfile?: {\n begin(selector: string): void;\n };\n __tempoResetCaptureDeterminism?: () => void;\n };\n // Legacy SDKs only reset here. Current SDKs reset again at\n // storyboard admission, after any uncached imports have run.\n captureWindow.__tempoResetCaptureDeterminism?.();\n captureWindow.__tempoCaptureRenderProfile?.begin(nextSelector);\n // The URL selects a storyboard; the state still belongs to\n // its app router (e.g. BrowserRouter's idx and usr fields).\n history.replaceState(history.state, \"\", url);\n window.dispatchEvent(\n new PopStateEvent(\"popstate\", { state: history.state }),\n );\n },\n { url: targetUrl, nextSelector: selector },\n );\n }\n worker.currentSelector = selector;\n } finally {\n timings.navigateMs = Date.now() - navStart;\n options.timing?.recordBoard(\"navigate\", timings.navigateMs);\n }\n const settleStart = Date.now();\n try {\n await settlePage(page, selector, {\n timeoutMs:\n capture.settleTimeoutMs ?? options.settleTimeoutMs ?? 30_000,\n onStep: (name, ms) =>\n options.timing?.recordBoard(`settle.${name}`, ms),\n });\n } finally {\n timings.settleMs = Date.now() - settleStart;\n options.timing?.recordBoard(\"settle\", timings.settleMs);\n }\n\n const profile = await page.evaluate(\n readRenderProfileInPage,\n target.hostCanvasPath,\n );\n options.timing?.recordBoard(\n \"render.dom-content-loaded\",\n profile.domContentLoadedMs,\n );\n options.timing?.recordBoard(\n \"render.react-injected\",\n profile.reactInjectedMs,\n );\n options.timing?.recordBoard(\n \"render.react-first-commit\",\n profile.reactFirstCommitMs,\n );\n options.timing?.recordBoard(\n \"render.react-last-commit\",\n profile.reactLastCommitMs,\n );\n options.timing?.recordBoard(\n \"render.react-commit-work\",\n profile.reactCommitWorkMs,\n );\n options.timing?.recordBoard(\n \"render.target-module\",\n profile.targetModuleLoadedMs,\n );\n options.timing?.recordBoard(\n \"render.target-attached\",\n profile.targetAttachedMs,\n );\n options.timing?.recordBoard(\n \"render.last-canvas-module\",\n profile.lastCanvasModuleLoadedMs,\n );\n options.timing?.recordBoard(\n \"render.last-script\",\n profile.lastScriptLoadedMs,\n );\n options.timing?.recordBoard(\n \"render.long-task-time\",\n profile.longTaskDurationMs,\n );\n options.timing?.recordBoard(\n \"requests.all\",\n profile.requestCount,\n \"count\",\n );\n options.timing?.recordBoard(\n \"requests.script\",\n profile.scriptRequestCount,\n \"count\",\n );\n options.timing?.recordBoard(\n \"requests.canvas-module\",\n profile.canvasModuleRequestCount,\n \"count\",\n );\n options.timing?.recordBoard(\n \"requests.unrelated-canvas\",\n profile.unrelatedCanvasModuleRequestCount,\n \"count\",\n );\n options.timing?.recordBoard(\n \"react.commits\",\n profile.reactCommitCount,\n \"count\",\n );\n options.timing?.recordBoard(\n \"render.long-tasks\",\n profile.longTaskCount,\n \"count\",\n );\n\n const value = await work(page, selector);\n return { value, navigated: navigatedSinceStart() };\n } catch (error) {\n markNavigated(error, navigatedSinceStart());\n throw error;\n } finally {\n if (ownsWorker) await worker.dispose();\n }\n };\n\n const captureStoryboardArtifactsOnce = async (\n request: StoryboardArtifactRequest,\n ): Promise<{ value: StoryboardArtifacts; navigated: boolean }> => {\n const artifacts = request.artifacts ?? [\"png\"];\n const timings = { contextMs: 0, navigateMs: 0, settleMs: 0 };\n return withSettledStoryboard(\n request,\n request,\n timings,\n async (page, selector) => {\n const result: StoryboardArtifacts = {};\n // Measured post-settle so callers can refuse to publish a capture of\n // a page whose CSS never loaded (see style-gate.ts).\n result.styles = await page.evaluate(assessPageStylesInPage, selector);\n if (artifacts.includes(\"png\")) {\n const pngBytes = new Uint8Array(\n await page\n .locator(selector)\n .screenshot({ type: \"png\", animations: \"disabled\" }),\n );\n const decoded = PNG.sync.read(Buffer.from(pngBytes));\n result.png = {\n pngBytes,\n width: decoded.width,\n height: decoded.height,\n };\n }\n if (artifacts.includes(\"html\")) {\n const baseUrl = `${new URL(options.hostUrl).origin}/`;\n const extracted = await page.evaluate(\n extractStoryboardDomInPage,\n selector,\n );\n const rawDocument = buildStoryboardHtmlDocument(extracted, {\n baseUrl,\n });\n if (request.portableHtml ?? true) {\n const inlined = await page.evaluate(inlinePortableHtmlInPage, {\n html: rawDocument,\n baseUrl,\n });\n result.html = {\n html: inlined.html,\n baseUrl,\n portable: true,\n warnings: inlined.warnings,\n };\n } else {\n result.html = {\n html: rawDocument,\n baseUrl,\n portable: false,\n warnings: [],\n };\n }\n }\n return result;\n },\n );\n };\n\n /** One-shot captures own a fresh worker per attempt, so a reload only\n * costs the attempt it interrupted. A capture that crossed documents on\n * the last permitted attempt is a failure, never an artifact. */\n const captureStoryboardArtifacts = async (\n request: StoryboardArtifactRequest,\n ): Promise<StoryboardArtifacts> => {\n const key = `${request.hostCanvasPath}#${request.storyboardId}`;\n for (let attemptIndex = 0; ; attemptIndex += 1) {\n const lastAttempt = attemptIndex >= BOARD_ATTEMPT_LIMIT - 1;\n const attempts = attemptIndex + 1;\n let outcome: { value: StoryboardArtifacts; navigated: boolean };\n try {\n outcome = await captureStoryboardArtifactsOnce(request);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n // A dead browser fails every attempt the same way, and Playwright\n // words that death like a closed page — ask the browser itself.\n if (!browser.isConnected()) throw browserGoneError(message);\n const retryable =\n !isBrowserGoneError(message) &&\n (readNavigatedMark(error) ||\n isNavigationInterruptedError(message) ||\n isPageGoneError(message));\n if (lastAttempt || !retryable) throw error;\n log(\n `[capture] ${key}: ${message.split(\"\\n\")[0]} (attempt ${attempts}/${BOARD_ATTEMPT_LIMIT}) — re-capturing`,\n );\n continue;\n }\n if (!outcome.navigated) return outcome.value;\n if (lastAttempt) throw new Error(navigatedRejectionError(attempts));\n log(\n `[capture] ${key}: document reloaded mid-capture (attempt ${attempts}/${BOARD_ATTEMPT_LIMIT}) — re-capturing`,\n );\n }\n };\n\n const captureOne = async (\n board: CaptureBoard,\n worker?: CaptureWorker,\n ): Promise<BoardAttempt & { result: CapturedStoryboard }> => {\n const timings = {\n contextMs: 0,\n navigateMs: 0,\n settleMs: 0,\n screenshotMs: 0,\n };\n try {\n const { value, navigated } = await withSettledStoryboard(\n {\n hostCanvasPath: board.hostCanvasPath,\n storyboardId: board.storyboardId,\n width: board.layout.width,\n height: board.layout.height,\n },\n {},\n timings,\n async (page, selector) => {\n const shotStart = Date.now();\n let pngBytes: Uint8Array;\n let htmlBytes: Uint8Array;\n try {\n const element = page.locator(selector);\n pngBytes = new Uint8Array(\n await element.screenshot({ type: \"png\", animations: \"disabled\" }),\n );\n htmlBytes = new TextEncoder().encode(\n await element.evaluate((node) => node.outerHTML),\n );\n } finally {\n timings.screenshotMs = Date.now() - shotStart;\n options.timing?.recordBoard(\"screenshot\", timings.screenshotMs);\n }\n\n return {\n canvasPath: board.canvasPath,\n storyboardId: board.storyboardId,\n status: looksBlank(pngBytes) ? (\"empty\" as const) : (\"ok\" as const),\n pngHash: sha256Hex(pngBytes),\n pngBytes,\n htmlHash: sha256Hex(htmlBytes),\n htmlBytes,\n timings,\n };\n },\n worker,\n );\n return { result: value, status: value.status, navigated };\n } catch (error) {\n const result: CapturedStoryboard = {\n canvasPath: board.canvasPath,\n storyboardId: board.storyboardId,\n status: \"render-error\",\n error: error instanceof Error ? error.message : String(error),\n timings,\n };\n // The thrown error carries whether the document was replaced during\n // this board even though the step that noticed says nothing about it\n // (a settle timeout under a reload loop reads \"Timeout exceeded\").\n return {\n result,\n status: result.status,\n error: result.error,\n navigated: readNavigatedMark(error),\n };\n }\n };\n\n return {\n browserVersion: browser.version(),\n envFingerprint: fingerprint,\n captureBoards: async (boards, captureOptions) => {\n if (boards.length === 0) return [];\n const label = captureOptions?.label ?? \"capture\";\n const settleTimeoutMs = options.settleTimeoutMs ?? 30_000;\n const results: CapturedStoryboard[] = new Array(boards.length);\n const workerCount = Math.max(1, Math.min(parallelPages, boards.length));\n const stormWindow = reloadStormWindowFor(workerCount);\n // Oldest-first, one entry per completed board: did it need a reload\n // re-capture? Shared across workers; JavaScript's run-to-completion\n // keeps the check-and-push atomic.\n const recentReloadRetries: boolean[] = [];\n let reloadRetriesTotal = 0;\n let aborted = false;\n let nextIndex = 0;\n let completed = 0;\n const workers = Array.from({ length: workerCount }, async () => {\n let worker = await createCaptureWorker({});\n const replaceWorker = async () => {\n await worker.dispose();\n if (!browser.isConnected()) {\n throw browserGoneError(\"browser disconnected\");\n }\n try {\n worker = await createCaptureWorker({});\n } catch (error) {\n // Only a dead browser refuses a fresh context; nothing in this\n // session can capture anything further.\n throw browserGoneError(\n error instanceof Error ? error.message : String(error),\n );\n }\n };\n try {\n for (;;) {\n if (aborted) return;\n const index = nextIndex;\n nextIndex += 1;\n if (index >= boards.length) return;\n const board = boards[index]!;\n const key = `${board.canvasPath}#${board.storyboardId}`;\n let result: CapturedStoryboard;\n let reloadRetries = 0;\n for (let attemptIndex = 0; ; attemptIndex += 1) {\n const attempt = await captureOne(board, worker);\n const decision = decideBoardRetry(attempt, attemptIndex);\n if (decision === \"accept\") {\n result = finalizeAcceptedAttempt(attempt, attemptIndex + 1);\n if (attemptIndex > 0) {\n options.timing?.recordBoard(\n \"capture.retries\",\n attemptIndex,\n \"count\",\n );\n }\n break;\n }\n const reason = describeRetryReason(attempt);\n if (reason === \"document reloaded mid-capture\")\n reloadRetries += 1;\n log(\n `${label}: ${key}: ${reason} (attempt ${attemptIndex + 1}/${BOARD_ATTEMPT_LIMIT}) — re-capturing`,\n );\n if (decision === \"retry-fresh-worker\") {\n await replaceWorker();\n } else {\n await awaitDocumentQuiet(worker, settleTimeoutMs);\n }\n }\n results[index] = result;\n completed += 1;\n if (completed % 10 === 0 || completed === boards.length) {\n log(`${label}: ${completed}/${boards.length}`);\n }\n reloadRetriesTotal += reloadRetries;\n recentReloadRetries.push(reloadRetries > 0);\n if (recentReloadRetries.length > stormWindow) {\n recentReloadRetries.shift();\n }\n if (isReloadStorm(recentReloadRetries, stormWindow)) {\n aborted = true;\n throw new CaptureReloadStormError(\n `capture aborted: the document reloaded during the last ${stormWindow} storyboards in a row (${reloadRetriesTotal} re-captures so far) — check the devserver output and storyboard navigation for repeated reloads`,\n );\n }\n if (result.status === \"render-error\") {\n await replaceWorker();\n }\n }\n } finally {\n await worker.dispose();\n }\n });\n await Promise.all(workers);\n return results;\n },\n captureStoryboard: async (request) => {\n const { png } = await captureStoryboardArtifacts({\n ...request,\n artifacts: [\"png\"],\n });\n return png!;\n },\n captureStoryboardHtml: async (request) => {\n const { html } = await captureStoryboardArtifacts({\n ...request,\n artifacts: [\"html\"],\n portableHtml: request.portable ?? true,\n });\n return html!;\n },\n captureStoryboardArtifacts,\n dispose: async () => {\n await browser.close().catch(() => {});\n },\n };\n}\n","/**\n * The pending-marker contract, shared between the source app and the producer.\n *\n * A Tempo-owned admission or Suspense boundary renders a single hidden marker\n * element while waiting instead of `null`. That turns \"this subtree is briefly\n * empty because content is loading\" — which is indistinguishable from \"this\n * subtree is now empty\" once it reaches the DOM — into something the producer can\n * recognise without inspecting React internals.\n *\n * This file is imported directly by tempo-sdk and bundled into it, so it must\n * stay dependency-free: no imports, no DOM globals beyond the element passed in.\n */\n\n/** Marks an element as a Tempo-owned boundary's pending placeholder. */\nexport const PROJECTION_PENDING_ATTRIBUTE = \"data-tempo-projection-pending\";\n\n/**\n * Is this root showing a pending marker and nothing else?\n *\n * The rule is deliberately strict — marker-ONLY. A root that still holds real\n * content alongside a marker is a partially-loaded tree, and partial content is\n * exactly what the mirror should show; only a root whose entire visible output\n * is placeholder has nothing worth publishing. An empty root is likewise not\n * pending: emptiness with no marker is a real state (a conditional unmount, a\n * foreign boundary with `fallback={null}`) and stays a real change.\n */\nexport function isProjectionPendingOnly(rootElement: Element): boolean {\n const children = rootElement.children;\n if (children.length === 0) return false;\n for (let index = 0; index < children.length; index += 1) {\n const child = children[index];\n if (!child || !child.hasAttribute(PROJECTION_PENDING_ATTRIBUTE)) {\n return false;\n }\n }\n return true;\n}\n","import { PROJECTION_PENDING_ATTRIBUTE } from \"@modules/iframe-projection/combined/projection-pending\";\n\n/**\n * The CanvasHost DIRECT-RENDER URL contract, in one place.\n *\n * A canvas host document keyed by its query string mounts exactly one\n * storyboard at (0,0):\n *\n * /tempo-host?canvasPath=<module key>&storyboardId=<id>&width=&height=\n *\n * `width`/`height` pin the slot's size; `intrinsicSize=1` lets the slot hug\n * its rendered root (with any supplied size as the slot's MINIMUM), and\n * `tempoCaptureIdle=1` mounts nothing at all. The host re-reads the URL on\n * `popstate`, so a driver switches storyboards in place with\n * `history.replaceState` instead of reloading (see frame-pool's\n * `replaceState`). Two drivers share this file: the in-app capture lane\n * (`direct-render-capture-lane.ts`, over an offscreen frame) and the headless\n * engine (`tempo-modules/canvas-capture-headless`, over Playwright).\n *\n * Deliberately dependency-light: the headless engine is bundled into a\n * published CLI, so nothing here may drag the React runtime or Electron in.\n * The render id mirrors tempo-sdk's `getCanvasStoryboardRenderId`; the\n * headless engine's tests pin the two stay in sync.\n */\n\nexport const DIRECT_RENDER_PARAMS = {\n canvasPath: \"canvasPath\",\n storyboardId: \"storyboardId\",\n width: \"width\",\n height: \"height\",\n intrinsicSize: \"intrinsicSize\",\n idle: \"tempoCaptureIdle\",\n} as const;\n\n/** The attribute the SDK stamps on every storyboard's render root. */\nexport const STORYBOARD_RENDER_ID_ATTRIBUTE = \"data-tempo-storyboard-render-id\";\n\n/** Matches a Tempo Suspense boundary's pending placeholder — the \"still\n * loading\" marker a settle wait must see drained inside the target. */\nexport const STORYBOARD_PENDING_SELECTOR = `[${PROJECTION_PENDING_ATTRIBUTE}]`;\n\nexport function buildCanvasStoryboardRenderId(\n canvasPath: string,\n storyboardId: string,\n): string {\n return `canvas:${encodeURIComponent(canvasPath.trim())}#storyboard:${encodeURIComponent(\n storyboardId.trim(),\n )}`;\n}\n\n/** The selector of one storyboard's render root in a direct-render document. */\nexport function buildStoryboardRenderSelector(renderId: string): string {\n return `[${STORYBOARD_RENDER_ID_ATTRIBUTE}=${JSON.stringify(renderId)}]`;\n}\n\nexport type DirectRenderTarget = {\n /** Canvas file path relative to the project root (the host module key). */\n canvasPath: string;\n storyboardId: string;\n /** Slot size in CSS px; both or neither. */\n width?: number;\n height?: number;\n /** Let the slot hug its rendered root; a supplied size becomes its minimum. */\n intrinsicSize?: boolean;\n};\n\nexport function buildDirectRenderUrl(\n hostUrl: string,\n target: DirectRenderTarget,\n): string {\n const hasWidth = target.width !== undefined;\n const hasHeight = target.height !== undefined;\n if (hasWidth !== hasHeight) {\n throw new Error(\"Direct render width and height must be provided together\");\n }\n const url = new URL(hostUrl);\n url.searchParams.delete(DIRECT_RENDER_PARAMS.idle);\n url.searchParams.set(DIRECT_RENDER_PARAMS.canvasPath, target.canvasPath);\n url.searchParams.set(DIRECT_RENDER_PARAMS.storyboardId, target.storyboardId);\n if (hasWidth && hasHeight) {\n url.searchParams.set(DIRECT_RENDER_PARAMS.width, String(target.width));\n url.searchParams.set(DIRECT_RENDER_PARAMS.height, String(target.height));\n } else {\n url.searchParams.delete(DIRECT_RENDER_PARAMS.width);\n url.searchParams.delete(DIRECT_RENDER_PARAMS.height);\n }\n if (target.intrinsicSize) {\n url.searchParams.set(DIRECT_RENDER_PARAMS.intrinsicSize, \"1\");\n } else {\n url.searchParams.delete(DIRECT_RENDER_PARAMS.intrinsicSize);\n }\n return url.toString();\n}\n\n/** The host document with NOTHING mounted: the cheapest boot, and the hop a\n * driver uses to force a clean remount of the same storyboard. */\nexport function buildDirectRenderIdleUrl(hostUrl: string): string {\n const url = new URL(hostUrl);\n for (const param of Object.values(DIRECT_RENDER_PARAMS)) {\n url.searchParams.delete(param);\n }\n url.searchParams.set(DIRECT_RENDER_PARAMS.idle, \"1\");\n return url.toString();\n}\n","/** Shared browser bootstrap for deterministic visual capture environments. */\n\nexport const FROZEN_EPOCH_MS = Date.UTC(2026, 0, 1, 12, 0, 0);\n\n/** Survives Electron IPC error serialization so capture callers can report a\n * deterministic-setup failure instead of misclassifying it as a render bug. */\nexport const DETERMINISM_SETUP_ERROR_TOKEN =\n \"[frame-pool-capture:determinism_setup_failed]\";\n\n/** Runs before application code in both Playwright VRT and Electron frames. */\nexport function buildFreezeInitScript(): string {\n return `(() => {\n const FROZEN_NOW = ${FROZEN_EPOCH_MS};\n const RealDate = Date;\n class FrozenDate extends RealDate {\n constructor(...args) {\n if (args.length === 0) {\n super(FROZEN_NOW);\n } else {\n super(...args);\n }\n }\n static now() {\n return FROZEN_NOW;\n }\n }\n FrozenDate.parse = RealDate.parse;\n FrozenDate.UTC = RealDate.UTC;\n Date = FrozenDate;\n globalThis.Date = FrozenDate;\n if (typeof performance !== \"undefined\") {\n const origin = performance.now();\n const realNow = performance.now.bind(performance);\n performance.now = () => realNow() - origin;\n }\n let seed;\n globalThis.__tempoResetCaptureDeterminism = () => {\n seed = 0x74656d70;\n };\n globalThis.__tempoResetCaptureDeterminism();\n Math.random = () => {\n seed |= 0;\n seed = (seed + 0x6d2b79f5) | 0;\n let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n})();`;\n}\n\nexport const FREEZE_MOTION_CSS = `\n*, *::before, *::after {\n animation-duration: 0s !important;\n animation-delay: 0s !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0s !important;\n transition-delay: 0s !important;\n scroll-behavior: auto !important;\n caret-color: transparent !important;\n}\n`;\n","/**\n * Browser-side \"settled\" wait, authored as a SOURCE STRING so it can run in\n * any page driver: Electron's `webContents.executeJavaScript` (the pooled\n * offscreen frames, whose main bundle is bytecode-compiled and cannot\n * stringify functions at runtime) and Playwright's `page.evaluate` (the\n * headless canvas capture engine). Both drivers evaluate the identical\n * contract, so a storyboard that counts as settled for a visual-regression\n * baseline counts as settled for a canvas screenshot.\n *\n * Settled = the target subtree has nothing left to change, proven in order:\n *\n * 1. `target` — the selector is attached to the document.\n * 2. `fonts` — `document.fonts.status === \"loaded\"` (a page without the\n * Font Loading API passes; it has no fonts to wait for).\n * 3. `suspense` — no descendant of the target matches `pendingSelector`\n * (the caller's \"still loading\" marker; skipped when none is given).\n * 4. `content-quiet` — a cheap content signature (rect + descendant count +\n * text length + image count) holds still for `quietMs`, and every RENDERED\n * `<img>` inside the target (laid out, not visibility-hidden) has finished\n * loading — a rendered `loading=\"lazy\"` image is switched to eager first,\n * since a capture never scrolls it into view, while a hidden responsive\n * variant never holds the window. Geometry alone would race a fixed-size\n * card swapping \"Loading\" for fetched text, and an image's load handler\n * can do the same, so an incomplete image restarts the window.\n * A rendered `<video>` is held to a similar bar, with one crucial\n * difference: it must have DECODED the frame it is going to show\n * (`readyState >= HAVE_CURRENT_DATA`), because a video whose first frame\n * has not decoded paints as empty space — so without this a storyboard\n * with a paused clip captures WITH the frame on some runs and WITHOUT it\n * on others, flaking its baseline for no visible reason. Unlike an image,\n * though, this wait FAILS OPEN: an errored, sourceless or explicitly\n * `preload=\"none\"` element counts as finished, and the whole video wait is\n * bounded by {@link VIDEO_FRAME_WAIT_CAP_MS}. A strict driver turns a\n * content-quiet timeout into a failed capture, and a video that never\n * decodes must degrade to the pre-existing behaviour (capture it blank),\n * never take the storyboard down with it.\n * 5. `paint` — double `requestAnimationFrame`, so everything above has\n * reached the screen. Optional: a driver that proves paint freshness\n * itself (frame-pool's beacon captures) skips it.\n *\n * Every step is bounded by `stepTimeoutMs` on its own. The script resolves —\n * never rejects — with a {@link SettleWaitReport}: the caller decides how a\n * failed step surfaces, and a driver-level rejection therefore always means\n * the DOCUMENT went away (navigation, crash), never a slow page.\n *\n * All timing reads `performance.now()`: capture contexts run the determinism\n * kit (`capture-determinism.ts`), which FREEZES `Date` so pages render the\n * same clock every time, while `performance.now()` is only rebased and keeps\n * advancing. A wait keyed off `Date.now()` in such a page would never elapse\n * — neither its quiet window nor its own timeouts.\n */\n\nexport type SettleStep =\n | \"target\"\n | \"fonts\"\n | \"suspense\"\n | \"content-quiet\"\n | \"paint\";\n\n/** Evaluation order of the settle steps; timings are reported in it. */\nexport const SETTLE_STEPS: readonly SettleStep[] = [\n \"target\",\n \"fonts\",\n \"suspense\",\n \"content-quiet\",\n \"paint\",\n];\n\nexport type SettleWaitOptions = {\n /** The element whose subtree must settle. */\n selector: string;\n /** Descendants matching this selector mean \"still loading\" (step 3).\n * Omit to skip the step. */\n pendingSelector?: string;\n /** How long the content signature must hold still. Default 300. */\n quietMs?: number;\n /** Budget for EACH step. Default 15_000. */\n stepTimeoutMs?: number;\n /** Budget of the content-quiet step alone, when it should differ from\n * `stepTimeoutMs`: a driver capturing live, animating content gives the\n * quiet window a short budget while the mount steps keep theirs. */\n quietTimeoutMs?: number;\n /** When the content-quiet budget elapses, resolve `ok` with `quiet: false`\n * instead of failing the run — the content never held still, but every\n * other step settled and the caller would rather capture a moving target\n * than nothing. Default false (strict). */\n quietBestEffort?: boolean;\n /** Poll interval for the waiting steps. Default 50. */\n pollMs?: number;\n /** Run the double-rAF paint step. Default true. */\n paint?: boolean;\n};\n\n/**\n * Longest a not-yet-decoded `<video>` may hold the content-quiet window.\n * Waiting for a first frame buys baseline stability; it must never be able to\n * fail a capture, so the wait is capped and then abandoned. Comfortably covers\n * a local asset decode without eating a meaningful slice of the step budget.\n */\nexport const VIDEO_FRAME_WAIT_CAP_MS = 2_000;\n\nexport const DEFAULT_SETTLE_QUIET_MS = 300;\nexport const DEFAULT_SETTLE_STEP_TIMEOUT_MS = 15_000;\nexport const DEFAULT_SETTLE_POLL_MS = 50;\n\n/** Per-step durations in ms, present for every step that ran. */\nexport type SettleStepTimings = Partial<Record<SettleStep, number>>;\n\nexport type SettleWaitReport =\n | {\n ok: true;\n steps: SettleStepTimings;\n /** False when `quietBestEffort` gave up on the content-quiet step: the\n * target was still changing when that budget ran out. */\n quiet?: boolean;\n }\n | { ok: false; step: SettleStep; message: string; steps: SettleStepTimings };\n\n/**\n * Survives IPC / Playwright error serialization so a caller can tell \"the\n * document was replaced while settling\" from \"the page never settled\".\n */\nexport const SETTLE_NAVIGATED_TOKEN = \"[frame-pool-settle:navigated]\";\n\n/** Upper bound on a whole settle run: every step spending its full budget. */\nexport function settleWaitBudgetMs(options: SettleWaitOptions): number {\n const stepTimeoutMs = options.stepTimeoutMs ?? DEFAULT_SETTLE_STEP_TIMEOUT_MS;\n const quietTimeoutMs = options.quietTimeoutMs ?? stepTimeoutMs;\n const stepCount = SETTLE_STEPS.length - (options.paint === false ? 1 : 0);\n return stepTimeoutMs * (stepCount - 1) + quietTimeoutMs;\n}\n\nexport function buildSettleWaitScript(options: SettleWaitOptions): string {\n const quietMs = Math.max(0, Math.round(options.quietMs ?? DEFAULT_SETTLE_QUIET_MS));\n const stepTimeoutMs = Math.max(\n 1,\n Math.round(options.stepTimeoutMs ?? DEFAULT_SETTLE_STEP_TIMEOUT_MS),\n );\n const quietTimeoutMs = Math.max(\n 1,\n Math.round(options.quietTimeoutMs ?? stepTimeoutMs),\n );\n const pollMs = Math.max(1, Math.round(options.pollMs ?? DEFAULT_SETTLE_POLL_MS));\n return `\n (function() {\n var selector = ${JSON.stringify(options.selector)};\n var pendingSelector = ${JSON.stringify(options.pendingSelector ?? null)};\n var quietMs = ${quietMs};\n var stepTimeoutMs = ${stepTimeoutMs};\n var quietTimeoutMs = ${quietTimeoutMs};\n var quietBestEffort = ${options.quietBestEffort === true ? \"true\" : \"false\"};\n var pollMs = ${pollMs};\n var runPaint = ${options.paint === false ? \"false\" : \"true\"};\n var steps = {};\n // Cleared when the content-quiet step gives up under best effort.\n var quiet = true;\n // Monotonic and immune to the determinism kit's frozen Date.\n var now = typeof performance !== 'undefined' && performance && typeof performance.now === 'function'\n ? function() { return performance.now(); }\n : function() { return Date.now(); };\n\n function failure(step, message) {\n return { ok: false, step: step, message: message, steps: steps };\n }\n\n // Polls \\`check\\` until it answers true, or \\`budgetMs\\` elapses. The\n // first check runs synchronously so an already-settled step costs no\n // poll interval. A step run with \\`giveUp\\` resolves at its budget\n // instead of failing, recording that the content never held still.\n function until(step, check, budgetMs, giveUp) {\n var started = now();\n return new Promise(function(resolve, reject) {\n function tick() {\n var done;\n try {\n done = check();\n } catch (error) {\n steps[step] = Math.round(now() - started);\n reject(failure(step, String((error && error.message) || error)));\n return;\n }\n if (done) {\n steps[step] = Math.round(now() - started);\n resolve();\n return;\n }\n if (now() - started >= budgetMs) {\n steps[step] = Math.round(now() - started);\n if (giveUp) {\n quiet = false;\n resolve();\n return;\n }\n reject(failure(step, 'Timeout ' + budgetMs + 'ms exceeded'));\n return;\n }\n setTimeout(tick, pollMs);\n }\n tick();\n });\n }\n\n var quietKey = null;\n var quietSince = 0;\n // When a rendered video was first seen without a decoded frame, and how\n // long that may hold the quiet window before we give up on it.\n var videoWaitStartedAt = null;\n var videoWaitCapMs = ${VIDEO_FRAME_WAIT_CAP_MS};\n // Images that take part in the capture: laid out and not hidden. A\n // hidden responsive variant never starts native lazy loading and must\n // not hold the window; a rendered lazy image never starts either,\n // because a capture does not scroll, so it is asked for eagerly.\n function renderedMatches(target, tag) {\n var all = Array.prototype.slice.call(target.querySelectorAll(tag));\n var rendered = [];\n for (var i = 0; i < all.length; i++) {\n var el = all[i];\n if (el.getClientRects().length === 0) continue;\n var visibility = getComputedStyle(el).visibility;\n if (visibility === 'hidden' || visibility === 'collapse') continue;\n rendered.push(el);\n }\n return rendered;\n }\n function renderedImages(target) {\n return renderedMatches(target, 'img');\n }\n // A video paints its current frame only once that frame is DECODED.\n // HAVE_CURRENT_DATA (2) is exactly that bar.\n //\n // Everything else here is about FAILING OPEN. The baseline lane runs\n // this step strict, so a video that can never reach that bar would turn\n // a capture that used to succeed into a settle.content-quiet timeout —\n // for every storyboard in the repo, not just ones we had in mind. So a\n // video only holds the window while it is plausibly ON ITS WAY to a\n // frame: an errored element, one with no source at all, and one the page\n // explicitly opted out of loading (preload=\"none\", whose poster-or-\n // nothing paint is already deterministic) are all treated as finished.\n function videoFrameReady(video) {\n if (video.error) return true;\n if (video.preload === 'none') return true;\n var networkState = video.networkState;\n if (networkState === 0 /* NETWORK_EMPTY */) return true;\n if (networkState === 3 /* NETWORK_NO_SOURCE */) return true;\n return video.readyState >= 2 /* HAVE_CURRENT_DATA */;\n }\n function contentQuiet() {\n var target = document.querySelector(selector);\n if (!target) return false;\n var images = renderedImages(target);\n for (var i = 0; i < images.length; i++) {\n if (images[i].loading === 'lazy') images[i].loading = 'eager';\n }\n for (var j = 0; j < images.length; j++) {\n if (!images[j].complete) {\n // Load/error handlers can replace content without moving its\n // geometry: start a fresh quiet window once they have run.\n quietKey = null;\n return false;\n }\n }\n // Second line of defence behind videoFrameReady's fail-open rules: a\n // video that is legitimately loading can still stall forever (a dev\n // server that never answers, a huge asset). Waiting is an OPTIMISATION\n // for baseline stability, never a reason to fail a capture, so the\n // whole video wait is bounded — past the cap we capture whatever is\n // on screen, exactly as we did before videos were considered at all.\n var videos = renderedMatches(target, 'video');\n for (var w = 0; w < videos.length; w++) {\n if (!videoFrameReady(videos[w])) {\n if (videoWaitStartedAt === null) videoWaitStartedAt = now();\n if (now() - videoWaitStartedAt < videoWaitCapMs) {\n quietKey = null;\n return false;\n }\n break;\n }\n }\n var rect = target.getBoundingClientRect();\n var text = target.innerText || '';\n var key = [\n rect.x,\n rect.y,\n rect.width,\n rect.height,\n target.querySelectorAll('*').length,\n text.length,\n images.length\n ].join(',');\n var at = now();\n if (quietKey !== key) {\n quietKey = key;\n quietSince = at;\n return false;\n }\n return at - quietSince >= quietMs;\n }\n\n function paint() {\n if (!runPaint) return Promise.resolve();\n var started = now();\n return new Promise(function(resolve) {\n requestAnimationFrame(function() {\n requestAnimationFrame(function() {\n steps.paint = Math.round(now() - started);\n resolve();\n });\n });\n });\n }\n\n return until('target', function() {\n return document.querySelector(selector) !== null;\n }, stepTimeoutMs, false)\n .then(function() {\n return until('fonts', function() {\n var fonts = document.fonts;\n return !fonts || fonts.status === 'loaded';\n }, stepTimeoutMs, false);\n })\n .then(function() {\n return until('suspense', function() {\n if (!pendingSelector) return true;\n var target = document.querySelector(selector);\n if (!target) return false;\n return !target.querySelector(pendingSelector);\n }, stepTimeoutMs, false);\n })\n .then(function() {\n return until('content-quiet', contentQuiet, quietTimeoutMs, quietBestEffort);\n })\n .then(paint)\n .then(\n function() {\n return { ok: true, steps: steps, quiet: quiet };\n },\n function(caught) {\n if (caught && caught.ok === false) return caught;\n return failure('paint', String((caught && caught.message) || caught));\n }\n );\n })()\n `;\n}\n","/**\n * Playwright's wording when the document was replaced under a running step.\n * Capture pages are never navigated on purpose mid-board, so every one of\n * these is an outside reload — the devserver's dep optimizer discovering a\n * dependency and broadcasting `full-reload`, an HMR-driven reload, the\n * canvas host's own recovery reload, or a ws-disconnect reload. None is the\n * storyboard's fault, so the board is re-captured instead of reported as a\n * `render-error` (the 2026-09 VRT \"occasionally fails to render\" class).\n */\nexport function isNavigationInterruptedError(message: string): boolean {\n return /Execution context was destroyed|most likely because of a navigation|Cannot find context with specified id|[Ff]rame was detached|Navigation interrupted|is interrupted by another navigation|Inspected target navigated or closed|Cannot find object with id|context was destroyed/.test(\n message,\n );\n}\n","import { STORYBOARD_PENDING_SELECTOR } from \"@modules/component-render-pool/direct-render\";\nimport {\n buildFreezeInitScript,\n FREEZE_MOTION_CSS,\n FROZEN_EPOCH_MS,\n} from \"@modules/frame-pool/capture-determinism\";\nimport {\n buildSettleWaitScript,\n SETTLE_STEPS,\n type SettleStep,\n type SettleWaitReport,\n} from \"@modules/frame-pool/settle-script\";\nimport type { Page } from \"playwright\";\n\nimport { isNavigationInterruptedError } from \"./navigation-errors\";\n\nexport { buildFreezeInitScript, FREEZE_MOTION_CSS, FROZEN_EPOCH_MS };\nexport type { SettleStep };\n\n/**\n * The determinism kit (TEM-1775 §5.3). Determinism is the product: under a\n * blocking gate every false diff is a blocked merge, so every source of\n * nondeterminism the kit can pin, it pins. Behavioral changes here MUST bump\n * `KIT_VERSION` (fingerprint.ts) — that is what turns \"the kit changed\"\n * into a fresh baseline epoch instead of a wall of false regressions.\n */\n\n/** Chromium launch args: pinned color profile, pinned DPR, no scrollbars,\n * grayscale text AA (LCD subpixel rendering varies with the headless GPU\n * path). */\nexport const CHROMIUM_LAUNCH_ARGS = [\n \"--force-color-profile=srgb\",\n \"--force-device-scale-factor=2\",\n \"--hide-scrollbars\",\n \"--disable-lcd-text\",\n];\n\n/**\n * Settle = the page has nothing left to change: target attached, fonts\n * loaded, Tempo's own Suspense boundaries drained (`data-tempo-projection-\n * pending` markers — the deterministic \"still loading\" signal every Tempo\n * boundary must emit), a bounded content-quiet window on the target, then\n * double-rAF for paint.\n *\n * The contract is frame-pool's `settle-script.ts`, evaluated here through\n * Playwright and in the Tempo app through the frame's `waitForSettled` —\n * the same source string, so a storyboard that counts as settled for a\n * baseline counts as settled for an in-app screenshot. Every step is bounded\n * by `timeoutMs` on its own; a failed step is thrown as `settle.<step>: …`.\n *\n * A reload landing mid-wait destroys the evaluation context. Playwright's\n * `waitForFunction` (the previous driver) re-ran itself in the replacement\n * document; this wait does the same by re-evaluating the script there,\n * bounded by the budget every step would have spent, so the engine's own\n * reload accounting (`documentReplaced`) — not this wait — decides whether\n * the capture is trustworthy. A page that reloads until that budget is gone\n * is reported as a settle timeout, the verdict `waitForFunction` reached.\n */\nexport async function settlePage(\n page: Page,\n targetSelector: string,\n options: {\n quietMs?: number;\n timeoutMs?: number;\n onStep?: (name: SettleStep, ms: number) => void;\n } = {},\n): Promise<void> {\n const quietMs = options.quietMs ?? 300;\n const timeoutMs = options.timeoutMs ?? 15_000;\n const budgetMs = timeoutMs * SETTLE_STEPS.length;\n const deadline = Date.now() + budgetMs;\n let resumes = 0;\n for (;;) {\n let report: SettleWaitReport;\n try {\n report = (await page.evaluate(\n buildSettleWaitScript({\n selector: targetSelector,\n pendingSelector: STORYBOARD_PENDING_SELECTOR,\n quietMs,\n stepTimeoutMs: Math.max(1, Math.min(timeoutMs, deadline - Date.now())),\n }),\n )) as SettleWaitReport;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!isNavigationInterruptedError(message)) throw error;\n resumes += 1;\n if (Date.now() >= deadline) {\n throw new Error(\n `settle: Timeout ${budgetMs}ms exceeded; the document was replaced ${resumes} times while waiting`,\n );\n }\n continue;\n }\n for (const step of SETTLE_STEPS) {\n const ms = report.steps[step];\n if (ms !== undefined) options.onStep?.(step, ms);\n }\n if (!report.ok) {\n throw new Error(`settle.${report.step}: ${report.message}`);\n }\n return;\n }\n}\n","/**\n * Zero-setup Chromium (TEM-2121): when a capture launches and Playwright's\n * Chromium isn't installed, download it automatically and retry — canvas\n * screenshots/share must \"just work\" in cloud sandboxes without a manual\n * `npx playwright install chromium` step. The download is one-time per\n * machine (Playwright's browser cache persists) and runs inside the\n * capture call, whose MCP budget (10 min) comfortably covers it.\n *\n * Opt out with `TEMPO_MCP_NO_AUTO_INSTALL=1` (locked-down environments);\n * the original launch error then surfaces with the manual command.\n */\nimport { spawn } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\n\n/** Kill a wedged download rather than hang the tool call to its budget. */\nconst INSTALL_TIMEOUT_MS = 8 * 60 * 1000;\n\n/** Playwright's missing-browser launch failure (vs a real crash). */\nexport function isMissingBrowserError(err: unknown): boolean {\n const message = err instanceof Error ? err.message : String(err);\n // `playwright install-deps` is Playwright's remedy for MISSING SYSTEM\n // LIBRARIES around an installed browser — a different failure that a\n // browser download cannot fix, so it must not classify as missing.\n return (\n message.includes(\"Executable doesn't exist\") ||\n /playwright install(?!-deps)/.test(message)\n );\n}\n\nexport function autoInstallDisabled(): boolean {\n return process.env.TEMPO_MCP_NO_AUTO_INSTALL === \"1\";\n}\n\n/**\n * Run Playwright's own installer for Chromium, resolved from the SAME\n * playwright package the capture imports (so the downloaded revision\n * always matches the driver).\n */\nexport async function installChromium(\n log: (line: string) => void,\n spawnImpl: typeof spawn = spawn,\n): Promise<void> {\n const require = createRequire(import.meta.url);\n const cliPath = path.join(\n path.dirname(require.resolve(\"playwright/package.json\")),\n \"cli.js\",\n );\n\n await new Promise<void>((resolve, reject) => {\n const child = spawnImpl(\n process.execPath,\n [cliPath, \"install\", \"chromium\"],\n { stdio: [\"ignore\", \"pipe\", \"pipe\"], windowsHide: true },\n );\n let tail = \"\";\n const collect = (chunk: Buffer | string) => {\n tail = (tail + String(chunk)).slice(-2000);\n };\n child.stdout?.on(\"data\", collect);\n child.stderr?.on(\"data\", collect);\n\n const timer = setTimeout(() => {\n child.kill(\"SIGKILL\");\n reject(\n new Error(\n `Chromium download timed out after ${INSTALL_TIMEOUT_MS / 60000} minutes. Run \\`npx playwright install chromium\\` manually.`,\n ),\n );\n }, INSTALL_TIMEOUT_MS);\n\n child.on(\"error\", (err) => {\n clearTimeout(timer);\n reject(err);\n });\n child.on(\"exit\", (code) => {\n clearTimeout(timer);\n if (code === 0) {\n log(\"Chromium installed.\");\n resolve();\n } else {\n reject(\n new Error(\n `playwright install chromium exited with ${code}: ${tail.trim().slice(-500)}`,\n ),\n );\n }\n });\n });\n}\n","import type { EnvFingerprint } from \"./types\";\n\n/**\n * The environment fingerprint (TEM-1775 §3.1): comparisons only ever run\n * within one fingerprint. Bumping any component (Chromium major, the font\n * story, the determinism kit) flips the string, and the first run on the new\n * fingerprint reports a fresh baseline epoch — everything `new`, nothing\n * `changed` — instead of hundreds of false regressions.\n */\n\n/** Bump on ANY behavioral change to the determinism kit (§5.3).\n * 2: content-quiet settle signature, per-board browser contexts,\n * service workers blocked.\n * 3: load rendered lazy images without scrolling; hidden image variants\n * do not block content readiness.\n * 4: reset seeded randomness at storyboard admission, after imports, so cold\n * and reused capture pages render the same random content.\n * 5: size the browser viewport to each storyboard before mounting, so\n * responsive styles and viewport-relative layout match its dimensions. */\nexport const KIT_VERSION = \"5\";\n\n/** v1 dogfood renders with the runner's system fonts behind a\n * `document.fonts.ready` wait; a bundled font set replaces this later. */\nexport const FONTS_BUNDLE_VERSION = \"system-1\";\n\nexport const DEVICE_SCALE_FACTOR = 2;\n\nexport function buildEnvFingerprint(input: {\n os: string;\n browserVersion: string;\n}): { fingerprint: string; parts: EnvFingerprint } {\n const parts: EnvFingerprint = {\n os: input.os,\n // FULL Chromium version, not just the major: a patch-level rendering\n // change must open a fresh epoch, not surface as mass false diffs\n // inside the old one. The version moves only with the lockfile's\n // Playwright pin, so epochs track dependency bumps.\n browser: `chromium-${input.browserVersion}`,\n dpr: DEVICE_SCALE_FACTOR,\n fontsBundleVersion: FONTS_BUNDLE_VERSION,\n kitVersion: KIT_VERSION,\n };\n return {\n fingerprint: [\n parts.os,\n parts.browser,\n `dpr${parts.dpr}`,\n `fonts-${parts.fontsBundleVersion}`,\n `kit-${parts.kitVersion}`,\n ].join(\"/\"),\n parts,\n };\n}\n","import type { ExtractedStoryboardDom } from \"./page-scripts\";\n\n/**\n * Reassembles an extracted storyboard DOM into a standalone HTML document,\n * mirroring the share flow's `projectionHtmlCaptureToDocument`\n * (@tempo-modules/static-canvas-capture): the rebuilt document's `<body>`\n * plays the role the direct-render slot played in the capture host — the\n * box the storyboard fills. A viewer renders this document in an iframe\n * already sized to the storyboard, so the html/body chain must track the\n * viewport (percentage-height storyboard roots resolve against a definite\n * height like they did in the sized slot).\n */\n\nconst escapeHtml = (value: string): string =>\n value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n\n// Placed before the captured head so author CSS keeps precedence.\nconst ROOT_SIZE_STYLE = \"<style>html{width:100%;height:100%;}</style>\";\n\nfunction serializeAttributes(attributes: Record<string, string>): string {\n return Object.entries(attributes)\n .filter(([name]) => name !== \"style\")\n .map(([name, value]) => ` ${name}=\"${escapeHtml(value)}\"`)\n .join(\"\");\n}\n\n/** The captured body's width/height described the capture viewport, not the\n * storyboard's geometry. Drop them and pin the body to the viewport; any\n * other declarations are kept as-is. */\nfunction buildBodyStyle(style: string | undefined): string {\n const kept = (style ?? \"\")\n .split(\";\")\n .map((declaration) => declaration.trim())\n .filter((declaration) => {\n const colon = declaration.indexOf(\":\");\n if (colon <= 0) return false;\n const property = declaration.slice(0, colon).trim().toLowerCase();\n return property !== \"width\" && property !== \"height\";\n });\n kept.push(\"width: 100%\", \"height: 100%\", \"margin: 0\");\n return `${kept.join(\"; \")};`;\n}\n\nexport function buildStoryboardHtmlDocument(\n capture: ExtractedStoryboardDom,\n options: { baseUrl: string; title?: string },\n): string {\n const title = options.title\n ? `<title>${escapeHtml(options.title)}</title>`\n : \"\";\n const base = /<base[\\s>]/i.test(capture.headHtml)\n ? \"\"\n : `<base href=\"${escapeHtml(options.baseUrl)}\">`;\n return [\n \"<!DOCTYPE html>\",\n `<html${serializeAttributes(capture.htmlAttributes)}>`,\n \"<head>\",\n '<meta charset=\"utf-8\">',\n title,\n base,\n ROOT_SIZE_STYLE,\n capture.headHtml,\n \"</head>\",\n `<body${serializeAttributes(capture.bodyAttributes)} style=\"${escapeHtml(\n buildBodyStyle(capture.bodyAttributes.style),\n )}\">`,\n capture.elementHtml,\n \"</body>\",\n \"</html>\",\n ].join(\"\");\n}\n","/**\n * Browser-side functions passed to Playwright's `page.evaluate`. Playwright\n * serializes the function source and runs it inside the page, so each one\n * MUST be fully self-contained: no references to module-scope helpers or\n * imports — every helper is nested inside the function body.\n */\n\nexport type ExtractedStoryboardDom = {\n /** `document.head.innerHTML` with `<script>` elements stripped. In Vite\n * dev mode the page's styles live here (Vite injects CSS as `<style>`\n * tags), alongside any real `<link rel=\"stylesheet\">`. */\n headHtml: string;\n htmlAttributes: Record<string, string>;\n bodyAttributes: Record<string, string>;\n /** The storyboard element's outerHTML. */\n elementHtml: string;\n};\n\n/** Serializes the pieces `buildStoryboardHtmlDocument` reassembles into a\n * standalone document. Runs in the page. */\nexport function extractStoryboardDomInPage(\n selector: string,\n): ExtractedStoryboardDom {\n const readAttributes = (element: Element): Record<string, string> => {\n const attributes: Record<string, string> = {};\n for (const attr of Array.from(element.attributes)) {\n attributes[attr.name] = attr.value;\n }\n return attributes;\n };\n const target = document.querySelector(selector);\n if (!target) {\n throw new Error(`storyboard element not found for selector ${selector}`);\n }\n const head = document.head.cloneNode(true) as HTMLHeadElement;\n for (const script of Array.from(head.querySelectorAll(\"script\"))) {\n script.remove();\n }\n return {\n headHtml: head.innerHTML,\n htmlAttributes: readAttributes(document.documentElement),\n bodyAttributes: readAttributes(document.body),\n elementHtml: target.outerHTML,\n };\n}\n\nexport type PageStyleAssessment = {\n /** CSS rules across every same-origin stylesheet. */\n authorRuleCount: number;\n /** Cross-origin stylesheets whose `cssRules` can't be read. A CDN sheet\n * can carry a project's entire styling, so any of these means real CSS\n * is present even when `authorRuleCount` is near zero. */\n unreadableSheetCount: number;\n /** Total characters across all `<style>` elements. */\n styleTextBytes: number;\n /** Elements carrying a `class` attribute inside the storyboard. */\n classElementCount: number;\n /** Elements carrying an inline `style` attribute inside the storyboard. */\n inlineStyledElementCount: number;\n};\n\n/**\n * Measures how much CSS actually made it into the page — the signal behind\n * the style-starvation gate. A devserver whose CSS pipeline failed (missing\n * native tooling after a partial install is the classic cloud-VM case)\n * still renders correct DOM and settles cleanly, so without this check a\n * capture of that page LOOKS successful while every utility class is dead.\n * Runs in the page; must stay self-contained.\n */\nexport function assessPageStylesInPage(\n selector: string,\n): PageStyleAssessment {\n let authorRuleCount = 0;\n let unreadableSheetCount = 0;\n for (const sheet of Array.from(document.styleSheets)) {\n try {\n authorRuleCount += sheet.cssRules.length;\n } catch {\n unreadableSheetCount += 1;\n }\n }\n let styleTextBytes = 0;\n for (const style of Array.from(document.querySelectorAll(\"style\"))) {\n styleTextBytes += (style.textContent ?? \"\").length;\n }\n const scope = document.querySelector(selector) ?? document.body;\n return {\n authorRuleCount,\n unreadableSheetCount,\n styleTextBytes,\n classElementCount: scope.querySelectorAll(\"[class]\").length,\n inlineStyledElementCount: scope.querySelectorAll(\"[style]\").length,\n };\n}\n\nexport type InlinePortableHtmlResult = { html: string; warnings: string[] };\n\n/**\n * Makes a captured storyboard document self-contained (\"portable\") so it\n * renders identically when served from a share page where the author's\n * devserver is NOT reachable. Mirrors the transforms of tempo-client's\n * `makeShareHtmlPortable` (share-html-portable.ts), re-hosted to run INSIDE\n * the live Playwright page — where `DOMParser` exists and the devserver is\n * still up to serve the fetches:\n *\n * - non-public `<link rel=\"stylesheet\">` → `<style>` with the fetched CSS\n * (its own `url(...)` refs inlined, resolved against the stylesheet URL)\n * - `url(...)` refs in inline `<style>`s and `style=\"\"` attributes → data:\n * URIs, resolved against the document base\n * - non-public `<img src>` → data: URIs\n * - the non-public `<base href>` is dropped once everything is inlined\n * - `<script>` elements are removed (a share renders in a sandboxed\n * srcdoc iframe; dead references would only 404)\n *\n * Publicly fetchable refs (CDN fonts, https assets) are left as-is.\n * Every fetch is best-effort: a failure records a warning and keeps the\n * original reference rather than aborting the capture.\n */\nexport async function inlinePortableHtmlInPage(input: {\n html: string;\n baseUrl: string;\n}): Promise<InlinePortableHtmlResult> {\n const warnings: string[] = [];\n const warn = (message: string) => {\n warnings.push(message);\n };\n\n const cssUrlMatcher = (): RegExp => /url\\(\\s*(['\"]?)([^'\")]+?)\\1\\s*\\)/gi;\n\n const isPubliclyFetchableUrl = (url: URL): boolean => {\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return false;\n const hostname = url.hostname;\n if (hostname === \"localhost\" || hostname.endsWith(\".localhost\"))\n return false;\n if (hostname === \"::1\" || hostname === \"[::1]\" || hostname === \"0.0.0.0\")\n return false;\n if (/^127\\./.test(hostname)) return false;\n if (\n /^10\\./.test(hostname) ||\n /^192\\.168\\./.test(hostname) ||\n /^172\\.(1[6-9]|2\\d|3[01])\\./.test(hostname)\n )\n return false;\n return true;\n };\n\n /** Absolute URL ONLY when the ref needs inlining (non-public, non-data). */\n const resolveAssetUrl = (\n value: string | null,\n baseHref: string,\n ): string | null => {\n const trimmed = value?.trim();\n if (!trimmed || trimmed.startsWith(\"data:\") || trimmed.startsWith(\"#\")) {\n return null;\n }\n try {\n const resolved = new URL(trimmed, baseHref);\n return isPubliclyFetchableUrl(resolved) ? null : resolved.href;\n } catch {\n return null;\n }\n };\n\n const bytesToBase64 = (bytes: Uint8Array): string => {\n let binary = \"\";\n const chunkSize = 0x8000;\n for (let i = 0; i < bytes.length; i += chunkSize) {\n binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));\n }\n return btoa(binary);\n };\n\n const blobToDataUrl = async (\n blob: Blob,\n contentType: string,\n ): Promise<string> => {\n const bytes = new Uint8Array(await blob.arrayBuffer());\n const mime = blob.type || contentType || \"application/octet-stream\";\n return `data:${mime};base64,${bytesToBase64(bytes)}`;\n };\n\n const inlineCssUrls = async (\n cssText: string,\n cssBaseHref: string,\n ): Promise<string> => {\n const toFetch = new Map<string, string>();\n for (const match of cssText.matchAll(cssUrlMatcher())) {\n const raw = match[2]!.trim();\n const resolved = resolveAssetUrl(raw, cssBaseHref);\n if (resolved) toFetch.set(raw, resolved);\n }\n if (toFetch.size === 0) return cssText;\n\n const inlined = new Map<string, string>();\n await Promise.all(\n Array.from(toFetch, async ([raw, resolvedUrl]) => {\n try {\n const response = await fetch(resolvedUrl);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const blob = await response.blob();\n const contentType =\n blob.type || response.headers.get(\"content-type\") || \"\";\n inlined.set(raw, await blobToDataUrl(blob, contentType));\n } catch (error) {\n warn(`failed to inline CSS asset ${resolvedUrl}: ${String(error)}`);\n }\n }),\n );\n if (inlined.size === 0) return cssText;\n\n return cssText.replace(\n cssUrlMatcher(),\n (full, _quote: string, raw: string) => {\n const dataUri = inlined.get(raw.trim());\n return dataUri ? `url(\"${dataUri}\")` : full;\n },\n );\n };\n\n const doc = new DOMParser().parseFromString(input.html, \"text/html\");\n const baseHref =\n doc.querySelector(\"base[href]\")?.getAttribute(\"href\") ?? input.baseUrl;\n\n // Order matters: inline the existing inline-<style> url()s and element\n // `style=\"...url()\"` attributes (resolved against the document base)\n // BEFORE inlining external stylesheets, so the <style> elements we\n // synthesize for those — whose url()s are already resolved against their\n // own stylesheet URL — aren't reprocessed.\n await Promise.all(\n Array.from(doc.querySelectorAll(\"style\")).map(async (style) => {\n const css = style.textContent ?? \"\";\n if (!css.includes(\"url(\")) return;\n style.textContent = await inlineCssUrls(css, baseHref);\n }),\n );\n await Promise.all(\n Array.from(doc.querySelectorAll<HTMLElement>('[style*=\"url(\"]')).map(\n async (element) => {\n const style = element.getAttribute(\"style\");\n if (!style) return;\n element.setAttribute(\"style\", await inlineCssUrls(style, baseHref));\n },\n ),\n );\n\n await Promise.all(\n Array.from(\n doc.querySelectorAll<HTMLLinkElement>('link[rel~=\"stylesheet\"][href]'),\n ).map(async (link) => {\n const resolvedUrl = resolveAssetUrl(link.getAttribute(\"href\"), baseHref);\n if (!resolvedUrl) return; // public or data: — loads fine as-is.\n try {\n const response = await fetch(resolvedUrl);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const cssText = await inlineCssUrls(await response.text(), resolvedUrl);\n const style = doc.createElement(\"style\");\n style.setAttribute(\"data-tempo-inlined-stylesheet\", \"true\");\n const media = link.getAttribute(\"media\");\n if (media) style.setAttribute(\"media\", media);\n style.textContent = cssText;\n link.replaceWith(style);\n } catch (error) {\n warn(`failed to inline stylesheet ${resolvedUrl}: ${String(error)}`);\n }\n }),\n );\n\n await Promise.all(\n Array.from(doc.querySelectorAll(\"img[src]\")).map(async (image) => {\n const resolvedUrl = resolveAssetUrl(image.getAttribute(\"src\"), baseHref);\n if (!resolvedUrl) return;\n try {\n const response = await fetch(resolvedUrl);\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const blob = await response.blob();\n const contentType =\n blob.type || response.headers.get(\"content-type\") || \"\";\n if (!contentType.toLowerCase().startsWith(\"image/\")) return;\n image.setAttribute(\"src\", await blobToDataUrl(blob, contentType));\n image.removeAttribute(\"srcset\");\n image.setAttribute(\"loading\", \"eager\");\n image.setAttribute(\"decoding\", \"sync\");\n } catch (error) {\n warn(`failed to inline image ${resolvedUrl}: ${String(error)}`);\n }\n }),\n );\n\n for (const script of Array.from(doc.querySelectorAll(\"script\"))) {\n script.remove();\n }\n\n // Drop a non-public <base href> once everything is inlined — the\n // devserver base is dead on a share page and would only mis-resolve any\n // remaining relative reference. A public base (rare) is kept.\n const base = doc.querySelector(\"base[href]\");\n if (base && resolveAssetUrl(base.getAttribute(\"href\"), input.baseUrl)) {\n base.remove();\n }\n\n return { html: `<!DOCTYPE html>${doc.documentElement.outerHTML}`, warnings };\n}\n","type ReactCommitSample = {\n atMs: number;\n actualDurationMs: number;\n};\n\ntype CaptureRenderProfile = {\n startedAtMs: number;\n fullNavigation: boolean;\n targetSelector: string;\n targetAttachedMs: number | null;\n reactInjectedMs: number | null;\n reactCommits: ReactCommitSample[];\n longTaskCount: number;\n longTaskDurationMs: number;\n begin(targetSelector: string): void;\n};\n\ntype CaptureProfileWindow = Window & {\n __tempoCaptureRenderProfile?: CaptureRenderProfile;\n __REACT_DEVTOOLS_GLOBAL_HOOK__?: Record<string, unknown>;\n};\n\nexport type RenderProfileSnapshot = {\n domContentLoadedMs: number;\n targetAttachedMs: number;\n targetModuleLoadedMs: number;\n lastCanvasModuleLoadedMs: number;\n lastScriptLoadedMs: number;\n reactInjectedMs: number;\n reactFirstCommitMs: number;\n reactLastCommitMs: number;\n reactCommitWorkMs: number;\n reactCommitCount: number;\n longTaskCount: number;\n longTaskDurationMs: number;\n requestCount: number;\n scriptRequestCount: number;\n canvasModuleRequestCount: number;\n unrelatedCanvasModuleRequestCount: number;\n};\n\n/**\n * Installed before any page script. This is the smallest useful slice of the\n * React DevTools hook: React reports renderer injection and root commits, but\n * no backend walks the fiber tree or changes application state.\n */\nexport function installRenderProfileInPage(): void {\n const profileWindow = window as CaptureProfileWindow;\n performance.setResourceTimingBufferSize(10_000);\n const readTargetSelector = () => {\n const params = new URLSearchParams(window.location.search);\n const canvasPath =\n params.get(\"canvasPath\")?.trim() ?? params.get(\"pageFile\")?.trim();\n const storyboardId =\n params.get(\"storyboardId\")?.trim() ?? params.get(\"exportName\")?.trim();\n if (!canvasPath || !storyboardId) return \"\";\n const renderId = `canvas:${encodeURIComponent(canvasPath)}#storyboard:${encodeURIComponent(storyboardId)}`;\n return `[data-tempo-storyboard-render-id=${JSON.stringify(renderId)}]`;\n };\n const profile: CaptureRenderProfile = {\n startedAtMs: performance.now(),\n fullNavigation: true,\n targetSelector: readTargetSelector(),\n targetAttachedMs: null,\n reactInjectedMs: null,\n reactCommits: [],\n longTaskCount: 0,\n longTaskDurationMs: 0,\n begin(targetSelector) {\n this.startedAtMs = performance.now();\n this.fullNavigation = false;\n this.targetSelector = targetSelector;\n this.targetAttachedMs = null;\n this.reactInjectedMs = null;\n this.reactCommits = [];\n this.longTaskCount = 0;\n this.longTaskDurationMs = 0;\n },\n };\n profileWindow.__tempoCaptureRenderProfile = profile;\n\n const markTargetAttached = () => {\n if (\n profile.targetAttachedMs === null &&\n profile.targetSelector &&\n document.querySelector(profile.targetSelector)\n ) {\n profile.targetAttachedMs = performance.now() - profile.startedAtMs;\n }\n };\n markTargetAttached();\n new MutationObserver(markTargetAttached).observe(document, {\n childList: true,\n subtree: true,\n });\n\n try {\n new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n profile.longTaskCount += 1;\n profile.longTaskDurationMs += entry.duration;\n }\n }).observe({ type: \"longtask\", buffered: true });\n } catch {\n // Long Task Timing is optional; the other milestones remain useful.\n }\n\n if (profileWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__) return;\n let nextRendererId = 0;\n const renderers = new Map<number, unknown>();\n profileWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {\n supportsFiber: true,\n // Consumers that also load `modules/iframe-projection` see this window\n // property through its global `DevToolsHook` augmentation, where\n // `renderers` is typed by ReactRenderer; this stub only counts them.\n renderers: renderers as never,\n inject(renderer: unknown) {\n profile.reactInjectedMs ??= performance.now() - profile.startedAtMs;\n nextRendererId += 1;\n renderers.set(nextRendererId, renderer);\n return nextRendererId;\n },\n onCommitFiberRoot(_rendererId: number, root: { current?: unknown }) {\n const current = root.current as { actualDuration?: unknown } | undefined;\n profile.reactCommits.push({\n atMs: performance.now() - profile.startedAtMs,\n actualDurationMs:\n typeof current?.actualDuration === \"number\"\n ? current.actualDuration\n : 0,\n });\n },\n onCommitFiberUnmount() {},\n checkDCE() {},\n onScheduleFiberRoot() {},\n };\n}\n\n/** Read browser milestones after the target has settled. All times are from\n * navigation start, matching PerformanceResourceTiming.responseEnd. */\nexport function readRenderProfileInPage(\n targetCanvasPath: string,\n): RenderProfileSnapshot {\n const profileWindow = window as CaptureProfileWindow;\n const profile = profileWindow.__tempoCaptureRenderProfile;\n const startedAtMs = profile?.startedAtMs ?? 0;\n const navigation = performance.getEntriesByType(\"navigation\")[0] as\n | PerformanceNavigationTiming\n | undefined;\n const resources = (\n performance.getEntriesByType(\"resource\") as PerformanceResourceTiming[]\n ).filter((entry) => entry.startTime >= startedAtMs);\n const normalizedTarget = `/${targetCanvasPath.replace(/^tempo\\//, \"\")}`;\n const canvasModules = resources.filter((entry) => {\n const pathname = new URL(entry.name).pathname;\n return pathname.includes(\"/designs/\") && pathname.includes(\".canvas\");\n });\n const targetModule = canvasModules.find(\n (entry) => new URL(entry.name).pathname === normalizedTarget,\n );\n const scriptResources = resources.filter(\n (entry) => entry.initiatorType === \"script\",\n );\n const reactCommits = profile?.reactCommits ?? [];\n\n return {\n domContentLoadedMs: profile?.fullNavigation\n ? (navigation?.domContentLoadedEventEnd ?? 0)\n : 0,\n targetAttachedMs:\n profile?.targetAttachedMs ?? performance.now() - startedAtMs,\n targetModuleLoadedMs: targetModule\n ? targetModule.responseEnd - startedAtMs\n : 0,\n lastCanvasModuleLoadedMs: Math.max(\n 0,\n ...canvasModules.map((entry) => entry.responseEnd - startedAtMs),\n ),\n lastScriptLoadedMs: Math.max(\n 0,\n ...scriptResources.map((entry) => entry.responseEnd - startedAtMs),\n ),\n reactInjectedMs: profile?.reactInjectedMs ?? 0,\n reactFirstCommitMs: reactCommits[0]?.atMs ?? 0,\n reactLastCommitMs: reactCommits[reactCommits.length - 1]?.atMs ?? 0,\n reactCommitWorkMs: reactCommits.reduce(\n (total, commit) => total + commit.actualDurationMs,\n 0,\n ),\n reactCommitCount: reactCommits.length,\n longTaskCount: profile?.longTaskCount ?? 0,\n longTaskDurationMs: profile?.longTaskDurationMs ?? 0,\n requestCount: resources.length,\n scriptRequestCount: scriptResources.length,\n canvasModuleRequestCount: canvasModules.length,\n unrelatedCanvasModuleRequestCount: canvasModules.filter(\n (entry) => new URL(entry.name).pathname !== normalizedTarget,\n ).length,\n };\n}\n","import { PNG } from \"pngjs\";\n\nimport { DEVICE_SCALE_FACTOR } from \"./fingerprint\";\nimport type {\n CompositeOptions,\n CompositeResult,\n CompositeStoryboardImage,\n} from \"./types\";\n\n/**\n * Composites per-storyboard PNG captures into one canvas-layout image —\n * the headless counterpart of the share flow's composite OG/unfurl image\n * (@tempo-modules/static-canvas-capture composite.ts, which needs a browser\n * `<canvas>`; this one is pure pngjs so it runs in plain Node).\n *\n * Inputs are positioned by their storyboard layout x/y (CSS px, scaled by\n * `scale`) and drawn at their PNG's intrinsic pixel size — all inputs must\n * therefore have been captured at the same deviceScaleFactor as `scale`.\n * Later inputs draw over earlier ones (source-over alpha blending).\n */\n\nconst DEFAULT_PADDING = 24;\n\n/** Padding in output px; a percentage resolves against the smaller content\n * dimension (mirrors the share composite's `padding: \"5%\"` semantics). */\nexport function normalizeCompositePadding(\n value: CompositeOptions[\"padding\"],\n contentWidth: number,\n contentHeight: number,\n): number {\n if (value == null) return DEFAULT_PADDING;\n if (typeof value === \"string\") {\n const match = value.trim().match(/^(\\d+(?:\\.\\d+)?)%$/);\n if (!match) {\n throw new Error('Composite padding percentage must use the form \"5%\"');\n }\n const percent = Number(match[1]);\n if (!Number.isFinite(percent) || percent < 0) {\n throw new Error(\"Composite padding percentage must be non-negative\");\n }\n return Math.round(Math.min(contentWidth, contentHeight) * (percent / 100));\n }\n if (!Number.isFinite(value) || value < 0) {\n throw new Error(\"Composite padding must be a non-negative number\");\n }\n return Math.round(value);\n}\n\nexport function compositeStoryboardPngs(\n images: readonly CompositeStoryboardImage[],\n options: CompositeOptions = {},\n): CompositeResult {\n if (images.length === 0) {\n throw new Error(\"At least one image is required to composite a canvas image\");\n }\n const scale = options.scale ?? DEVICE_SCALE_FACTOR;\n\n const decoded = images.map((image) => {\n if (!Number.isFinite(image.x) || !Number.isFinite(image.y)) {\n throw new Error(\"Composite image inputs must have finite positions\");\n }\n return {\n png: PNG.sync.read(Buffer.from(image.pngBytes)),\n x: Math.round(image.x * scale),\n y: Math.round(image.y * scale),\n };\n });\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const { png, x, y } of decoded) {\n minX = Math.min(minX, x);\n minY = Math.min(minY, y);\n maxX = Math.max(maxX, x + png.width);\n maxY = Math.max(maxY, y + png.height);\n }\n const contentWidth = maxX - minX;\n const contentHeight = maxY - minY;\n const padding = normalizeCompositePadding(\n options.padding,\n contentWidth,\n contentHeight,\n );\n\n const width = contentWidth + padding * 2;\n const height = contentHeight + padding * 2;\n const output = new PNG({ width, height });\n\n if (options.background) {\n const { r, g, b } = options.background;\n const a = Math.round((options.background.a ?? 1) * 255);\n for (let index = 0; index < output.data.length; index += 4) {\n output.data[index] = r;\n output.data[index + 1] = g;\n output.data[index + 2] = b;\n output.data[index + 3] = a;\n }\n }\n\n for (const { png, x, y } of decoded) {\n const offsetX = x - minX + padding;\n const offsetY = y - minY + padding;\n for (let row = 0; row < png.height; row += 1) {\n for (let col = 0; col < png.width; col += 1) {\n const src = (row * png.width + col) * 4;\n const dst = ((offsetY + row) * width + (offsetX + col)) * 4;\n const srcA = png.data[src + 3]! / 255;\n if (srcA === 0) continue;\n if (srcA === 1) {\n output.data[dst] = png.data[src]!;\n output.data[dst + 1] = png.data[src + 1]!;\n output.data[dst + 2] = png.data[src + 2]!;\n output.data[dst + 3] = 255;\n continue;\n }\n // Source-over on straight alpha.\n const dstA = output.data[dst + 3]! / 255;\n const outA = srcA + dstA * (1 - srcA);\n for (let channel = 0; channel < 3; channel += 1) {\n const srcC = png.data[src + channel]!;\n const dstC = output.data[dst + channel]!;\n output.data[dst + channel] =\n outA === 0\n ? 0\n : Math.round((srcC * srcA + dstC * dstA * (1 - srcA)) / outA);\n }\n output.data[dst + 3] = Math.round(outA * 255);\n }\n }\n }\n\n return {\n pngBytes: new Uint8Array(PNG.sync.write(output)),\n width,\n height,\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { TempoDevServer } from \"@tempo-modules/tempo-devserver\";\nimport type { HttpUrlSource } from \"@tempo-modules/tempo-devserver\";\n\n/**\n * The headless devserver session: boots the tempobook devserver (a plain\n * Node/Vite child — no Electron) for a project's tempo dir and registers\n * canvases into the generated host, waiting for the registration receipt so\n * Vite provably serves the rewritten wrapper.\n */\n\nexport type TempoDevserverConfig = {\n scripts: {\n install: string;\n start: string;\n stop: string | null;\n updateSdk?: string;\n };\n tempoHostUrl: HttpUrlSource | null;\n};\n\n/** Reads and validates the devserver-relevant slice of tempo.config.json.\n * Throws when scripts.install / scripts.start are missing — the devserver\n * cannot boot without them. */\nexport async function readTempoDevserverConfig(\n tempoConfigPath: string,\n): Promise<TempoDevserverConfig> {\n const config = JSON.parse(await readFile(tempoConfigPath, \"utf-8\")) as {\n scripts?: {\n install?: string;\n start?: string;\n stop?: string | null;\n updateSdk?: unknown;\n };\n urls?: { tempoHost?: HttpUrlSource | null };\n };\n if (!config.scripts?.install || !config.scripts?.start) {\n throw new Error(\n `tempo.config.json at ${tempoConfigPath} must declare scripts.install and scripts.start`,\n );\n }\n const updateSdk = config.scripts.updateSdk;\n if (\n updateSdk != null &&\n (typeof updateSdk !== \"string\" || updateSdk.trim().length === 0)\n ) {\n throw new Error(\n `tempo.config.json at ${tempoConfigPath}: scripts.updateSdk must be a non-empty string when provided`,\n );\n }\n return {\n scripts: {\n install: config.scripts.install,\n start: config.scripts.start,\n stop: config.scripts.stop ?? null,\n ...(typeof updateSdk === \"string\" ? { updateSdk: updateSdk.trim() } : {}),\n },\n tempoHostUrl: config.urls?.tempoHost ?? null,\n };\n}\n\nexport type CanvasHostLayout = {\n /** The folder holding tempo.config.json (`<repo>/tempo`, or the repo root\n * for a legacy root-level config). */\n tempoDir: string;\n /** The project root the devserver keys canvas modules against\n * (`projectRootForTempoDir`). */\n projectRoot: string;\n /** The canvases root: `paths.canvases` resolved against the tempo dir,\n * default `<tempoDir>/designs`. */\n pagesDir: string;\n /** The config file that decided the layout, or null when none exists\n * (defaults apply — an onboarding PR's merge-base has no host yet). */\n configPath: string | null;\n};\n\n/**\n * The project root for a tempo dir, by the SAME rule `TempoDevServer`\n * applies (`resolveRuntimePaths`): a dir named `tempo` belongs to its\n * parent project; anything else (a legacy root-level config, a custom\n * host folder) is its own project root. Every `hostCanvasPath` — the\n * canvas-host module key the devserver registers and the direct-render URL\n * takes — is relative to this root, so enumeration and devserver startup\n * must agree on it.\n */\nexport function projectRootForTempoDir(tempoDir: string): string {\n const resolved = path.resolve(tempoDir);\n return path.basename(resolved) === \"tempo\" ? path.dirname(resolved) : resolved;\n}\n\n/**\n * Where a repo's canvas host lives, resolved exactly like the Tempo app\n * (`resolveCanvasesDirSync` in tempo-project): the canonical\n * `<repo>/tempo/tempo.config.json` first, then a legacy root-level\n * `<repo>/tempo.config.json` (only when it is a desktop config — it has a\n * `scripts` member), with `paths.canvases` honored relative to the config's\n * folder. Both the CI capture and the setup doctor enumerate through this so\n * \"tracked\" is the same set in both; an explicit `tempoDir` skips discovery\n * but still honors its `paths.canvases`.\n */\nexport async function resolveCanvasHostLayout(options: {\n repoRoot: string;\n tempoDir?: string;\n}): Promise<CanvasHostLayout> {\n const repoRoot = path.resolve(options.repoRoot);\n const candidates = options.tempoDir\n ? [path.join(path.resolve(options.tempoDir), \"tempo.config.json\")]\n : [\n path.join(repoRoot, \"tempo\", \"tempo.config.json\"),\n path.join(repoRoot, \"tempo.config.json\"),\n ];\n for (const configPath of candidates) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(await readFile(configPath, \"utf-8\"));\n } catch {\n continue;\n }\n if (typeof parsed !== \"object\" || parsed === null || !(\"scripts\" in parsed)) {\n continue;\n }\n const tempoDir = path.dirname(configPath);\n const paths = (parsed as { paths?: { canvases?: unknown } }).paths;\n const canvases =\n typeof paths?.canvases === \"string\" && paths.canvases.trim().length > 0\n ? paths.canvases.trim().split(\"\\\\\").join(\"/\")\n : \"designs\";\n return {\n tempoDir,\n projectRoot: projectRootForTempoDir(tempoDir),\n pagesDir: path.resolve(tempoDir, canvases),\n configPath,\n };\n }\n const tempoDir = options.tempoDir\n ? path.resolve(options.tempoDir)\n : path.join(repoRoot, \"tempo\");\n return {\n tempoDir,\n projectRoot: projectRootForTempoDir(tempoDir),\n pagesDir: path.join(tempoDir, \"designs\"),\n configPath: null,\n };\n}\n\n/** Resolves the tempo dir (the folder holding tempo.config.json) from\n * whichever of the three addressing forms the caller has. */\nexport function resolveTempoDir(options: {\n tempoDir?: string;\n repoRoot?: string;\n tempoConfigPath?: string;\n}): string {\n if (options.tempoConfigPath) return path.dirname(options.tempoConfigPath);\n if (options.tempoDir) return options.tempoDir;\n if (options.repoRoot) return path.join(options.repoRoot, \"tempo\");\n throw new Error(\n \"startDevserverSession needs one of tempoDir, repoRoot, or tempoConfigPath\",\n );\n}\n\n/**\n * Registration receipt barrier: proves Vite will serve a wrapper that\n * contains the canvas (see tempo-sdk CLAUDE.md). 409 = wrapper not yet\n * rewritten — `reRegister` and retry briefly. A framework without the\n * capability header predates the barrier; skip in that case.\n */\nexport async function awaitCanvasRegistration(options: {\n origin: string;\n hostCanvasPath: string;\n reRegister: () => Promise<void>;\n /** Attempts before giving up (matches the historical 21-post budget). */\n maxAttempts?: number;\n retryDelayMs?: number;\n log?: (line: string) => void;\n}): Promise<void> {\n const log = options.log ?? (() => {});\n const maxAttempts = options.maxAttempts ?? 21;\n const retryDelayMs = options.retryDelayMs ?? 250;\n for (let attempt = 0; ; attempt += 1) {\n const response = await fetch(\n `${options.origin}/__tempo/canvas-host-registration`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ canvasPath: options.hostCanvasPath }),\n },\n ).catch(() => null);\n if (!response) {\n throw new Error(`registration barrier unreachable at ${options.origin}`);\n }\n if (!response.headers.get(\"x-tempo-canvas-host-registration\")) {\n log(\"registration barrier not supported by host framework — skipping\");\n return;\n }\n if (response.ok) return;\n if (attempt >= maxAttempts - 1) {\n throw new Error(\n `canvas ${options.hostCanvasPath} never registered into the host (last status ${response.status})`,\n );\n }\n await options.reRegister();\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs));\n }\n}\n\n/**\n * Devserver output lines that explain a capture going wrong: Vite's dep\n * optimizer re-bundling mid-session (every wave ends in a `full-reload`\n * broadcast that replaces the document under any in-flight capture), the\n * scan failure that turns pre-bundling off entirely (so EVERY dependency is\n * discovered at runtime, wave after wave), HMR-driven reloads, and the\n * tempo plugin's own recovery reloads. Surfacing these in the capture log is\n * what makes a \"board occasionally fails to render\" run diagnosable from CI\n * output alone.\n */\nexport const DEVSERVER_RELOAD_SIGNAL_RE =\n /optimized dependencies changed|new dependencies optimized|Re-optimizing dependencies|Forced re-optimization|Failed to run dependency scan|could not be resolved|\\(imported by |Skipping dependency pre-bundling|page reload|full-reload|server restarted|\\[tempo\\] (?:re-emitting|recovered)|hmr update|hmr invalidate/i;\n\n/** The one signal worth a loud warning: with the scan aborted, Vite serves\n * nothing pre-bundled and reloads every page on each discovery wave. */\nconst DEVSERVER_SCAN_FAILURE_RE = /Failed to run dependency scan/i;\n\n// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape stripping\nconst ANSI_RE = /\\x1b\\[[0-9;]*[A-Za-z]/g;\n\nfunction cleanDevserverLine(line: string): string {\n return line.replace(ANSI_RE, \"\").trim();\n}\n\n/** Reassembles the child's stdout/stderr byte stream into whole lines. A\n * `data` chunk ends wherever the pipe buffer did, so a signal can straddle\n * two chunks; each line is delivered exactly once, ANSI-stripped and\n * trimmed, blanks dropped. `flush` releases an unterminated tail. */\nexport function createDevserverLineSplitter(): {\n push(chunk: string): string[];\n flush(): string[];\n} {\n let remainder = \"\";\n return {\n push(chunk) {\n const parts = (remainder + chunk).split(/\\r?\\n/);\n remainder = parts.pop() ?? \"\";\n return parts.map(cleanDevserverLine).filter((line) => line.length > 0);\n },\n flush() {\n const tail = cleanDevserverLine(remainder);\n remainder = \"\";\n return tail.length > 0 ? [tail] : [];\n },\n };\n}\n\nexport function isDevserverReloadSignal(line: string): boolean {\n return DEVSERVER_RELOAD_SIGNAL_RE.test(line);\n}\n\n/** A capture-log line for a reload signal, or null when the line is not\n * one. The scan failure gets its consequence spelled out, because the\n * line Vite prints reads like a warning while it is what disables\n * pre-bundling for the whole session. */\nexport function formatDevserverReloadSignal(line: string): string | null {\n const clean = cleanDevserverLine(line);\n if (!isDevserverReloadSignal(clean)) return null;\n if (DEVSERVER_SCAN_FAILURE_RE.test(clean)) {\n return `⚠ devserver: ${clean} — dependency pre-bundling is OFF for this session; every dependency will be discovered at runtime and each discovery wave full-reloads every capture page`;\n }\n return `devserver: ${clean}`;\n}\n\nexport type DevserverSession = {\n origin: string;\n hostUrl: string;\n /** The project root (parent of the tempo dir) — host canvas paths are\n * relative to it. */\n projectRoot: string;\n tempoDir: string;\n /** Registers one more canvas into the running host (idempotent) and\n * waits for its registration receipt — for long-lived sessions that\n * discover canvases after boot. */\n registerCanvas: (hostCanvasPath: string) => Promise<void>;\n dispose: () => Promise<void>;\n};\n\n/**\n * Boots the tempobook devserver for the given tempo dir and registers every\n * canvas in `hostCanvasPaths` into the generated host, waiting for each\n * registration receipt.\n */\nexport async function startDevserverSession(options: {\n /** The tempo dir (holding tempo.config.json), e.g. `<repo>/tempo`. */\n tempoDir?: string;\n /** Alternative addressing: the project root; tempoDir = `<repoRoot>/tempo`. */\n repoRoot?: string;\n /** Alternative addressing: the config file itself. */\n tempoConfigPath?: string;\n /** Canvas file paths relative to the project root to pre-register. */\n hostCanvasPaths?: string[];\n readyTimeoutMs?: number;\n log?: (line: string) => void;\n /** Every stdout/stderr line the devserver child prints (ANSI-stripped,\n * blanks dropped, reassembled across chunk boundaries), from launch\n * through dispose. Pair with `formatDevserverReloadSignal` to surface\n * only the lines that explain a reload mid-capture. A throwing listener\n * is reported through `log` and never reaches the child's stream\n * handling. */\n onOutput?: (line: string) => void;\n}): Promise<DevserverSession> {\n const log = options.log ?? (() => {});\n const tempoDir = resolveTempoDir(options);\n const tempoConfigPath =\n options.tempoConfigPath ?? path.join(tempoDir, \"tempo.config.json\");\n const config = await readTempoDevserverConfig(tempoConfigPath);\n const hostCanvasPaths = options.hostCanvasPaths ?? [];\n\n const devServer = new TempoDevServer({\n tempoConfigPath,\n tempoHostUrl: config.tempoHostUrl,\n scripts: config.scripts,\n });\n await devServer.initialize();\n // The same root the devserver keys modules against — a legacy root-level\n // config is its own project root, not its parent folder.\n const projectRoot = projectRootForTempoDir(tempoDir);\n for (const hostCanvasPath of hostCanvasPaths) {\n await devServer.ensureCanvasRegistered(\n path.join(projectRoot, hostCanvasPath),\n );\n }\n\n const handle = await devServer.startDevServer();\n const onOutput = options.onOutput;\n const outputLines = onOutput ? createDevserverLineSplitter() : null;\n const forwardOutput = (lines: string[]) => {\n if (!onOutput) return;\n for (const line of lines) {\n try {\n onOutput(line);\n } catch (error) {\n // The child's data handler parses readiness from the same chunk\n // after notifying listeners; a listener exception must never\n // unwind it.\n log(\n `devserver output listener threw: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n };\n if (outputLines) {\n // Subscribed before the ready barrier: the optimizer's startup scan\n // (and its failure mode) prints while the server is still booting.\n handle.onOutput((data) => forwardOutput(outputLines.push(String(data))));\n }\n const ready = await handle.waitUntilReady(options.readyTimeoutMs ?? 120_000);\n if (!ready.ok || !ready.url) {\n // Stop the producer before flushing its unterminated tail, or output\n // that arrives during the stop would split the final line.\n await handle.stop().catch(() => {});\n await devServer.dispose();\n forwardOutput(outputLines?.flush() ?? []);\n throw new Error(\n `tempobook devserver failed to become ready: ${ready.message ?? (ready.timedOut ? \"timeout\" : \"unknown\")}`,\n );\n }\n const hostUrl = ready.url;\n const origin = new URL(hostUrl).origin;\n log(`devserver ready at ${hostUrl}`);\n\n const registerCanvas = async (hostCanvasPath: string) => {\n await devServer.ensureCanvasRegistered(\n path.join(projectRoot, hostCanvasPath),\n );\n await awaitCanvasRegistration({\n origin,\n hostCanvasPath,\n reRegister: () =>\n devServer.ensureCanvasRegistered(\n path.join(projectRoot, hostCanvasPath),\n ),\n log,\n });\n };\n\n for (const hostCanvasPath of hostCanvasPaths) {\n await awaitCanvasRegistration({\n origin,\n hostCanvasPath,\n reRegister: () =>\n devServer.ensureCanvasRegistered(\n path.join(projectRoot, hostCanvasPath),\n ),\n log,\n });\n }\n\n return {\n origin,\n hostUrl,\n projectRoot,\n tempoDir,\n registerCanvas,\n dispose: async () => {\n // In-app the host process is deliberately adoptable across restarts —\n // `dispose()` detaches without killing it. A capture run owns its\n // devserver, so stop the child tree explicitly.\n await handle.stop().catch(() => {});\n await devServer.dispose();\n forwardOutput(outputLines?.flush() ?? []);\n },\n };\n}\n","import { spawn } from \"node:child_process\";\n\nexport interface AbortableShellCommandResult {\n exitCode: number | null;\n stdout: string;\n stderr: string;\n /** Bounded combined tail in delivery order, for command diagnostics. */\n output: string;\n aborted: boolean;\n timedOut: boolean;\n spawnError: Error | null;\n}\n\nexport interface AbortableShellCommandOptions {\n command: string;\n cwd: string;\n env?: NodeJS.ProcessEnv;\n signal?: AbortSignal;\n maxOutputBytes?: number;\n /** Live stdout and stderr, before the command settles. */\n onOutput?: (data: string) => void;\n /** Stop the process tree after this long without stdout or stderr. */\n idleTimeoutMs?: number;\n}\n\nfunction appendTail(\n current: string,\n chunk: Buffer | string,\n limit: number,\n): string {\n return `${current}${String(chunk)}`.slice(-limit);\n}\n\n/**\n * Run one shell command as an abortable process tree.\n *\n * The promise settles from the child's close event, never merely from the\n * abort event. On POSIX abort it also completes the bounded TERM-to-KILL group\n * ladder; on Windows it waits for the taskkill tree walk. A caller may\n * therefore retain lifecycle authority until managed teardown has finished.\n */\nexport function runAbortableShellCommand(\n options: AbortableShellCommandOptions,\n): Promise<AbortableShellCommandResult> {\n const outputLimit = options.maxOutputBytes ?? 4_000;\n if (options.signal?.aborted) {\n return Promise.resolve({\n exitCode: null,\n stdout: \"\",\n stderr: \"\",\n output: \"\",\n aborted: true,\n timedOut: false,\n spawnError: null,\n });\n }\n\n return new Promise((resolve) => {\n let stdout = \"\";\n let stderr = \"\";\n let output = \"\";\n let aborted = false;\n let timedOut = false;\n let settled = false;\n let terminationComplete = true;\n let closeResult: number | null | undefined;\n let forceTimer: ReturnType<typeof setTimeout> | null = null;\n let idleTimer: ReturnType<typeof setTimeout> | null = null;\n let child: ReturnType<typeof spawn>;\n try {\n child = spawn(options.command, {\n cwd: options.cwd,\n shell: true,\n detached: process.platform !== \"win32\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n windowsHide: true,\n env: options.env,\n });\n } catch (error) {\n resolve({\n exitCode: null,\n stdout,\n stderr,\n output,\n aborted: false,\n timedOut: false,\n spawnError: error instanceof Error ? error : new Error(String(error)),\n });\n return;\n }\n\n let spawnError: Error | null = null;\n const finishIfComplete = (): void => {\n if (settled || closeResult === undefined || !terminationComplete) return;\n settled = true;\n if (forceTimer) clearTimeout(forceTimer);\n if (idleTimer) clearTimeout(idleTimer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n resolve({\n exitCode: closeResult,\n stdout,\n stderr,\n output,\n aborted,\n timedOut,\n spawnError,\n });\n };\n const terminateTree = (reason: \"abort\" | \"timeout\"): void => {\n if (aborted || timedOut || closeResult !== undefined) return;\n aborted = reason === \"abort\";\n timedOut = reason === \"timeout\";\n if (idleTimer) clearTimeout(idleTimer);\n terminationComplete = false;\n const pid = child.pid;\n if (pid != null && process.platform !== \"win32\") {\n try {\n process.kill(-pid, \"SIGTERM\");\n } catch {\n try {\n child.kill(\"SIGTERM\");\n } catch {}\n }\n forceTimer = setTimeout(() => {\n try {\n process.kill(-pid, \"SIGKILL\");\n } catch {\n try {\n child.kill(\"SIGKILL\");\n } catch {}\n }\n terminationComplete = true;\n finishIfComplete();\n }, 1_000);\n return;\n }\n if (pid != null) {\n const killer = spawn(\"taskkill\", [\"/PID\", String(pid), \"/T\", \"/F\"], {\n stdio: \"ignore\",\n windowsHide: true,\n });\n killer.on(\"error\", () => {\n try {\n child.kill(\"SIGKILL\");\n } catch {}\n terminationComplete = true;\n finishIfComplete();\n });\n killer.on(\"close\", () => {\n terminationComplete = true;\n finishIfComplete();\n });\n } else {\n try {\n child.kill(\"SIGKILL\");\n } catch {}\n terminationComplete = true;\n }\n };\n const onAbort = () => terminateTree(\"abort\");\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n // AbortSignal does not replay an abort that lands between the pre-spawn\n // check and listener registration.\n if (options.signal?.aborted) onAbort();\n\n const resetIdleTimer = (): void => {\n if (idleTimer) clearTimeout(idleTimer);\n if (\n aborted ||\n timedOut ||\n closeResult !== undefined ||\n !options.idleTimeoutMs\n )\n return;\n idleTimer = setTimeout(\n () => terminateTree(\"timeout\"),\n options.idleTimeoutMs,\n );\n };\n const forwardOutput = (chunk: Buffer | string): void => {\n if (settled || String(chunk).length === 0) return;\n output = appendTail(output, chunk, outputLimit);\n resetIdleTimer();\n // Output observers cannot take ownership away from process cleanup.\n try {\n options.onOutput?.(String(chunk));\n } catch {}\n };\n resetIdleTimer();\n\n child.stdout?.on(\"data\", (chunk) => {\n stdout = appendTail(stdout, chunk, outputLimit);\n forwardOutput(chunk);\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr = appendTail(stderr, chunk, outputLimit);\n forwardOutput(chunk);\n });\n\n child.on(\"error\", (error) => {\n spawnError = error;\n });\n child.on(\"close\", (exitCode) => {\n if (idleTimer) clearTimeout(idleTimer);\n closeResult = exitCode;\n finishIfComplete();\n });\n });\n}\n","/**\n * AppDevServer — supervises the user's REAL app dev server (the command\n * configured as `apps[0].start` in `tempo.config.json`), so route\n * storyboards (`TempoRouteStoryboard`) can load real app pages inside the\n * canvas iframe.\n *\n * This is intentionally a separate, much-smaller supervisor than the canvas\n * sidecar's `TempoDevServer`. It does NOT manage generated files, reconcile\n * loops, or `.tempo/` artifacts — those belong to the canvas host. It only:\n *\n * 1. Allocates a free TCP port per workspace (probe-and-claim, persisted).\n * 2. Substitutes optional `${APP_PORT}` (or legacy `${PORT}`) tokens and\n * spawns the command from the user's project root (not `tempo/`).\n * 3. Resolves a static URL, executes an explicit URL command once, or\n * watches stdout for a local URL, then HTTP-gates readiness.\n * 4. Uses the child as liveness authority for foreground commands, or the\n * URL after a clean launcher handoff backed by an explicit stop command.\n * 5. Attempts the configured stop for the current run without making a\n * future start prove that an old local or remote URL disappeared.\n *\n * Per-workspace isolation is the caller's responsibility — instantiate one\n * `AppDevServer` per workspace. Warm retention, leases, idle expiry, and LRU\n * capacity belong to `DevServerPool`, not this process-level supervisor.\n */\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport {\n type ChildProcess,\n execFile,\n spawn,\n spawnSync,\n} from \"node:child_process\";\nimport net from \"node:net\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { abortableWait } from \"@modules/abortable-wait\";\n\nimport {\n applyBundledNodeEnv,\n getBundledNodeBinDir,\n probeNodeVersion,\n removeDirFromPathEnv,\n shouldUseBundledNode,\n} from \"./bundled-node\";\nimport { devLog } from \"./debug-log\";\nimport {\n DEV_LOCK_CONFLICT_REAP_MAX_AGE_MS,\n type DevServerLockConflict,\n parseDevServerLockConflict,\n} from \"./dev-lock-conflict\";\nimport { classifyDevServerOutput } from \"./hint-classifier\";\nimport {\n extractLeadingEnvAssignments,\n findPowerShellSyntax,\n} from \"./shell-dialect\";\nimport { findPortListenerPid } from \"./port-listener\";\nimport {\n type RotatingLogReadResult,\n RotatingFileLog,\n} from \"./rotating-file-log\";\nimport {\n DEVSERVER_WATCHDOG_FILE_NAME,\n DEVSERVER_WATCHDOG_SOURCE,\n} from \"./devserver-watchdog-source\";\nimport { atomicWriteText } from \"./atomic-write\";\nimport {\n type AbortableShellCommandResult,\n runAbortableShellCommand,\n} from \"./abortable-shell-command\";\nimport { resolveHttpUrlSource } from \"./http-url-source\";\nimport type { HttpUrlSource, TempoHostLivenessSource } from \"./types\";\n\nexport const APP_DEVSERVER_STATE_FILENAME = \"app-devserver.json\";\n\n/**\n * Filename under `<workspaceRoot>/.tempo/` where the supervisor mirrors\n * the child's recent stdout/stderr (up to 8 MB — see\n * `APP_DEVSERVER_LOG_MAX_BYTES_PER_FILE`). Lets future bug reports include the\n * supervisor's real output even when the renderer is running a stale\n * build (the in-memory `recentOutputTail` array can be out of sync\n * with what the user actually sees if the broadcast logic was added\n * after their build). Users can `tail -F` this directly so their terminal\n * follows the active filename across rotation.\n */\nexport const APP_DEVSERVER_LOG_FILENAME = \"app-devserver.log\";\n\n/**\n * Size cap for the on-disk output mirror: two files of at most 4 MB each,\n * for an 8 MB maximum per workspace. Without a cap, a chatty or\n * error-looping dev server (e.g. a Next.js rebuild loop) grows the\n * file without bound for the life of the session, and the file\n * survives app relaunches until the next start cycle truncates it.\n * 4 MB (vs the 1 MB the NDJSON lifecycle logs use) because raw\n * dev-server output is bulky and the diagnostic value is in the\n * recent tail.\n */\nexport const APP_DEVSERVER_LOG_MAX_BYTES_PER_FILE = 4_194_304;\n\n/** Reasonable, unprivileged port range — wide enough to never run out. */\nconst DEFAULT_PORT_RANGE: PortRange = { min: 40000, max: 60000 };\n\n/**\n * How long to wait for the user's app to print a URL after spawn. A\n * healthy `npm run dev` / `next dev` / `vite` will print its first\n * line within 1–3 seconds — npm always prints the script-invocation\n * banner immediately, before the framework even starts. If 30 s pass\n * with ZERO output, something is wrong at the spawn / shell layer\n * (wrong cwd, missing deps, the command silently exits, etc.) — much\n * better to surface an error fast than to leave the user staring at\n * \"Starting your app dev server…\" indefinitely.\n *\n * Prior attempts at 180 s were a misdiagnosis — the right fix for\n * \"cold compile takes a long time\" is HTTP-readiness budget (below),\n * not URL-scrape budget. The URL itself appears fast.\n */\nconst DEFAULT_URL_SCRAPE_TIMEOUT_MS = 30_000;\n\n/**\n * Maximum tolerated NO-PROGRESS stall during the HTTP readiness gate, NOT a\n * hard total deadline. This is where Next.js + Turbopack cold-compile time\n * lives — the server prints \"Local: …\" within seconds but doesn't actually\n * answer HTTP until it's bound the port + compiled the first route (routinely\n * 30–60 s+ on a non-trivial app, and far worse on a slow/OneDrive-backed disk).\n *\n * The gate keeps waiting as long as the server is making progress — streaming\n * compile output, or holding our probe open mid-compile — and only gives up\n * after this much continuous quiet. Callers pass a per-framework value (see\n * `resolveDevserverReadyTimeoutMs`); this is the framework-agnostic fallback.\n */\nconst DEFAULT_HTTP_READY_TIMEOUT_MS = 180_000;\n\n/**\n * Per-attempt HTTP probe timeout. Generous on purpose: a framework compiling a\n * route on-demand HOLDS the request open until the cold compile finishes, so a\n * sub-second abort never observes the eventual 200 and — worse — re-pokes the\n * compiler every cycle. Riding a single patient request through the compile is\n * the core fix for \"did not become reachable in time\" on slow machines. Mirrors\n * tempo-api's cloud probe (`checkDevServerHealth`: 5 s).\n */\nconst HTTP_PROBE_ATTEMPT_TIMEOUT_MS = 5_000;\n\n/**\n * Absolute ceiling regardless of progress, so a server that stays alive and\n * chatty but never actually serves can't hang the start forever. Matches\n * tempo-api's 10-min \"all apps\" cap.\n */\nconst HTTP_PROBE_ABSOLUTE_CAP_MS = 600_000;\n\n/** Hostname we always emit in the public URL (0.0.0.0 → 127.0.0.1, etc.). */\nconst PUBLIC_HOSTNAME = \"127.0.0.1\";\n\nexport interface PortRange {\n min: number;\n max: number;\n}\n\nexport type AppDevServerPhase =\n | \"idle\"\n | \"starting\"\n | \"running\"\n | \"error\"\n | \"stopped\";\n\nexport type AppDevServerReconciliationVerdict =\n | \"nothing_to_reconcile\"\n | \"live_sibling\"\n | \"foreign_process\"\n | \"orphan_reaped\"\n | \"orphan_cleanup_failed\";\n\nexport interface AppDevServerStatus {\n phase: AppDevServerPhase;\n /** Allocated port for this workspace, when one has been chosen. */\n port: number | null;\n /** Public URL the renderer should load route storyboards from. */\n url: string | null;\n /** Most recent error message, when `phase === \"error\"`. */\n error: string | null;\n /** Process-backed for foreground launches; probe-backed after handoff. */\n livenessSource?: TempoHostLivenessSource | null;\n /** Tail of stdout/stderr for diagnostics (last lines, truncated). */\n recentOutputTail?: string;\n}\n\nexport interface AppDevServerOptions {\n /**\n * Directory the spawned command runs in. For the common case (app at the\n * project root) this is the project root. For monorepos where the app\n * lives in a subdirectory (`tempo.config.json` → `appDir: \"apps/web\"`),\n * the caller should pre-resolve `projectRoot + appDir` and pass it here\n * so the user's `package.json` / lockfile is in scope when the command\n * runs (`npm run dev` looks up `dev` in the cwd's package.json). The\n * supervisor itself does no app-root resolution — that's\n * `tempo-project`'s job (see `resolveAppRoot` in `initialize.ts`).\n * NOT the `tempo/` sidecar root.\n */\n cwd: string;\n /** Workspace root (worktree path). Persistent state for this workspace\n * lives under `<workspaceRoot>/.tempo/app-devserver.json`. May equal\n * `cwd` when there is no separate workspace root. */\n workspaceRoot: string;\n /** The app's command from `tempo.config.json` `apps[0].start`. */\n command: string;\n /** Matching teardown for a daemonized/containerized/remote launcher. */\n stop?: string;\n /**\n * Optional readiness and route-storyboard URL from `apps[0].url`.\n * `${APP_PORT}` (and legacy `${PORT}`) is substituted with the selected\n * local app port, but a literal URL is otherwise opaque: its host and port\n * do not affect allocation.\n */\n url?: HttpUrlSource;\n /** Override the URL-scrape timeout. Defaults to 30s. */\n urlScrapeTimeoutMs?: number;\n /**\n * Override the HTTP-readiness budget — the maximum tolerated NO-PROGRESS\n * stall (not a hard total deadline). Callers pass a per-framework value\n * (`resolveDevserverReadyTimeoutMs`). Defaults to 180s.\n */\n httpReadyTimeoutMs?: number;\n /** Test/operator override for command-backed URL resolution. */\n urlCommandTimeoutMs?: number;\n /** Test-only override for post-ready probe cadence. */\n urlHealthProbeIntervalMs?: number;\n /** Test-only override for consecutive probe failures before demotion. */\n urlHealthFailuresToDemote?: number;\n /** Override the port range. Defaults to 40000-60000. */\n portRange?: PortRange;\n /**\n * Identifies the Electron app channel and manager process that owns any\n * child recorded in the durable state file. When supplied, orphan cleanup\n * refuses to kill a recorded child while its owning manager is still alive.\n */\n ownership?: {\n ownerId: string;\n managerPid: number;\n managerStartedAt: string | null;\n };\n}\n\nconst PERSISTED_STATE_VERSION = 7;\n\ninterface PersistedState {\n version: number;\n port: number;\n allocatedAt: number;\n /**\n * PID of the spawned child (`detached: true` makes it its own process-\n * group leader, so pgid === pid). Written right after spawn, cleared on\n * clean teardown. If the Tempo main process dies hard (dev-mode restart,\n * crash, SIGKILL) the quit handlers never run and the child survives as\n * an orphan still holding `port` — this field is how the NEXT session\n * identifies and reaps that orphan instead of colliding with\n * EADDRINUSE. `null` = no live child recorded.\n */\n pid: number | null;\n /** Process-birth identity paired with pid. Absent in pre-v4 files, which\n * deliberately makes those records ineligible for destructive cleanup. */\n pidStartedAt: string | null;\n /**\n * PID of the process actually LISTENING on `port`, resolved via\n * netstat/lsof once the server reported ready. On win32 this is the real\n * `next dev` grandchild, NOT `pid` (the shell wrapper) — and it's the only\n * durable handle on the tree once the wrapper exits: `pid` is cleared by\n * the exit handler the moment the shell dies, while the grandchild (which\n * holds the port AND Next.js 16's per-directory dev lock) can survive it.\n * Cleared after the current stop attempt, or overwritten by the next ready\n * cycle. Absent in version-2 files.\n */\n serverPid: number | null;\n /** Process-birth identity paired with serverPid; see pidStartedAt. */\n serverPidStartedAt: string | null;\n /** Stable hash of this app channel's userData registry path. */\n ownerId: string | null;\n /** Electron main process which launched the recorded child. */\n managerPid: number | null;\n /** Process-birth identity paired with managerPid. */\n managerStartedAt: string | null;\n}\n\ninterface ReadyResult {\n ok: boolean;\n url: string | null;\n error: string | null;\n}\n\ntype Listener = (status: AppDevServerStatus) => void;\nexport interface AppDevServerExit {\n code: number | null;\n signal: NodeJS.Signals | null;\n error: string;\n}\n\ntype ExitListener = (exit: AppDevServerExit) => void;\n\nconst ANSI_ESCAPE_RE = /\\x1b\\[[0-9;]*m/g;\nconst URL_RE = /https?:\\/\\/[^\\s\"'`<>]+/gi;\nconst LOCAL_HOSTNAMES = new Set([\n \"127.0.0.1\",\n \"localhost\",\n \"::1\",\n \"[::1]\",\n \"0.0.0.0\",\n \"::\",\n]);\n\nexport class AppDevServer {\n private readonly options: Required<\n Pick<AppDevServerOptions, \"command\" | \"cwd\" | \"workspaceRoot\">\n > & {\n stop: string | undefined;\n url: HttpUrlSource | undefined;\n urlScrapeTimeoutMs: number;\n httpReadyTimeoutMs: number;\n urlCommandTimeoutMs: number;\n urlHealthProbeIntervalMs: number;\n urlHealthFailuresToDemote: number;\n portRange: PortRange;\n ownership: AppDevServerOptions[\"ownership\"];\n };\n\n private status: AppDevServerStatus = {\n phase: \"idle\",\n port: null,\n url: null,\n error: null,\n };\n\n private child: ChildProcess | null = null;\n private launcherFinished = false;\n private commandEnv: NodeJS.ProcessEnv | null = null;\n private urlHealthProbeTimer: ReturnType<typeof setInterval> | null = null;\n private urlHealthProbeUrl: string | null = null;\n private urlHealthProbeConsecutiveFailures = 0;\n private urlHealthProbeInFlight = false;\n private readonly outputLog: RotatingFileLog;\n /** Invalidates late stdout/stderr/exit events from an earlier start cycle. */\n private outputEpoch = 0;\n private recentOutput: string[] = [];\n private static readonly MAX_RECENT_LINES = 30;\n /** Epoch ms of the most recent child stdout/stderr chunk. The HTTP\n * readiness gate treats fresh output as liveness/progress, so a slow but\n * visibly-compiling dev server is never timed out mid-compile. */\n private lastOutputAt = 0;\n\n private startPromise: Promise<ReadyResult> | null = null;\n private stopPromise: Promise<void> | null = null;\n\n /** Serializes every state-file mutation. The child `exit` handler\n * fire-and-forgets a read-modify-write of the same file that\n * `runStopCycle` awaits its own writes on — without this chain the\n * two interleave, losing updates or leaving a reader a truncated\n * file mid-`writeFile`. */\n private stateFileChain: Promise<unknown> = Promise.resolve();\n\n private intentionalStop = false;\n private detectedBaseUrl: string | null = null;\n /**\n * Dev-lock conflict parsed from a POST-ready crash of the previous child\n * (Next 16.2 goes \"✓ Ready\" before its per-directory lock check, so the\n * \"Another next dev server is already running\" abort arrives after the\n * start cycle has resolved — TEM-1491). Consumed (once, age-gated) by the\n * next runStartCycle, which reaps the named holder before spawning.\n *\n * Post-ready exits ONLY: a pre-ready conflict is reaped by the start\n * cycle's own conflict branch, so recording it here too would re-kill an\n * already-dead (possibly recycled) pid on a later start. The timestamp\n * bounds the record's life — a stale conflict is dropped, because a lock\n * that is genuinely still held reproduces a fresh banner on the next spawn.\n */\n private pendingLockConflict: {\n conflict: DevServerLockConflict;\n at: number;\n } | null = null;\n\n private listeners: Listener[] = [];\n private outputListeners: Array<(data: string) => void> = [];\n private exitListeners: ExitListener[] = [];\n\n /**\n * Debounce timer for the \"output arrived, push status\" emit. setStatus\n * was previously only called on phase transitions, so during the (up\n * to 180 s) starting window the renderer never saw the live stdout\n * accumulating in `recentOutput`. Bug bundle\n * `tempo-bug-report-2026-06-10T17-04-16-441.zip` showed exactly this:\n * `phase: \"starting\"`, `recentOutputTail: \"\"`. The debounce keeps the\n * IPC chatter bounded (~2 pushes/sec max) while still showing the\n * user that something is happening.\n */\n private outputBroadcastTimer: ReturnType<typeof setTimeout> | null = null;\n private static readonly OUTPUT_BROADCAST_DEBOUNCE_MS = 500;\n private hasParentDeathWatchdog = false;\n\n constructor(options: AppDevServerOptions) {\n this.options = {\n command: options.command,\n stop: options.stop,\n url: options.url,\n cwd: path.resolve(options.cwd),\n workspaceRoot: path.resolve(options.workspaceRoot),\n urlScrapeTimeoutMs: resolveUrlScrapeTimeout(options.urlScrapeTimeoutMs),\n httpReadyTimeoutMs: resolveHttpReadyTimeout(options.httpReadyTimeoutMs),\n urlCommandTimeoutMs: options.urlCommandTimeoutMs ?? 30_000,\n urlHealthProbeIntervalMs: options.urlHealthProbeIntervalMs ?? 5_000,\n urlHealthFailuresToDemote: options.urlHealthFailuresToDemote ?? 3,\n portRange: options.portRange ?? DEFAULT_PORT_RANGE,\n ownership: options.ownership,\n };\n this.outputLog = new RotatingFileLog({\n filePath: this.getLogFilePath(),\n maxBytesPerFile: APP_DEVSERVER_LOG_MAX_BYTES_PER_FILE,\n });\n }\n\n getStatus(): AppDevServerStatus {\n return {\n ...this.status,\n livenessSource:\n this.status.phase === \"running\"\n ? this.launcherFinished\n ? \"probe\"\n : \"child\"\n : null,\n recentOutputTail: this.recentOutput.join(\"\").slice(-2000),\n };\n }\n\n /** Snapshot retained output without racing this supervisor's writer. */\n readOutputLogTail(tailBytes: number): Promise<RotatingLogReadResult> {\n return this.outputLog.readTail(tailBytes);\n }\n\n /**\n * Resolve the workspace's stable app port WITHOUT touching the child\n * process. While the supervisor is starting/running this returns the\n * live port — calling `allocatePort()` in that window would be wrong,\n * because its orphan-reap branch treats the recorded child pid as a\n * leftover and kills it. Otherwise this runs the same probe-and-claim\n * path `start()` uses, so a terminal run launched from the header's\n * Run button lands on the exact port route storyboards expect.\n */\n async ensurePort(): Promise<number> {\n if (\n this.status.port != null &&\n (this.status.phase === \"starting\" || this.status.phase === \"running\")\n ) {\n return this.status.port;\n }\n return await this.allocatePort();\n }\n\n /**\n * Reconcile a process named by the durable ownership registry without ever\n * allocating a replacement port or overwriting the old evidence.\n *\n * `true` is deliberately a proof result, not \"cleanup was attempted\": at\n * least one persisted pid + birth identity must exist, every such process\n * must be gone after the proof-gated reap, and an occupied recorded port\n * must be attributable to an identifiable foreign process. Missing/corrupt\n * Missing state and live/foreign ownership are distinct from a confirmed\n * orphan whose identity-proven cleanup failed.\n */\n async reconcilePersistedProcess(): Promise<AppDevServerReconciliationVerdict> {\n const persisted = await this.readPersistedState();\n if (!persisted) return \"nothing_to_reconcile\";\n\n const ownership = await this.classifyPersistedProcessOwnership(persisted);\n if (ownership !== \"reapable\") {\n return ownership;\n }\n const identities = [\n [persisted.pid, persisted.pidStartedAt, \"shell\"] as const,\n [persisted.serverPid, persisted.serverPidStartedAt, \"server\"] as const,\n ];\n const hadProcessIdentity = identities.some(\n ([pid, startedAt]) => pid != null && startedAt != null,\n );\n if (!hadProcessIdentity) {\n return \"nothing_to_reconcile\";\n }\n\n if (\n persisted.pid != null &&\n (await pidStartIdentityMatches(persisted.pid, persisted.pidStartedAt))\n ) {\n const outcome = await reapOwnedProcessGroup(persisted.pid);\n devLog(\"app-devserver:registry-orphan-group-reap\", {\n pid: persisted.pid,\n port: persisted.port,\n outcome,\n workspaceRoot: this.options.workspaceRoot,\n });\n }\n\n if (\n persisted.serverPid != null &&\n (await pidStartIdentityMatches(\n persisted.serverPid,\n persisted.serverPidStartedAt,\n ))\n ) {\n const outcome = await reapConflictingDevServer(persisted.serverPid);\n devLog(\"app-devserver:registry-orphan-server-reap\", {\n serverPid: persisted.serverPid,\n port: persisted.port,\n outcome,\n workspaceRoot: this.options.workspaceRoot,\n });\n }\n\n for (const [pid, startedAt] of identities) {\n if (pid != null && (await pidStartIdentityMatches(pid, startedAt))) {\n return \"orphan_cleanup_failed\";\n }\n }\n\n if (!(await isPortFree(persisted.port))) {\n const listenerPid = await findPortListenerPid(persisted.port);\n if (listenerPid == null || persisted.serverPidStartedAt == null) {\n return \"foreign_process\";\n }\n // Matching pid with a different birth identity is PID reuse and thus a\n // foreign listener; a different identifiable pid is foreign as well.\n if (\n listenerPid === persisted.serverPid &&\n (await pidStartIdentityMatches(\n listenerPid,\n persisted.serverPidStartedAt,\n ))\n ) {\n return \"orphan_cleanup_failed\";\n }\n }\n\n await this.persistChildPid(null);\n await this.persistServerPid(null);\n return \"orphan_reaped\";\n }\n\n onStatusChange(listener: Listener): () => void {\n this.listeners.push(listener);\n return () => {\n this.listeners = this.listeners.filter((l) => l !== listener);\n };\n }\n\n onOutput(listener: (data: string) => void): () => void {\n this.outputListeners.push(listener);\n return () => {\n this.outputListeners = this.outputListeners.filter((l) => l !== listener);\n };\n }\n\n /** Observe post-ready unexpected exits; manager-owned stops do not emit. */\n onExit(listener: ExitListener): () => void {\n this.exitListeners.push(listener);\n return () => {\n this.exitListeners = this.exitListeners.filter((l) => l !== listener);\n };\n }\n\n /**\n * Start (or attach to an in-flight start) the dev server. Idempotent —\n * concurrent callers share one start cycle.\n */\n async start(signal?: AbortSignal): Promise<ReadyResult> {\n throwIfAppLaunchAborted(signal);\n if (this.status.phase === \"running\" && this.status.url) {\n return { ok: true, url: this.status.url, error: null };\n }\n if (this.startPromise) {\n // A joining caller owns only its wait. Its cancellation must not tear\n // down launch work owned by the caller that created startPromise.\n return await waitForAppStart(this.startPromise, signal);\n }\n if (this.stopPromise) {\n await this.stopPromise.catch(() => {});\n }\n\n // Never overwrite a child retained after failed termination.\n this.startPromise = (async () => {\n if (this.child) await this.killChild();\n return await this.runStartCycle(0, signal);\n })().finally(() => {\n this.startPromise = null;\n });\n return this.startPromise;\n }\n\n /**\n * Stop the dev server gracefully (SIGTERM, then SIGKILL). Idempotent.\n * Does NOT clear the persisted port — a subsequent start reuses it.\n */\n async stop(): Promise<void> {\n if (this.status.phase === \"idle\" || this.status.phase === \"stopped\") {\n return;\n }\n if (this.stopPromise) return this.stopPromise;\n\n this.stopPromise = this.runStopCycle().finally(() => {\n this.stopPromise = null;\n });\n return this.stopPromise;\n }\n\n /**\n * Synchronous best-effort SIGKILL of the child process group. For the\n * Electron `will-quit` hard-shutdown path, which can't await `stop()`.\n * Doesn't wait for exit or update status — the process is dying with\n * the app.\n */\n forceKill(): void {\n this.intentionalStop = true;\n this.stopUrlHealthProbe();\n this.outputEpoch += 1;\n this.clearOutputBroadcastTimer();\n const child = this.child;\n if (!child) return;\n const pid = child.pid;\n if (!pid) return;\n try {\n if (process.platform !== \"win32\") {\n if (this.hasParentDeathWatchdog) {\n // destroy(), not end() — same rationale as the tempo-devserver\n // watchdog teardown: no uv_shutdown left racing the SIGTERM below.\n try {\n child.stdin?.destroy();\n } catch {}\n try {\n process.kill(-pid, \"SIGTERM\");\n this.child = null;\n return;\n } catch {}\n }\n try {\n process.kill(-pid, \"SIGKILL\");\n } catch {\n child.kill(\"SIGKILL\");\n }\n } else {\n // Tree-kill, synchronously — `child.kill()` would terminate only the\n // `cmd.exe` shell wrapper and orphan the actual dev server (the\n // grandchild `next dev`/`vite` the shell spawned), which then holds\n // both the port and Next.js 16's per-directory dev lock forever\n // (bundle t17b8rsd…: 19 lock conflicts in one session). `/T` walks\n // the child tree from the still-live wrapper. spawnSync because\n // `will-quit` can't await.\n spawnSync(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], {\n windowsHide: true,\n timeout: 3000,\n });\n }\n } catch {}\n this.child = null;\n }\n\n /**\n * Decide, for THIS start cycle, whether to route the app devserver through\n * the host's bundled Node toolchain. Returns the shim bin dir to apply, or\n * null to use the user's environment untouched.\n *\n * Presence-only (`requireFloor: false`): the app's own Node version\n * requirement is unknown, so a PRESENT system Node is always preferred —\n * deliberately independent of the canvas devserver's process-global\n * activation latch, which may have fired for the canvas's Vite 7 floor while\n * the user's Node still works fine for their app (never shadow a Node that\n * works for this surface). The probe env strips the shim dir the host\n * appended to the global PATH, so it measures the user's REAL Node, not the\n * shim. Re-evaluated on every start cycle, so installing Node mid-session is\n * picked up on the next app-devserver start.\n */\n private async resolveBundledNodeBinDirForStart(): Promise<string | null> {\n const binDir = getBundledNodeBinDir();\n if (!binDir) return null;\n const systemNodeVersion = await probeNodeVersion(\n removeDirFromPathEnv({ ...process.env }, binDir),\n );\n if (\n shouldUseBundledNode({\n systemNodeVersion,\n requireFloor: false,\n floorOk: systemNodeVersion != null,\n })\n ) {\n devLog(\"app-devserver:bundled_node_activated\", {\n cwd: this.options.cwd,\n systemNodeVersion,\n binDir,\n });\n return binDir;\n }\n return null;\n }\n\n /**\n * @param attempt Re-spawn counter for the Next.js 16 dev-lock recovery. The\n * first crash that turns out to be a \"Another next dev server is already\n * running\" abort attempts a proof-gated reap and re-enters with\n * `attempt + 1` only when ownership matched;\n * we allow exactly one such retry so a genuinely stuck directory surfaces\n * the error instead of looping.\n */\n private async runStartCycle(\n attempt = 0,\n signal?: AbortSignal,\n ): Promise<ReadyResult> {\n throwIfAppLaunchAborted(signal);\n const outputEpoch = ++this.outputEpoch;\n this.setStatus({ phase: \"starting\", error: null });\n this.intentionalStop = false;\n this.launcherFinished = false;\n this.commandEnv = null;\n this.stopUrlHealthProbe();\n this.detectedBaseUrl = null;\n this.recentOutput = [];\n let startSpawned = false;\n\n // Refuse a PowerShell-shaped command before spawning anything (TEM-436).\n // `set_app_dev_command` rejects these at author time, but a hand-edited\n // tempo.config.json bypasses that guard entirely; without this check\n // cmd.exe aborts while PARSING the command and the exit handler reports a\n // generic \"exited unexpectedly (code 1)\" crash. Checking the configured\n // string makes the failure deterministic on every platform.\n const dialectIssue = findPowerShellSyntax(this.options.command);\n if (dialectIssue) {\n const error = `The app dev command was rejected before spawn: it contains PowerShell-only syntax (${dialectIssue}), which the shell Tempo runs commands with (cmd.exe on Windows) cannot parse — nothing was started. Rewrite \"apps[0].start\" in tempo.config.json as a single plain cross-platform command — use \\${APP_PORT} only if it needs Tempo's allocated helper port — and if the app lives in a subdirectory set \"appDir\" instead of cd/pushd-ing inside the command (chaining with \\`&&\\` is fine; cmd supports it).`;\n devLog(\"app-devserver:shell-dialect-rejected\", {\n command: this.options.command,\n construct: dialectIssue,\n });\n this.setStatus({ phase: \"error\", error, url: null });\n return { ok: false, url: null, error };\n }\n const stopDialectIssue = this.options.stop\n ? findPowerShellSyntax(this.options.stop)\n : null;\n if (stopDialectIssue) {\n const error = `The app stop command was rejected before spawn: it contains PowerShell-only syntax (${stopDialectIssue}). Rewrite \"apps[0].stop\" as one plain cross-platform command.`;\n this.setStatus({ phase: \"error\", error, url: null });\n return { ok: false, url: null, error };\n }\n const urlCommand =\n typeof this.options.url === \"object\" ? this.options.url.command : null;\n const urlDialectIssue = urlCommand\n ? findPowerShellSyntax(urlCommand)\n : null;\n if (urlDialectIssue) {\n const error = `The app URL command was rejected before spawn: it contains PowerShell-only syntax (${urlDialectIssue}). Rewrite \"apps[0].url.command\" as one plain cross-platform command.`;\n this.setStatus({ phase: \"error\", error, url: null });\n return { ok: false, url: null, error };\n }\n\n // Truncate the disk log at the start of each cycle so a fresh run\n // doesn't accumulate output from prior crashes. Best-effort.\n await this.truncateLogFile();\n throwIfAppLaunchAborted(signal);\n\n let removeAbortListener = () => {};\n\n try {\n // A PREVIOUS run of this server may have died to a Next.js 16 dev-lock\n // conflict AFTER it went ready (Next 16.2 prints \"✓ Ready\" and only then\n // aborts on the per-directory lock — TEM-1491). That exit never reaches\n // the pre-ready conflict branch below, so the holder it named would\n // survive into this start and kill it the same way. Reap it first —\n // once, and only while fresh: a stale record's pid/port may belong to a\n // recycled, unrelated process by now, and a lock that is genuinely\n // still held reproduces a fresh banner on this very spawn. The reap is\n // still proof-gated against our persisted pid + birth identity; banner\n // text alone is never kill authority.\n const pending = this.pendingLockConflict;\n if (pending != null && attempt === 0) {\n this.pendingLockConflict = null;\n if (Date.now() - pending.at <= DEV_LOCK_CONFLICT_REAP_MAX_AGE_MS) {\n await this.reapLockConflictHolder(pending.conflict, attempt);\n }\n }\n throwIfAppLaunchAborted(signal);\n\n // FALLBACK-ONLY: if the user has no system Node at all, route this app\n // devserver through the host's bundled Node toolchain (same mechanism as\n // the canvas devserver). Presence-only here — the app's own Node floor is\n // unknown, so a present Node is never shadowed.\n const bundledNodeBinDir = await this.resolveBundledNodeBinDirForStart();\n throwIfAppLaunchAborted(signal);\n\n const port = await this.allocatePort();\n throwIfAppLaunchAborted(signal);\n this.setStatus({ port });\n\n // Lift leading `NAME=value` prefixes (the documented CRA/Remix shape\n // `PORT=${APP_PORT} npm run dev`, legacy `TEMPO=true …` commands) into\n // the spawn env AFTER app-port substitution, so values are literals.\n // POSIX sh would apply the prefix itself; cmd.exe has no such syntax\n // and dies parsing it (\"'PORT=3001' is not recognized…\"), which made\n // the only natural appStart for env-driven frameworks a guaranteed\n // Windows failure (TEM-436 round 2).\n const lifted = extractLeadingEnvAssignments(\n this.substitutePort(this.options.command, port),\n );\n const command = lifted.command;\n const env = buildAppCommandEnv(port);\n // Lifted assignments land LAST — after the defaults and the leakage\n // strip — matching sh, where a command-line prefix beats everything in\n // the inherited environment.\n Object.assign(env, lifted.env);\n this.commandEnv = env;\n\n devLog(\"app-devserver:spawn\", {\n command,\n configuredUrl: this.options.url ?? null,\n liftedEnvKeys: Object.keys(lifted.env),\n cwd: this.options.cwd,\n port,\n });\n\n const commandEnv = bundledNodeBinDir\n ? applyBundledNodeEnv(env, bundledNodeBinDir)\n : env;\n const watchdogPath = await this.ensureParentDeathWatchdog();\n throwIfAppLaunchAborted(signal);\n this.hasParentDeathWatchdog = watchdogPath !== null;\n this.child = watchdogPath\n ? spawn(process.execPath, [watchdogPath, command], {\n cwd: this.options.cwd,\n detached: process.platform !== \"win32\",\n // stdin is the parent-death signal. Child stdout/stderr pass\n // through the watchdog unchanged. IPC carries the real managed\n // process-group pid back without contaminating server output.\n stdio: [\"pipe\", \"pipe\", \"pipe\", \"ipc\"],\n windowsHide: true,\n env: {\n ...commandEnv,\n TEMPO_WATCHDOG_CHILD_ELECTRON_RUN_AS_NODE:\n commandEnv.ELECTRON_RUN_AS_NODE ?? \"__TEMPO_UNSET__\",\n ELECTRON_RUN_AS_NODE: \"1\",\n },\n })\n : spawn(command, {\n cwd: this.options.cwd,\n shell: true,\n detached: process.platform !== \"win32\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n // Suppress the visible cmd.exe window on Windows — users close it,\n // tearing the devserver down with 0xC000013A. No-op off Windows.\n windowsHide: true,\n env: commandEnv,\n });\n startSpawned = true;\n\n this.wireProcessEvents(outputEpoch);\n const onAbort = () => {\n // killChild settles only after the spawned tree exits (or the bounded\n // escalation completes), so cancellation cannot release the pool\n // while this incarnation is still doing launch work.\n void this.killChild().catch(() => {});\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n removeAbortListener = () => signal?.removeEventListener(\"abort\", onAbort);\n // AbortSignal does not replay an abort that landed before registration.\n if (signal?.aborted) onAbort();\n\n // Record the live child's pid (its process-group id — detached\n // spawn makes it the group leader). If this Tempo session dies\n // hard without running quit handlers, the NEXT session reads this\n // pid back and reaps the orphaned group instead of colliding with\n // EADDRINUSE on the persisted port. Cleared by the current stop attempt.\n const managedRootPid = watchdogPath\n ? await waitForWatchdogChildPid(this.child)\n : (this.child.pid ?? null);\n throwIfAppLaunchAborted(signal);\n await this.persistChildPid(managedRootPid);\n\n const url = await this.waitForReady(port, signal);\n if (!url) {\n // Two reasons waitForReady can return null:\n //\n // 1. The child died before we ever saw a URL (or before HTTP\n // came up). In that case the `child.on(\"exit\")` handler\n // has already transitioned phase to \"error\" with the\n // exit code + tail in the message — leave it alone, it's\n // a strictly more informative signal than \"did not become\n // reachable\".\n // 2. Both deadlines elapsed while the child was still alive.\n // That's a real timeout, and we own the error message.\n //\n // Branching this way (rather than blindly overwriting) means a\n // user whose `appStart` crashes on launch sees \"App dev server\n // exited unexpectedly (code 1, signal null). <tail>\" instead\n // of a vague \"did not become reachable in time.\" Caught by\n // the e2e immediate-exit test.\n if (this.status.phase === \"error\") {\n // Next.js 16 per-directory dev-lock conflict: the child bound our\n // (free) port fine but aborted because a DIFFERENT `next dev` for\n // this directory — typically an orphan from a hard-killed prior\n // session — still holds the lock. That orphan is on another port,\n // so allocatePort()'s port-based reap can't see it; the banner's\n // PID is only a candidate; persisted pid + birth identity must\n // independently prove ownership before reaping and re-spawning.\n // Retry at most twice because a second conflict can name a different\n // proven Tempo-owned holder. (bundles\n // t17dsrf95f4r46daj9b74eczd988yy1w, t17b8rsd7f76zz8pqqdn2acnh589m1s6)\n const conflict =\n attempt < 2\n ? parseDevServerLockConflict(this.recentOutput.join(\"\"))\n : null;\n if (\n conflict != null &&\n (await this.reapLockConflictHolder(conflict, attempt))\n ) {\n // Lock released — re-spawn. allocatePort() reuses the same (now\n // free) port; the crash already cleared our recorded child pid.\n removeAbortListener();\n removeAbortListener = () => {};\n return await this.runStartCycle(attempt + 1, signal);\n }\n const startError = this.status.error;\n const cleanupError = await this.cleanupFailedLaunch(\n port,\n startSpawned,\n );\n const finalError = cleanupError\n ? `${startError ?? \"App dev server failed to start.\"}\\n\\nCleanup also failed: ${cleanupError}`\n : startError;\n this.setStatus({ phase: \"error\", error: finalError, url: null });\n return {\n ok: false,\n url: null,\n error: finalError,\n };\n }\n const error = \"App dev server did not become reachable in time.\";\n const cleanupError = await this.cleanupFailedLaunch(port, startSpawned);\n const finalError = cleanupError\n ? `${error}\\n\\nCleanup also failed: ${cleanupError}`\n : error;\n this.setStatus({ phase: \"error\", error: finalError, url: null });\n return { ok: false, url: null, error: finalError };\n }\n\n throwIfAppLaunchAborted(signal);\n this.setStatus({ phase: \"running\", url, error: null });\n if (this.launcherFinished) this.startUrlHealthProbe(url);\n\n // Resolve the pid actually LISTENING on the port, but persist it only\n // after proving membership in the tree we spawned. A Docker-published\n // port reports the host networking daemon here, which must remain\n // diagnostic-only rather than becoming future kill authority.\n const serverPid = await findPortListenerPid(port);\n throwIfAppLaunchAborted(signal);\n const persisted = await this.readPersistedState();\n throwIfAppLaunchAborted(signal);\n const shellPid = persisted?.pid ?? this.child?.pid ?? null;\n const ownedServerPid =\n serverPid != null &&\n shellPid != null &&\n (await pidBelongsToSpawnedTree(shellPid, serverPid))\n ? serverPid\n : null;\n await this.persistServerPid(ownedServerPid);\n throwIfAppLaunchAborted(signal);\n devLog(\"app-devserver:server-pid-resolved\", {\n port,\n serverPid,\n ownedServerPid,\n shellPid,\n });\n\n return { ok: true, url, error: null };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const cleanupError = await this.cleanupFailedLaunch(\n this.status.port,\n startSpawned,\n );\n const finalError = cleanupError\n ? `${message}\\n\\nCleanup also failed: ${cleanupError}`\n : message;\n this.setStatus({ phase: \"error\", error: finalError });\n return { ok: false, url: null, error: finalError };\n } finally {\n removeAbortListener();\n }\n }\n\n /** Undo resources created by this launch attempt, without carrying cleanup\n * authority into a later session. Every new start gets a fresh attempt. */\n private async cleanupFailedLaunch(\n port: number | null,\n startWasSpawned: boolean,\n ): Promise<string | null> {\n this.stopUrlHealthProbe();\n if (!startWasSpawned) {\n this.commandEnv = null;\n return null;\n }\n let cleanupError: string | null = null;\n if (this.options.stop && port != null) {\n const stopResult = await this.runBoundedCommand(\n this.options.stop,\n port,\n 10_000,\n );\n devLog(\"app-devserver:failed-launch-stop-command\", {\n command: this.options.stop,\n exitCode: stopResult.exitCode,\n aborted: stopResult.aborted,\n spawnError: stopResult.spawnError?.message ?? null,\n stderr: stopResult.stderr.slice(-1_000),\n workspaceRoot: this.options.workspaceRoot,\n });\n cleanupError = describeShellCommandFailure(stopResult);\n }\n try {\n await this.killChild();\n } catch (error) {\n return [\n cleanupError,\n error instanceof Error ? error.message : String(error),\n ]\n .filter(Boolean)\n .join(\"\\n\");\n }\n this.launcherFinished = false;\n this.commandEnv = null;\n return cleanupError;\n }\n\n private async runStopCycle(): Promise<void> {\n this.intentionalStop = true;\n this.stopUrlHealthProbe();\n this.clearOutputBroadcastTimer();\n const port = this.status.port;\n if (this.options.stop && port != null) {\n const stopResult = await this.runBoundedCommand(\n this.options.stop,\n port,\n 10_000,\n );\n devLog(\"app-devserver:stop-command\", {\n command: this.options.stop,\n exitCode: stopResult.exitCode,\n aborted: stopResult.aborted,\n spawnError: stopResult.spawnError?.message ?? null,\n stderr: stopResult.stderr.slice(-1_000),\n workspaceRoot: this.options.workspaceRoot,\n });\n }\n await this.killChild();\n // Reject any late stream events from the old child, then wait until every\n // chunk already accepted by the log writer has reached disk before a\n // subsequent start is allowed to reset the journal.\n this.outputEpoch += 1;\n await this.outputLog.flush();\n\n // Clear this run's identities only after the owned child exits. A remote\n // URL remaining reachable does not itself confer local kill authority.\n await this.persistChildPid(null);\n await this.persistServerPid(null);\n this.launcherFinished = false;\n this.commandEnv = null;\n this.setStatus({\n phase: \"stopped\",\n url: null,\n error: null,\n });\n }\n\n /**\n * Reap a Next lock holder only when the banner pid still matches a pid and\n * process-birth identity this supervisor persisted. Banner text and a raw\n * listener lookup are diagnostics, never independent kill authority.\n */\n private async reapLockConflictHolder(\n conflict: DevServerLockConflict,\n attempt: number,\n ): Promise<boolean> {\n const persisted = await this.readPersistedState();\n const identities = persisted\n ? [\n [persisted.pid, persisted.pidStartedAt] as const,\n [persisted.serverPid, persisted.serverPidStartedAt] as const,\n ]\n : [];\n const identityMatches = (\n await Promise.all(\n identities.map(async ([pid, startedAt]) =>\n pid === conflict.pid\n ? await pidStartIdentityMatches(pid, startedAt)\n : false,\n ),\n )\n ).some(Boolean);\n if (!identityMatches) {\n const listenerPid =\n conflict.port == null ? null : await findPortListenerPid(conflict.port);\n devLog(\"app-devserver:dev-lock-unowned\", {\n pid: conflict.pid,\n holderPort: conflict.port,\n listenerPid,\n attempt,\n workspaceRoot: this.options.workspaceRoot,\n });\n return false;\n }\n\n const outcome = await reapConflictingDevServer(conflict.pid);\n devLog(\"app-devserver:dev-lock-reap\", {\n pid: conflict.pid,\n holderPort: conflict.port,\n outcome,\n attempt,\n workspaceRoot: this.options.workspaceRoot,\n });\n if (conflict.port != null) {\n await waitForPortFree(conflict.port, 2500);\n }\n return true;\n }\n\n private wireProcessEvents(outputEpoch: number): void {\n const child = this.child;\n if (!child) return;\n\n const onData = (chunk: Buffer | string) => {\n if (outputEpoch !== this.outputEpoch) return;\n const text = String(chunk);\n this.lastOutputAt = Date.now();\n this.recentOutput.push(text);\n if (this.recentOutput.length > AppDevServer.MAX_RECENT_LINES) {\n this.recentOutput.shift();\n }\n // Persist to disk so a bug bundle captures the supervisor's retained\n // stdout/stderr even when the renderer is on a stale build that\n // doesn't have the in-memory streaming logic. Best-effort — a\n // failed write doesn't block process supervision. Users can also\n // `tail -F <workspaceRoot>/.tempo/app-devserver.log` directly.\n this.outputLog.append(text);\n for (const listener of this.outputListeners) {\n try {\n listener(text);\n } catch {}\n }\n // Debounced status broadcast so the renderer's `recentOutputTail`\n // updates while the supervisor is mid-starting. Without this,\n // listeners only see whatever output was captured at the LAST\n // phase transition — which was empty for `phase: \"starting\"`.\n this.scheduleOutputBroadcast();\n if (!this.detectedBaseUrl) {\n const scraped = extractServerUrl(text);\n if (scraped) {\n this.detectedBaseUrl = scraped;\n devLog(\"app-devserver:url_detected\", {\n pid: child.pid ?? null,\n baseUrl: scraped,\n });\n }\n }\n };\n\n child.stdout?.on(\"data\", onData);\n child.stderr?.on(\"data\", onData);\n\n child.on(\"error\", (err) => {\n devLog(\"app-devserver:spawn_error\", {\n pid: child.pid ?? null,\n message: err.message,\n });\n });\n\n child.on(\"exit\", (code, signal) => {\n const exitedAfterReady = this.status.phase === \"running\";\n devLog(\"app-devserver:exit\", {\n pid: child.pid ?? null,\n code,\n signal,\n intentional: this.intentionalStop,\n });\n if (outputEpoch !== this.outputEpoch || this.child !== child) return;\n this.child = null;\n // The recorded SHELL pid now points at a dead process — clear it so a\n // future session's reaper doesn't have to reason about it at all.\n // `serverPid` is intentionally KEPT: on win32 the listening grandchild\n // can outlive the shell (that survival is exactly the orphan case),\n // and this record is the only remaining handle on it. The current stop\n // attempt clears it; a subsequent start uses a fresh port if necessary.\n void this.persistChildPid(null);\n const cleanLauncherExit =\n !this.intentionalStop && code === 0 && signal == null;\n const acceptedLauncherHandoff =\n cleanLauncherExit &&\n this.options.stop != null &&\n (this.status.phase === \"starting\" || this.status.phase === \"running\");\n if (acceptedLauncherHandoff && this.options.stop) {\n this.launcherFinished = true;\n if (this.status.phase === \"running\" && this.status.url) {\n this.startUrlHealthProbe(this.status.url);\n }\n devLog(\"app-devserver:launcher-finished\", {\n pid: child.pid ?? null,\n afterReady: this.status.phase === \"running\",\n workspaceRoot: this.options.workspaceRoot,\n });\n return;\n }\n if (!this.intentionalStop && this.status.phase !== \"stopped\") {\n // Post-ready dev-lock abort (Next 16.2 prints \"✓ Ready\" first, then\n // exits 1 on the per-directory lock): the startup path's conflict\n // branch never sees this exit, so record the holder Next named here\n // and let the NEXT start cycle attempt a proof-gated reap (TEM-1491).\n // Gated on phase\n // \"running\" — this handler fires before the still-waiting start\n // cycle observes the death, so \"running\" is precisely \"the cycle\n // already resolved ok\". An in-cycle conflict (phase \"starting\") is\n // handled by that cycle's own conflict branch; recording it here too\n // would retain stale, possibly recycled pid diagnostics for a later\n // start.\n const conflict =\n this.status.phase === \"running\"\n ? parseDevServerLockConflict(this.recentOutput.join(\"\"))\n : null;\n if (conflict != null) {\n this.pendingLockConflict = { conflict, at: Date.now() };\n devLog(\"app-devserver:dev-lock-post-ready-conflict\", {\n pid: conflict.pid,\n holderPort: conflict.port,\n workspaceRoot: this.options.workspaceRoot,\n });\n }\n const tail = this.recentOutput.join(\"\").slice(-500);\n // Classify the tail so a recognizable failure (e.g. a cmd.exe-vs-\n // PowerShell shell-dialect mismatch — \"X was unexpected at this time\")\n // surfaces an actionable fix in `status.error`, which is what\n // `check_app_dev_server` / `start_app_dev_server` hand back to the\n // agent verbatim. Without this the agent only sees the raw stderr and\n // has to guess. See TEM-436.\n const hint = tail ? classifyDevServerOutput(tail) : null;\n const error =\n cleanLauncherExit && this.status.phase === \"starting\"\n ? 'The app start command exited successfully before the configured URL became ready. Add a matching non-empty \"apps[0].stop\" command so Tempo has teardown authority for the external server.'\n : `App dev server exited unexpectedly (code ${code ?? \"null\"}, signal ${signal ?? \"null\"}).${tail ? `\\n\\n${tail}` : \"\"}${hint ? `\\n\\nHint: ${hint.hint}` : \"\"}`;\n this.setStatus({\n phase: \"error\",\n error,\n url: null,\n });\n if (exitedAfterReady) {\n for (const listener of [...this.exitListeners]) {\n try {\n listener({ code, signal, error });\n } catch {}\n }\n }\n }\n });\n }\n\n private async waitForReady(\n port: number,\n signal?: AbortSignal,\n ): Promise<string | null> {\n const configuredUrl = await this.resolveConfiguredUrl(port, signal);\n if (!configuredUrl) {\n const scrapeDeadline = Date.now() + this.options.urlScrapeTimeoutMs;\n while (!this.detectedBaseUrl && Date.now() < scrapeDeadline) {\n throwIfAppLaunchAborted(signal);\n if (!this.hasLivenessAuthority()) return null;\n await sleepForAppLaunch(120, signal);\n }\n }\n // If the framework never printed a URL but the port is correct,\n // synthesize the public URL ourselves. This handles dev servers that\n // run silently (or print only a banner the regex misses).\n const baseUrl =\n configuredUrl ??\n this.detectedBaseUrl ??\n `http://${PUBLIC_HOSTNAME}:${port}`;\n const publicUrl = rewriteToPublicHost(\n baseUrl,\n port,\n configuredUrl === null,\n );\n\n // HTTP readiness gate. `httpReadyTimeoutMs` is the max tolerated\n // NO-PROGRESS stall, not a hard wall: as long as the server is making\n // progress — streaming compile output, or holding our probe open\n // mid-compile — we keep waiting past it, up to a hard absolute cap. This\n // is what lets a slow cold compile (46 s+ on a OneDrive-backed Windows\n // box, bundle t176psswt…) finish and be detected instead of being killed\n // with \"did not become reachable in time\" while it was visibly compiling.\n const start = Date.now();\n let lastProgressAt = start;\n while (true) {\n throwIfAppLaunchAborted(signal);\n if (!this.hasLivenessAuthority()) return null;\n\n const outcome = await probeHttpOnce(\n publicUrl,\n HTTP_PROBE_ATTEMPT_TIMEOUT_MS,\n signal,\n );\n throwIfAppLaunchAborted(signal);\n if (outcome === \"reachable\") return publicUrl;\n\n const now = Date.now();\n // A \"held\" probe (connection accepted but the server is busy) and fresh\n // stdout are both progress — the server is alive and working, so reset\n // the stall clock instead of counting this against the budget.\n if (outcome === \"held\") lastProgressAt = now;\n if (this.lastOutputAt > lastProgressAt)\n lastProgressAt = this.lastOutputAt;\n\n if (now - start >= HTTP_PROBE_ABSOLUTE_CAP_MS) return null;\n if (now - lastProgressAt >= this.options.httpReadyTimeoutMs) return null;\n\n await sleepForAppLaunch(httpProbeInterval(now - start), signal);\n }\n }\n\n private hasLivenessAuthority(): boolean {\n return this.launcherFinished || this.child !== null;\n }\n\n private async resolveConfiguredUrl(\n port: number,\n signal?: AbortSignal,\n ): Promise<string | null> {\n const source = this.options.url;\n if (!source) return null;\n\n // `stop + url.command` is the explicit launcher-handoff shape. The URL\n // getter often depends on provisioning completed by start (for example\n // `make remote-workspace` followed by `make show-workspace-port`), so\n // running both concurrently would make success scheduler-dependent.\n // Foreground processes omit stop and resolve their command immediately\n // after spawn; short-lived external launchers resolve only after their\n // clean exit has transferred liveness authority to the URL.\n if (typeof source === \"object\" && this.options.stop) {\n await this.waitForLauncherHandoff(signal);\n }\n\n try {\n return await resolveHttpUrlSource({\n source,\n label: \"apps[0].url\",\n cwd: this.options.cwd,\n env: this.commandEnv ?? buildAppCommandEnv(port),\n port,\n timeoutMs: this.options.urlCommandTimeoutMs,\n signal,\n substitutePort: substituteAppPort,\n });\n } catch (error) {\n throwIfAppLaunchAborted(signal);\n throw error;\n }\n }\n\n private async waitForLauncherHandoff(signal?: AbortSignal): Promise<void> {\n const deadline = Date.now() + HTTP_PROBE_ABSOLUTE_CAP_MS;\n while (!this.launcherFinished) {\n throwIfAppLaunchAborted(signal);\n if (!this.child) {\n throw new Error(\n this.status.error ??\n \"The app start command stopped before URL resolution could run.\",\n );\n }\n if (Date.now() >= deadline) {\n throw new Error(\n \"Timed out waiting for the app start command to hand off before running apps[0].url.command\",\n );\n }\n await sleepForAppLaunch(120, signal);\n }\n }\n\n private async runBoundedCommand(\n command: string,\n port: number,\n timeoutMs: number,\n signal?: AbortSignal,\n cwd = this.options.cwd,\n ) {\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await runAbortableShellCommand({\n command: substituteAppPort(command, port),\n cwd,\n env: this.commandEnv ?? buildAppCommandEnv(port),\n signal: controller.signal,\n maxOutputBytes: 4_000,\n });\n } finally {\n clearTimeout(timeout);\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n private startUrlHealthProbe(url: string): void {\n this.stopUrlHealthProbe();\n this.urlHealthProbeUrl = url;\n this.urlHealthProbeTimer = setInterval(() => {\n void this.runUrlHealthProbeTick();\n }, this.options.urlHealthProbeIntervalMs);\n this.urlHealthProbeTimer.unref?.();\n }\n\n private stopUrlHealthProbe(): void {\n if (this.urlHealthProbeTimer) clearInterval(this.urlHealthProbeTimer);\n this.urlHealthProbeTimer = null;\n this.urlHealthProbeUrl = null;\n this.urlHealthProbeConsecutiveFailures = 0;\n this.urlHealthProbeInFlight = false;\n }\n\n private async runUrlHealthProbeTick(): Promise<void> {\n if (this.urlHealthProbeInFlight) return;\n const url = this.urlHealthProbeUrl;\n if (!url || this.intentionalStop) return;\n this.urlHealthProbeInFlight = true;\n try {\n const outcome = await probeHttpOnce(url, 1_500);\n if (this.urlHealthProbeUrl !== url || this.intentionalStop) return;\n if (outcome === \"reachable\" || outcome === \"held\") {\n this.urlHealthProbeConsecutiveFailures = 0;\n return;\n }\n // Match TempoDevServer: a foreground child owns liveness. HTTP misses\n // are only terminal after a clean launcher handoff switched authority\n // to the configured URL probe.\n if (!this.launcherFinished) return;\n this.urlHealthProbeConsecutiveFailures += 1;\n if (\n this.urlHealthProbeConsecutiveFailures <\n this.options.urlHealthFailuresToDemote\n ) {\n return;\n }\n const error = `App dev server URL became unreachable after launcher handoff: ${url}`;\n this.launcherFinished = false;\n this.stopUrlHealthProbe();\n this.setStatus({ phase: \"error\", url: null, error });\n for (const listener of [...this.exitListeners]) {\n try {\n listener({ code: null, signal: null, error });\n } catch {}\n }\n } finally {\n this.urlHealthProbeInFlight = false;\n }\n }\n\n private async killChild(): Promise<void> {\n const child = this.child;\n if (!child) return;\n if (child.exitCode !== null || child.signalCode !== null) {\n this.child = null;\n return;\n }\n const pid = child.pid;\n\n if (!pid) {\n this.child = null;\n return;\n }\n try {\n if (process.platform !== \"win32\") {\n try {\n process.kill(-pid, \"SIGTERM\");\n } catch {\n child.kill(\"SIGTERM\");\n }\n } else {\n // MUST be a tree-kill, and MUST happen while the shell wrapper is\n // still alive. `spawn(command, {shell: true})` on win32 gives us the\n // pid of `cmd.exe`; the actual dev server (`next dev` / `vite`) is a\n // grandchild. `child.kill()` = TerminateProcess(cmd.exe) ONLY — no\n // process groups, no signal propagation — so every stop/restart\n // orphaned the real server, which kept the port AND Next.js 16's\n // per-directory dev lock (bundle t17b8rsd…: 19 lock-conflict aborts\n // in one session). `taskkill /T` walks the descendant tree rooted at\n // the (live) wrapper. Best-effort: the bounded wait below escalates\n // while this process still owns the child; a later start can choose a\n // fresh port if anything nevertheless survives.\n await killWindowsProcessTree(pid);\n }\n } catch {}\n\n // Wait briefly, then escalate to SIGKILL.\n const exited = await waitForChildExit(child, 5000);\n if (!exited) {\n try {\n if (process.platform !== \"win32\") {\n try {\n process.kill(-pid, \"SIGKILL\");\n } catch {\n child.kill(\"SIGKILL\");\n }\n } else {\n // taskkill /F is already forceful; retry the tree walk (a child\n // that spawned between enumeration and kill survives pass 1),\n // then last-ditch the wrapper itself.\n await killWindowsProcessTree(pid);\n child.kill(\"SIGKILL\");\n }\n } catch {}\n if (!(await waitForChildExit(child, 2000))) {\n throw new Error(\n `App dev server process ${pid} survived termination. Retry cleanup before starting a replacement.`,\n );\n }\n }\n if (this.child === child) this.child = null;\n }\n\n private async ensureParentDeathWatchdog(): Promise<string | null> {\n if (process.env.TEMPO_DEVSERVER_WATCHDOG === \"0\") return null;\n const watchdogPath = path.join(\n this.options.workspaceRoot,\n \".tempo\",\n `app-${DEVSERVER_WATCHDOG_FILE_NAME}`,\n );\n try {\n await mkdir(path.dirname(watchdogPath), { recursive: true });\n await atomicWriteText(watchdogPath, DEVSERVER_WATCHDOG_SOURCE);\n return watchdogPath;\n } catch (error) {\n devLog(\"app-devserver:watchdog-write-failed\", {\n watchdogPath,\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n }\n\n private substitutePort(command: string, port: number): string {\n return substituteAppPort(command, port);\n }\n\n /**\n * Schedule a status push so the renderer receives the latest\n * `recentOutputTail`. Debounced to keep IPC chatter bounded while\n * stdout is streaming heavily (Next.js bootstrap can dump dozens of\n * lines in <1 s). The push carries no updates — it's purely a signal\n * that the rolling tail snapshot in `getStatus()` has changed.\n */\n private scheduleOutputBroadcast(): void {\n if (this.outputBroadcastTimer) return;\n this.outputBroadcastTimer = setTimeout(() => {\n this.outputBroadcastTimer = null;\n // Only emit if we're actively starting — once `running` is\n // reached the URL is the signal, not the tail; once `error` /\n // `stopped` we've already pushed the final tail.\n if (this.status.phase !== \"starting\") return;\n this.setStatus({});\n }, AppDevServer.OUTPUT_BROADCAST_DEBOUNCE_MS);\n }\n\n private clearOutputBroadcastTimer(): void {\n if (!this.outputBroadcastTimer) return;\n clearTimeout(this.outputBroadcastTimer);\n this.outputBroadcastTimer = null;\n }\n\n // ---- Port allocation -------------------------------------------------\n\n private async allocatePort(): Promise<number> {\n const persisted = await this.readPersistedState();\n if (persisted) {\n // Port free → reuse it. A stale recorded pid is irrelevant here\n // (its process group is gone or at least not holding the port),\n // and skipping the reap when nothing is in the way is what makes\n // reaping safe — see reapOwnedProcessGroup's identity contract.\n if (await isPortFree(persisted.port)) {\n return persisted.port;\n }\n // Port occupied AND we recorded a child pid the previous session\n // never tore down (hard main-process death skips quit handlers).\n // That orphaned process group — OUR spawn — is what's holding the\n // port. Reap it so the workspace keeps its stable port instead of\n // leaking a `next dev` tree and drifting to a new port (observed:\n // EADDRINUSE against our own orphan, bundle 20-57-35).\n const mayReapPersistedProcess =\n await this.mayReapPersistedProcess(persisted);\n if (\n mayReapPersistedProcess &&\n persisted.pid != null &&\n (await pidStartIdentityMatches(persisted.pid, persisted.pidStartedAt))\n ) {\n const outcome = await reapOwnedProcessGroup(persisted.pid);\n devLog(\"app-devserver:orphan-reap\", {\n pid: persisted.pid,\n port: persisted.port,\n outcome,\n workspaceRoot: this.options.workspaceRoot,\n });\n // Process exit and socket release are not one atomic OS event. Wait\n // for the deterministic port-free signal before abandoning the stable\n // port; an immediate sample can drift to a new port even though the\n // identity-proven orphan was successfully reaped.\n if (await waitForPortFree(persisted.port, 2000)) {\n return persisted.port;\n }\n }\n // The shell-pid reap didn't free the port (or there was no shell pid\n // to reap — on win32 the shell often exits first and the exit handler\n // clears it). The recorded LISTENER pid is the second handle: it's\n // the real server process resolved by port at ready-time.\n //\n // Identity guard — kill ONLY when the CURRENT listener on the port is\n // still that recorded pid. `serverPid` was resolved last session; if\n // the orphan since died and the OS recycled its pid, a blind\n // `reapConflictingDevServer(serverPid)` would tree-kill an unrelated\n // process. And killing the current listener UNCONDITIONALLY is no\n // better: with no lock banner naming a holder, whoever occupies the\n // port now may be a legitimate stranger (the user's own server) that\n // claimed it after our orphan died. pid+port matching is what makes\n // the kill provably ours: the recorded pid, still alive, still\n // holding the exact socket we recorded it holding.\n if (mayReapPersistedProcess && persisted.serverPid != null) {\n const listenerPid = await findPortListenerPid(persisted.port);\n if (\n listenerPid != null &&\n listenerPid === persisted.serverPid &&\n (await pidStartIdentityMatches(\n listenerPid,\n persisted.serverPidStartedAt,\n ))\n ) {\n const outcome = await reapConflictingDevServer(persisted.serverPid);\n devLog(\"app-devserver:orphan-server-pid-reap\", {\n serverPid: persisted.serverPid,\n port: persisted.port,\n outcome,\n workspaceRoot: this.options.workspaceRoot,\n });\n if (await waitForPortFree(persisted.port, 2000)) {\n return persisted.port;\n }\n } else {\n devLog(\"app-devserver:orphan-server-pid-mismatch\", {\n serverPid: persisted.serverPid,\n listenerPid,\n port: persisted.port,\n workspaceRoot: this.options.workspaceRoot,\n });\n }\n }\n // Occupied by a process that is NOT our recorded child (or a reap\n // that failed). Never spawn onto it — claim a fresh port instead.\n devLog(\"app-devserver:persisted-port-occupied\", {\n port: persisted.port,\n workspaceRoot: this.options.workspaceRoot,\n });\n }\n const fresh = await findFreePort(this.options.portRange);\n await this.writePersistedState({\n version: PERSISTED_STATE_VERSION,\n port: fresh,\n allocatedAt: Date.now(),\n pid: null,\n pidStartedAt: null,\n serverPid: null,\n serverPidStartedAt: null,\n ...this.persistedOwnership(),\n });\n return fresh;\n }\n\n /**\n * Record (or clear) the live child (shell) pid in the persisted state\n * file via read-modify-write, preserving the port claim and serverPid.\n * Best-effort — if the state file is unreadable we skip rather than\n * fabricate one, because a pid without its companion port claim is\n * useless to the reaper.\n */\n private async persistChildPid(pid: number | null): Promise<void> {\n const pidStartedAt = pid == null ? null : await readPidStartIdentity(pid);\n await this.runExclusiveStateFileOp(async () => {\n const persisted = await this.readPersistedState();\n if (!persisted) {\n devLog(\"app-devserver:persist-pid-skipped-no-state\", {\n pid,\n workspaceRoot: this.options.workspaceRoot,\n });\n return;\n }\n await this.writePersistedStateUnlocked({\n version: PERSISTED_STATE_VERSION,\n port: persisted.port,\n allocatedAt: persisted.allocatedAt,\n pid,\n pidStartedAt,\n serverPid: persisted.serverPid,\n serverPidStartedAt: persisted.serverPidStartedAt,\n ...this.persistedOwnership(persisted),\n });\n });\n }\n\n /**\n * Record (or clear) the pid LISTENING on the allocated port — the real\n * dev server, which on win32 is a grandchild of the shell wrapper we\n * spawned. Same read-modify-write + skip-if-no-state contract as\n * {@link persistChildPid}.\n */\n private async persistServerPid(serverPid: number | null): Promise<void> {\n const serverPidStartedAt =\n serverPid == null ? null : await readPidStartIdentity(serverPid);\n await this.runExclusiveStateFileOp(async () => {\n const persisted = await this.readPersistedState();\n if (!persisted) {\n devLog(\"app-devserver:persist-server-pid-skipped-no-state\", {\n serverPid,\n workspaceRoot: this.options.workspaceRoot,\n });\n return;\n }\n await this.writePersistedStateUnlocked({\n version: PERSISTED_STATE_VERSION,\n port: persisted.port,\n allocatedAt: persisted.allocatedAt,\n pid: persisted.pid,\n pidStartedAt: persisted.pidStartedAt,\n serverPid,\n serverPidStartedAt,\n ...this.persistedOwnership(persisted),\n });\n });\n }\n\n private getStateFilePath(): string {\n return path.join(\n this.options.workspaceRoot,\n \".tempo\",\n APP_DEVSERVER_STATE_FILENAME,\n );\n }\n\n private persistedOwnership(\n fallback?: PersistedState,\n ): Pick<PersistedState, \"ownerId\" | \"managerPid\" | \"managerStartedAt\"> {\n const ownership = this.options.ownership;\n return ownership\n ? ownership\n : {\n ownerId: fallback?.ownerId ?? null,\n managerPid: fallback?.managerPid ?? null,\n managerStartedAt: fallback?.managerStartedAt ?? null,\n };\n }\n\n private async mayReapPersistedProcess(\n persisted: PersistedState,\n ): Promise<boolean> {\n return (\n (await this.classifyPersistedProcessOwnership(persisted)) === \"reapable\"\n );\n }\n\n private async classifyPersistedProcessOwnership(\n persisted: PersistedState,\n ): Promise<\"reapable\" | \"live_sibling\" | \"foreign_process\"> {\n const ownership = this.options.ownership;\n if (!ownership) return \"reapable\";\n if (\n persisted.ownerId !== ownership.ownerId ||\n persisted.managerPid == null ||\n persisted.managerStartedAt == null\n ) {\n return \"foreign_process\";\n }\n if (\n persisted.managerPid === ownership.managerPid &&\n persisted.managerStartedAt === ownership.managerStartedAt\n ) {\n return \"reapable\";\n }\n return (await pidStartIdentityMatches(\n persisted.managerPid,\n persisted.managerStartedAt,\n ))\n ? \"live_sibling\"\n : \"reapable\";\n }\n\n private async readPersistedState(): Promise<PersistedState | null> {\n try {\n const raw = await readFile(this.getStateFilePath(), \"utf-8\");\n const parsed = JSON.parse(raw);\n if (\n parsed &&\n typeof parsed === \"object\" &&\n typeof parsed.port === \"number\" &&\n Number.isInteger(parsed.port) &&\n parsed.port > 0 &&\n parsed.port < 65536\n ) {\n return {\n version: typeof parsed.version === \"number\" ? parsed.version : 0,\n port: parsed.port,\n allocatedAt:\n typeof parsed.allocatedAt === \"number\" ? parsed.allocatedAt : 0,\n // Absent in version-1 files (written before orphan reaping\n // existed) — treated as \"no live child recorded\".\n pid:\n typeof parsed.pid === \"number\" && Number.isInteger(parsed.pid)\n ? parsed.pid\n : null,\n pidStartedAt:\n typeof parsed.pidStartedAt === \"string\"\n ? parsed.pidStartedAt\n : null,\n // Absent in version-2 files (written before listener-pid\n // tracking existed) — treated as \"no listener recorded\".\n serverPid:\n typeof parsed.serverPid === \"number\" &&\n Number.isInteger(parsed.serverPid)\n ? parsed.serverPid\n : null,\n serverPidStartedAt:\n typeof parsed.serverPidStartedAt === \"string\"\n ? parsed.serverPidStartedAt\n : null,\n ownerId: typeof parsed.ownerId === \"string\" ? parsed.ownerId : null,\n managerPid:\n typeof parsed.managerPid === \"number\" &&\n Number.isInteger(parsed.managerPid)\n ? parsed.managerPid\n : null,\n managerStartedAt:\n typeof parsed.managerStartedAt === \"string\"\n ? parsed.managerStartedAt\n : null,\n };\n }\n } catch {}\n return null;\n }\n\n private async writePersistedState(state: PersistedState): Promise<void> {\n await this.runExclusiveStateFileOp(() =>\n this.writePersistedStateUnlocked(state),\n );\n }\n\n /**\n * The raw write. Only call from inside {@link runExclusiveStateFileOp}\n * (queueing here too would deadlock the chain). Temp-file + rename so a\n * concurrent reader never sees the truncated-but-not-yet-written window\n * of a plain `writeFile`.\n */\n private async writePersistedStateUnlocked(\n state: PersistedState,\n ): Promise<void> {\n const file = this.getStateFilePath();\n try {\n await mkdir(path.dirname(file), { recursive: true });\n const tmp = `${file}.${process.pid}.tmp`;\n await writeFile(tmp, `${JSON.stringify(state, null, 2)}\\n`, \"utf-8\");\n await rename(tmp, file);\n } catch (error) {\n devLog(\"app-devserver:persist-failed\", {\n file,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Chain `op` behind every previously queued state-file mutation. Both\n * pid-persist helpers and the raw state write go through here, so a\n * fire-and-forget write from the child `exit` handler can't interleave\n * with (or complete after) the awaited writes in `runStopCycle`.\n */\n private runExclusiveStateFileOp<T>(op: () => Promise<T>): Promise<T> {\n const next = this.stateFileChain.then(op, op);\n this.stateFileChain = next.catch(() => {});\n return next;\n }\n\n private getLogFilePath(): string {\n return path.join(\n this.options.workspaceRoot,\n \".tempo\",\n APP_DEVSERVER_LOG_FILENAME,\n );\n }\n\n /**\n * Wipe the disk log at the start of each runStartCycle. Best-effort —\n * a failed truncate doesn't block the run; we just append over the\n * stale content. Lives next to the port-assignment JSON so a single\n * `.tempo/` dir holds every supervisor artifact.\n */\n private async truncateLogFile(): Promise<void> {\n await this.outputLog.reset();\n }\n\n private setStatus(updates: Partial<AppDevServerStatus>): void {\n this.status = { ...this.status, ...updates };\n const snapshot = this.getStatus();\n for (const listener of this.listeners) {\n try {\n listener(snapshot);\n } catch {}\n }\n }\n}\n\n// ---- Helpers (file-scope) ----------------------------------------------\n\nfunction extractServerUrl(text: string): string | null {\n const stripped = text.replace(ANSI_ESCAPE_RE, \"\");\n const matches = stripped.match(URL_RE);\n if (!matches) return null;\n for (const raw of matches) {\n const cleaned = raw.replace(/[),.;]+$/, \"\");\n try {\n const parsed = new URL(cleaned);\n if (LOCAL_HOSTNAMES.has(parsed.hostname.toLowerCase())) {\n return cleaned;\n }\n } catch {}\n }\n return null;\n}\n\nfunction rewriteToPublicHost(\n url: string,\n fallbackPort: number,\n applyFallbackPort = true,\n): string {\n try {\n const parsed = new URL(url);\n if (parsed.hostname === \"0.0.0.0\" || parsed.hostname === \"::\") {\n parsed.hostname = PUBLIC_HOSTNAME;\n }\n if (applyFallbackPort && !parsed.port) {\n parsed.port = String(fallbackPort);\n }\n const normalizedPath = parsed.pathname.replace(/\\/+$/, \"\");\n return `${parsed.origin}${normalizedPath}${parsed.search}`;\n } catch {\n return `http://${PUBLIC_HOSTNAME}:${fallbackPort}`;\n }\n}\n\nexport type HttpProbeOutcome = \"reachable\" | \"refused\" | \"held\";\n\n/**\n * One HTTP attempt against the dev server, classified three ways so the\n * readiness loop can tell \"busy/compiling\" apart from \"not up\":\n * - \"reachable\": the server answered (ANY status, incl. 404/5xx) — it's\n * serving. The route-storyboard iframe renders whatever it returns,\n * including an error overlay, so we only require a response, not a 2xx.\n * - \"held\": OUR timeout fired before any response — the connection was\n * accepted but the server is busy, almost always a cold route compile.\n * This is liveness/progress, NOT a failure.\n * - \"refused\": network-level failure (ECONNREFUSED / reset) — nothing is\n * listening yet.\n * Exported for unit testing.\n */\nexport async function probeHttpOnce(\n url: string,\n timeoutMs: number,\n signal?: AbortSignal,\n): Promise<HttpProbeOutcome> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const onAbort = () => controller.abort();\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n if (signal?.aborted) onAbort();\n try {\n const response = await fetch(url, { signal: controller.signal });\n return response.status > 0 ? \"reachable\" : \"refused\";\n } catch {\n // Our own abort => the server held the connection without answering\n // (compiling). Any other throw is a network-level failure (not listening).\n return controller.signal.aborted ? \"held\" : \"refused\";\n } finally {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", onAbort);\n }\n}\n\n/**\n * Stepped poll interval that widens the longer we've been waiting — cheap and\n * responsive early, gentle on the compiler later (each probe to an uncompiled\n * route can trigger a compile cycle). Mirrors tempo-api's `waitForAppStatus`\n * adaptive cadence: 0.5 s for the first 20 s, 5 s up to 2 min, 10 s after.\n * Exported for unit testing.\n */\nexport function httpProbeInterval(elapsedMs: number): number {\n if (elapsedMs < 20_000) return 500;\n if (elapsedMs < 120_000) return 5_000;\n return 10_000;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nconst {\n throwIfAborted: throwIfAppLaunchAborted,\n sleep: sleepForAppLaunch,\n race: waitForAppStart,\n} = abortableWait(\"App devserver launch was cancelled\");\n\nfunction parseEnvMs(value: string | undefined): number | null {\n if (!value) return null;\n const n = Number(value);\n if (!Number.isFinite(n) || n <= 0) return null;\n return n;\n}\n\n/**\n * Pick the URL-scrape timeout. Three sources are possible and each has\n * a distinct reason; writing them out top-to-bottom makes the precedence\n * obvious without forcing the reader to parse a chained `??`.\n *\n * Exported for unit testing.\n */\nexport function resolveUrlScrapeTimeout(explicit: number | undefined): number {\n // E2E / operator env override → wins over everything. It is the\n // \"cut every dev-server wait\" escape hatch the failure-mode specs rely on,\n // and is NEVER set in production, so it can't affect real users. It MUST\n // beat the explicit value below: callers now thread a programmatic\n // per-framework budget through (`resolveDevserverReadyTimeoutMs`), and if\n // explicit won, that production budget would silently shadow this override\n // and the specs would wait 30 s+ instead of the 5 s they configure.\n const envOverride = parseEnvMs(\n process.env.TEMPO_APP_DEVSERVER_URL_SCRAPE_TIMEOUT_MS,\n );\n if (envOverride !== null) return envOverride;\n // Caller passed an explicit value → use it (per-framework budget, or any\n // future per-spawn override).\n if (explicit !== undefined) return explicit;\n // Production default — covers Next.js + Turbopack cold start on a\n // realistic-sized app.\n return DEFAULT_URL_SCRAPE_TIMEOUT_MS;\n}\n\n/**\n * Same shape as `resolveUrlScrapeTimeout`, for the HTTP-readiness gate.\n * Exported for unit testing.\n */\nexport function resolveHttpReadyTimeout(explicit: number | undefined): number {\n const envOverride = parseEnvMs(\n process.env.TEMPO_APP_DEVSERVER_HTTP_READY_TIMEOUT_MS,\n );\n if (envOverride !== null) return envOverride;\n if (explicit !== undefined) return explicit;\n return DEFAULT_HTTP_READY_TIMEOUT_MS;\n}\n\nfunction waitForChildExit(\n child: ChildProcess,\n timeoutMs: number,\n): Promise<boolean> {\n if (child.exitCode !== null || child.signalCode !== null) {\n return Promise.resolve(true);\n }\n return new Promise((resolve) => {\n const timer = setTimeout(() => resolve(false), timeoutMs);\n child.once(\"exit\", () => {\n clearTimeout(timer);\n resolve(true);\n });\n });\n}\n\nfunction waitForWatchdogChildPid(\n watchdog: ChildProcess,\n timeoutMs = 2_000,\n): Promise<number | null> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (pid: number | null) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n watchdog.off(\"message\", onMessage);\n watchdog.off(\"exit\", onExit);\n resolve(pid);\n };\n const onMessage = (message: unknown) => {\n if (\n typeof message === \"object\" &&\n message !== null &&\n \"type\" in message &&\n message.type === \"tempo-watchdog-child\" &&\n \"pid\" in message\n ) {\n finish(typeof message.pid === \"number\" ? message.pid : null);\n }\n };\n const onExit = () => finish(null);\n const timer = setTimeout(() => finish(null), timeoutMs);\n watchdog.on(\"message\", onMessage);\n watchdog.once(\"exit\", onExit);\n });\n}\n\n/**\n * Probe-and-claim port discovery. Asks the OS for an ephemeral port via\n * `listen(0)`, then immediately closes the socket. Race window is\n * sub-millisecond. Bias toward the supplied range when possible by\n * retrying when the OS hands back something outside it.\n *\n * The probe binds with NO host — the same unspecified-host bind the app\n * dev server itself performs (`next dev` listens on the IPv6 wildcard\n * `::` with dual-stack). Probing a specific address like `127.0.0.1`\n * gives false \"free\" results on macOS: Node listeners set SO_REUSEADDR,\n * which lets an IPv4-specific bind succeed while another process holds\n * the IPv6 wildcard on the same port. Verified live against an orphaned\n * `next-server` on `:::51053` — the 127.0.0.1 probe said free, the\n * wildcard probe correctly said EADDRINUSE.\n */\n/**\n * Substitute every literal `${APP_PORT}` token and its legacy `${PORT}` alias\n * in an app start command or URL. Exported so every launch/probe surface uses\n * the exact same transformation for the selected app port.\n */\nexport function substituteAppPort(command: string, port: number): string {\n return command.replace(/\\$\\{(?:APP_PORT|PORT)\\}/g, String(port));\n}\n\nfunction buildAppCommandEnv(port: number): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n APP_PORT: String(port),\n PORT: String(port),\n TEMPO_APP_PORT: String(port),\n TEMPO: \"true\",\n };\n for (const key of Object.keys(env)) {\n const normalized = key.toLowerCase();\n if (\n normalized.startsWith(\"npm_\") ||\n normalized.startsWith(\"pnpm_\") ||\n key === \"INIT_CWD\" ||\n key === \"NODE_PATH\"\n ) {\n delete env[key];\n }\n }\n return env;\n}\n\nfunction describeShellCommandFailure(\n result: AbortableShellCommandResult,\n): string | null {\n if (result.aborted) return \"the app stop command timed out\";\n if (result.spawnError) {\n return `the app stop command could not start: ${result.spawnError.message}`;\n }\n if (result.exitCode === 0) return null;\n const detail = result.stderr.trim();\n return `the app stop command exited with code ${result.exitCode ?? \"unknown\"}${detail ? `: ${detail}` : \"\"}`;\n}\n\nexport async function findFreePort(range: PortRange): Promise<number> {\n const maxAttempts = 50;\n for (let i = 0; i < maxAttempts; i++) {\n const candidate = await new Promise<number | null>((resolve) => {\n const server = net.createServer();\n server.once(\"error\", () => resolve(null));\n server.listen(0, () => {\n const addr = server.address();\n server.close(() => {\n if (addr && typeof addr === \"object\") resolve(addr.port);\n else resolve(null);\n });\n });\n });\n if (candidate != null) {\n if (candidate >= range.min && candidate <= range.max) return candidate;\n // OS picked outside our preferred range — accept on the last try.\n if (i === maxAttempts - 1) return candidate;\n }\n }\n throw new Error(\"Could not allocate a free TCP port for the app dev server.\");\n}\n\n/**\n * True when a wildcard listener can bind `port` — i.e. the bind the app\n * dev server will itself attempt. See `findFreePort` for why this must\n * NOT probe a specific address.\n */\nexport async function isPortFree(port: number): Promise<boolean> {\n return await new Promise<boolean>((resolve) => {\n const server = net.createServer();\n server.once(\"error\", () => resolve(false));\n server.listen(port, () => {\n server.close(() => resolve(true));\n });\n });\n}\n\n/**\n * Poll {@link isPortFree} until it reports free or `timeoutMs` elapses.\n * Kill verification: TerminateProcess/taskkill returns before the OS\n * finishes tearing the socket down, so an immediate probe can read a\n * false \"still occupied\" from a process that is already dying.\n */\nexport async function waitForPortFree(\n port: number,\n timeoutMs: number,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await isPortFree(port)) return true;\n await sleep(150);\n }\n return await isPortFree(port);\n}\n\nconst execFileAsync = promisify(execFile);\n\nexport async function readPidStartIdentity(\n pid: number,\n): Promise<string | null> {\n try {\n if (process.platform === \"win32\") {\n const { stdout } = await execFileAsync(\n \"powershell.exe\",\n [\n \"-NoLogo\",\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-Command\",\n `$target = Get-CimInstance -ClassName Win32_Process -Filter 'ProcessId = ${pid}'; if ($null -ne $target) { [Console]::Out.Write($target.CreationDate.ToUniversalTime().Ticks) }`,\n ],\n { windowsHide: true, timeout: 2_000 },\n );\n return stdout.trim() || null;\n }\n const { stdout } = await execFileAsync(\n \"ps\",\n [\"-p\", String(pid), \"-o\", \"lstart=\"],\n { windowsHide: true, timeout: 2_000 },\n );\n return stdout.trim() || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Prove that a listener belongs to the process tree spawned by this\n * supervisor. Port ownership alone is deliberately insufficient: Docker\n * publishes through com.docker.backend on macOS, and a recycled port may be\n * held by an unrelated process.\n */\nexport async function pidBelongsToSpawnedTree(\n rootPid: number,\n targetPid: number,\n): Promise<boolean> {\n if (rootPid === targetPid) return true;\n try {\n if (process.platform === \"win32\") {\n const { stdout } = await execFileAsync(\n \"powershell.exe\",\n [\n \"-NoLogo\",\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-Command\",\n `$rootPid = ${rootPid}; $currentPid = ${targetPid}; while ($currentPid -gt 0 -and $currentPid -ne $rootPid) { $target = Get-CimInstance -ClassName Win32_Process -Filter \\\"ProcessId = $currentPid\\\"; if ($null -eq $target) { break }; $currentPid = [int]$target.ParentProcessId }; if ($currentPid -eq $rootPid) { [Console]::Out.Write('owned') }`,\n ],\n { windowsHide: true, timeout: 2_000 },\n );\n return stdout.trim() === \"owned\";\n }\n const { stdout } = await execFileAsync(\n \"ps\",\n [\"-p\", String(targetPid), \"-o\", \"pgid=\"],\n { windowsHide: true, timeout: 2_000 },\n );\n return Number(stdout.trim()) === rootPid;\n } catch {\n return false;\n }\n}\n\nasync function pidStartIdentityMatches(\n pid: number,\n expected: string | null,\n): Promise<boolean> {\n if (!expected) return false;\n return (await readPidStartIdentity(pid)) === expected;\n}\n\n/**\n * win32 tree-kill: `taskkill /T /F` walks the descendant tree rooted at\n * `pid`. The root MUST still be alive for the walk to reach anything — a\n * dead root enumerates zero descendants — which is why killChild calls this\n * BEFORE the shell wrapper exits. Best-effort: failure (already dead /\n * access denied) is fine; callers verify via port probes.\n */\nasync function killWindowsProcessTree(pid: number): Promise<void> {\n try {\n await execFileAsync(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], {\n windowsHide: true,\n });\n } catch {}\n}\n\nexport type OrphanReapOutcome =\n /** No process group with the recorded id exists — nothing to reap. */\n | \"dead\"\n /** Group exited after SIGTERM. */\n | \"terminated\"\n /** Group needed SIGKILL. */\n | \"killed\"\n /** Still signalable after SIGKILL (likely unreaped zombies). The\n * caller's port probe decides what actually matters — the listening\n * socket closes at termination, before zombie reaping. */\n | \"survived\";\n\n/**\n * Kill an app-dev-server child left orphaned by a previous session whose\n * main process died without running quit handlers (dev-mode restart,\n * crash, SIGKILL).\n *\n * Identity contract — why signaling `-pid` is safe:\n * - We only persist pids of children we spawned `detached: true`, which\n * makes each child the LEADER of a brand-new process group\n * (pgid === pid). Signals here target that GROUP id, covering the full\n * `npm run dev` → `next dev` → `next-server` tree even if the leader\n * itself already exited (a group survives its leader, and an unrelated\n * process cannot join or claim a group id that still has members).\n * - The only way the group id can refer to strangers is full group death\n * followed by pid reuse by a new group leader. The CALLER guards that:\n * it reaps only when the persisted port is still OCCUPIED — full group\n * death releases the port, so the occupied-port precondition filters\n * the pid-reuse case out.\n */\nexport async function reapOwnedProcessGroup(\n pid: number,\n): Promise<OrphanReapOutcome> {\n if (process.platform === \"win32\") {\n // No POSIX process groups on Windows; `taskkill /T` walks the child\n // tree instead. Failure (already dead / access denied) is fine — the\n // caller's port probe makes the actual reuse decision. Verify the root\n // pid's death so the outcome (and its devLog line) is honest instead of\n // a blind \"issued\" — descendants the walk missed still show up in the\n // caller's port probe.\n const wasAlive = signalPid(pid, 0);\n if (!wasAlive) return \"dead\";\n await killWindowsProcessTree(pid);\n if (await waitForPidExit(pid, 2000)) return \"killed\";\n // Same direct-TerminateProcess fallback as reapConflictingDevServer:\n // taskkill's failures are silent by construction, so verify and finish\n // the job with process.kill before reporting a survivor.\n signalPid(pid, \"SIGKILL\");\n if (await waitForPidExit(pid, 1000)) return \"killed\";\n return \"survived\";\n }\n\n if (!signalProcessGroup(pid, 0)) return \"dead\";\n\n signalProcessGroup(pid, \"SIGTERM\");\n if (await waitForProcessGroupExit(pid, 2500)) return \"terminated\";\n signalProcessGroup(pid, \"SIGKILL\");\n if (await waitForProcessGroupExit(pid, 2000)) return \"killed\";\n return \"survived\";\n}\n\n/** Signal every process in the group whose id is `pid`. Returns false\n * when no such group exists (ESRCH) or it is unsignalable. */\nfunction signalProcessGroup(pid: number, signal: NodeJS.Signals | 0): boolean {\n try {\n process.kill(-pid, signal);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function waitForProcessGroupExit(\n pid: number,\n timeoutMs: number,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!signalProcessGroup(pid, 0)) return true;\n await sleep(100);\n }\n return !signalProcessGroup(pid, 0);\n}\n\n/**\n * Kill the `next dev` instance Next.js 16 reported as holding this directory's\n * dev lock — the PID it prints in its \"Another next dev server is already\n * running … PID: <n>\" abort (see {@link parseDevServerLockConflictPid}).\n *\n * Unlike {@link reapOwnedProcessGroup}, this targets the PID *directly*, not a\n * process group: the PID Next prints is the `next-server` process itself, which\n * — when the prior session spawned through a shell, as the supervisor does — is\n * NOT its own group leader, so a group signal would no-op. Killing `next-server`\n * directly is what releases the lock, and is exactly the `taskkill /PID <n> /F`\n * remedy Next prints for humans.\n *\n * Best-effort and PID-reuse-tolerant: only signals a PID that is still alive,\n * and the caller re-spawns + re-checks the lock rather than trusting the\n * outcome. The brief window between Next printing the PID and us signalling it\n * makes recycling vanishingly unlikely, but the re-spawn is the real guard.\n */\nexport async function reapConflictingDevServer(\n pid: number,\n): Promise<OrphanReapOutcome> {\n if (process.platform === \"win32\") {\n // `/T` walks the child tree (bun/cmd wrapper → next → next-server) so the\n // real lock holder dies even when `pid` is an ancestor. Verified: a\n // \"dead\" return tells the caller the banner PID was STALE (a prior /F\n // already killed it) and the live holder must be found another way —\n // e.g. by the banner port's listener.\n const wasAlive = signalPid(pid, 0);\n if (!wasAlive) return \"dead\";\n await killWindowsProcessTree(pid);\n if (await waitForPidExit(pid, 2000)) return \"killed\";\n // taskkill can fail without us ever seeing why (exit code and stderr are\n // deliberately discarded, and its /T tree-walk has known failure modes on\n // reparented orphans). TerminateProcess via process.kill is the same\n // mechanism as PowerShell's `Stop-Process -Force` — the remedy that\n // demonstrably kills these orphans in the field (TEM-1491) — so use it as\n // the final word before reporting a survivor.\n signalPid(pid, \"SIGKILL\");\n if (await waitForPidExit(pid, 1000)) return \"killed\";\n return \"survived\";\n }\n if (!signalPid(pid, 0)) return \"dead\";\n signalPid(pid, \"SIGTERM\");\n if (await waitForPidExit(pid, 2500)) return \"terminated\";\n signalPid(pid, \"SIGKILL\");\n if (await waitForPidExit(pid, 2000)) return \"killed\";\n return \"survived\";\n}\n\n/**\n * Signal a single pid (not a group). Returns false only when the process does\n * not exist (ESRCH). EPERM means the process EXISTS but is not signalable\n * from here (POSIX semantics; on Windows, OpenProcess was denied — elevated\n * or protected target) — report it as alive so callers don't misread a live\n * lock holder as \"dead\" and skip reaping entirely (TEM-1491).\n */\nfunction signalPid(pid: number, signal: NodeJS.Signals | 0): boolean {\n try {\n process.kill(pid, signal);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException)?.code === \"EPERM\";\n }\n}\n\nasync function waitForPidExit(\n pid: number,\n timeoutMs: number,\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!signalPid(pid, 0)) return true;\n await sleep(100);\n }\n return !signalPid(pid, 0);\n}\n","/**\n * Abortable waiting bound to one domain-specific cancellation message.\n *\n * Long-running work (a devserver launch, a dependency install) checks one\n * AbortSignal at many points and wants every cancellation to surface as the\n * same recognizable error: `name === \"AbortError\"` with a message naming the\n * operation that was cancelled. Bind that message once and get the three\n * waiting primitives every such workflow needs.\n */\nexport interface AbortableWait {\n /** A fresh AbortError carrying this family's cancellation message. */\n error(): Error;\n /** Bail out now when the signal is already aborted. */\n throwIfAborted(signal?: AbortSignal): void;\n /** Resolve after `ms`, or reject with the family's AbortError on abort. */\n sleep(ms: number, signal?: AbortSignal): Promise<void>;\n /**\n * Settle with `work`, unless the signal aborts first — then reject with the\n * family's AbortError while `work` keeps running unobserved.\n */\n race<T>(work: Promise<T>, signal?: AbortSignal): Promise<T>;\n}\n\nexport function abortableWait(cancelledMessage: string): AbortableWait {\n const error = (): Error => {\n const abortError = new Error(cancelledMessage);\n abortError.name = \"AbortError\";\n return abortError;\n };\n\n const throwIfAborted = (signal?: AbortSignal): void => {\n if (signal?.aborted) throw error();\n };\n\n const sleep = (ms: number, signal?: AbortSignal): Promise<void> => {\n throwIfAborted(signal);\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timeout);\n reject(error());\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n // An abort that landed between the check above and addEventListener\n // never fires the listener; settle it explicitly.\n if (signal?.aborted) onAbort();\n });\n };\n\n const race = <T>(work: Promise<T>, signal?: AbortSignal): Promise<T> => {\n throwIfAborted(signal);\n if (!signal) return work;\n return new Promise((resolve, reject) => {\n const onAbort = () => reject(error());\n const cleanup = () => signal.removeEventListener(\"abort\", onAbort);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n void work.then(\n (value) => {\n cleanup();\n resolve(value);\n },\n (workError) => {\n cleanup();\n reject(workError);\n },\n );\n if (signal.aborted) onAbort();\n });\n };\n\n return { error, throwIfAborted, sleep, race };\n}\n","/**\n * Bundled Node toolchain fallback.\n *\n * The Tempo canvas/app devservers shell out to `node`/`npm`/`npx` resolved from\n * the user's PATH. Brand-new (FTUE) users frequently have no usable Node on PATH\n * — node-not-found, or a version below the Vite 7 floor — so the devserver can't\n * start and the live canvas (the actual product) never comes up.\n *\n * The host points us at its fallback via `TEMPO_NODE_TOOLCHAIN_BIN_DIR`.\n * macOS/Linux use Electron's embedded Node through shell shims. Packaged\n * Windows builds ship native node.exe so non-shell callers can spawn Node.\n *\n * This module owns the FALLBACK-ONLY policy: prepend that shim dir to the\n * devserver spawn PATH **only when the user's own Node is unusable** (missing,\n * or — for surfaces that require it — below the floor). A Node that works for the\n * surface is never shadowed. The pure parts ({@link shouldUseBundledNode},\n * {@link applyBundledNodeEnv}) are unit-tested without spawning.\n */\nimport { spawn } from \"node:child_process\";\nimport path from \"node:path\";\n\n/**\n * Env var the Electron host sets to the absolute path of the bundled bin\n * dir (containing `node`/`npm`/`npx`, including node.exe on Windows). Absent\n * when no host toolchain is available (e.g. plain Node test runs) — then this\n * module is inert and the user's system Node is used as before.\n */\nexport const BUNDLED_NODE_BIN_DIR_ENV = \"TEMPO_NODE_TOOLCHAIN_BIN_DIR\";\n\n/** `native` for standalone Node; unset/`electron` for the embedded runtime. */\nexport const BUNDLED_NODE_RUNTIME_ENV = \"TEMPO_NODE_TOOLCHAIN_RUNTIME\";\n\n/** Absolute toolchain bin dir provided by the host, or null when unavailable. */\nexport function getBundledNodeBinDir(): string | null {\n const dir = process.env[BUNDLED_NODE_BIN_DIR_ENV];\n return dir && dir.trim().length > 0 ? dir : null;\n}\n\n// Process-global activation. Set true the first time ANY managed devserver\n// surface decides its system Node is unusable; from then on every managed spawn\n// uses the bundled toolchain. Monotonic (never flips back off within a process)\n// so a surface that already committed to bundled Node mid-startup stays\n// consistent across its many spawns (probe, install, start).\nlet activated = false;\n\n/** Whether the bundled toolchain has been activated for this process. */\nexport function isBundledNodeActivated(): boolean {\n return activated;\n}\n\n/** Latch activation on. Idempotent; never turns activation back off. */\nexport function setBundledNodeActivated(value: boolean): void {\n activated = activated || value;\n}\n\n/** Test-only: reset the process-global activation latch. */\nexport function resetBundledNodeActivationForTests(): void {\n activated = false;\n}\n\n/**\n * Pure fallback decision. Returns true when the managed devserver should fall\n * back to the bundled toolchain instead of the user's system Node:\n *\n * - `systemNodeVersion === null` → system Node missing → use bundled.\n * - `requireFloor && !floorOk` → system Node too old for this surface (e.g.\n * the Vite 7 host) → use bundled.\n * - otherwise → keep the user's Node (never shadow a Node that works here).\n */\nexport function shouldUseBundledNode(args: {\n systemNodeVersion: string | null;\n requireFloor: boolean;\n floorOk: boolean;\n}): boolean {\n if (args.systemNodeVersion == null) return true;\n if (args.requireFloor && !args.floorOk) return true;\n return false;\n}\n\n/**\n * Return a copy of `env` with the bundled bin dir prepended to PATH. Embedded\n * Electron needs `ELECTRON_RUN_AS_NODE=1`; native Node must not pass that flag\n * to unrelated Electron children. No-op when `binDir` is null.\n *\n * The shim dir is prepended (wins over a too-old system Node still on PATH).\n * `ELECTRON_RUN_AS_NODE` is scoped to this spawn env — NOT the host's global\n * `process.env` — so it covers npm's own child `node` (`process.execPath`)\n * invocations within the devserver subtree without leaking into unrelated\n * Electron processes the host may spawn elsewhere (cf. the leak that breaks\n * sibling Electron apps when this var is set process-wide).\n */\nexport function applyBundledNodeEnv(\n env: NodeJS.ProcessEnv,\n binDir: string | null,\n): NodeJS.ProcessEnv {\n if (!binDir) return env;\n const next = prependDirToPathEnv(env, binDir);\n if (env[BUNDLED_NODE_RUNTIME_ENV] === \"native\") {\n delete next.ELECTRON_RUN_AS_NODE;\n } else {\n next.ELECTRON_RUN_AS_NODE = \"1\";\n }\n return next;\n}\n\n/**\n * Return a copy of `env` with `dir` prepended to PATH (so its binaries win\n * over everything already there). Also used for the project Node override\n * (project-node-version.ts), where a version-manager install's bin dir must\n * shadow the default Node for managed package-manager spawns.\n */\nexport function prependDirToPathEnv(\n env: NodeJS.ProcessEnv,\n dir: string,\n): NodeJS.ProcessEnv {\n // Windows env keys are case-insensitive and the live key may be `Path`.\n const pathKey = Object.keys(env).find((key) => key.toLowerCase() === \"path\");\n const existingPath = pathKey ? env[pathKey] : env.PATH;\n const nextPath = [dir, existingPath]\n .filter((value): value is string => Boolean(value))\n .join(path.delimiter);\n return {\n ...env,\n [pathKey ?? \"PATH\"]: nextPath,\n };\n}\n\n/**\n * Return a copy of `env` with every PATH segment equal to `dir` removed. Used\n * to build a CLEAN system-Node probe env: the host appends the shim bin dir to\n * the global `process.env.PATH` at startup (last-resort fallback for shells),\n * so a probe that just spreads `process.env` would resolve the SHIM node and\n * report it as the system Node — masking \"node missing\" and defeating the\n * fallback decision. No-op (returns `env` unchanged) when PATH is absent or\n * contains no matching segment. Exact-string segment match — the only caller\n * removes the dir the host itself appended, so the strings are identical.\n */\nexport function removeDirFromPathEnv(\n env: NodeJS.ProcessEnv,\n dir: string,\n delimiter: string = path.delimiter,\n): NodeJS.ProcessEnv {\n const pathKey = Object.keys(env).find((key) => key.toLowerCase() === \"path\");\n const existingPath = pathKey ? env[pathKey] : env.PATH;\n if (!existingPath) return env;\n const nextPath = existingPath\n .split(delimiter)\n .filter((segment) => segment && segment !== dir)\n .join(delimiter);\n if (nextPath === existingPath) return env;\n return { ...env, [pathKey ?? \"PATH\"]: nextPath };\n}\n\n/**\n * Spawn `node -p \"process.versions.node\"` with the given env and return the\n * trimmed version string, or null when Node can't be resolved/run. Used to probe\n * the SYSTEM Node (caller passes an env WITHOUT the bundled shims) before\n * deciding whether to fall back.\n */\nexport function probeNodeVersion(\n env: NodeJS.ProcessEnv,\n): Promise<string | null> {\n return new Promise<string | null>((resolve) => {\n const child = spawn('node -p \"process.versions.node\"', {\n shell: true,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n windowsHide: true,\n env,\n });\n let output = \"\";\n child.stdout?.on(\"data\", (chunk) => {\n output += String(chunk);\n });\n child.on(\"error\", () => resolve(null));\n child.on(\"exit\", (code) => {\n resolve(code === 0 ? output.trim() || null : null);\n });\n });\n}\n","/**\n * Rotating NDJSON debug log for the dev-server / tempobook lifecycle.\n *\n * Writes one line per event to a single active file, with a single\n * \"previous\" file kept for cross-rotation history. Worst-case disk\n * footprint per stream is `2 * MAX_BYTES_PER_FILE` (default 2 MB).\n *\n * The supervisor and the legacy-layout migration both write through\n * this module to `<userData>/logs/devserver.log`. The Electron main\n * process configures the path at startup via `configureDebugLogPath`;\n * callers outside Electron (the canvas demo, integration tests) fall\n * back to `~/tempo-devserver-debug.log` so the trace still lands\n * somewhere visible.\n *\n * Quiet by design — NEVER writes to stdout/stderr. Polluting the\n * supervisor's own console output would interleave debug noise with\n * the user's dev-server stdout, which we classify and surface to the\n * UI; mixing the two would make both harder to read.\n *\n * Bundled into the diagnostics download (`<userData>/logs/*.log`) so\n * support can see the last ~1 MB of lifecycle events even on a\n * shipped build.\n */\n\nimport {\n appendFileSync,\n mkdirSync,\n renameSync,\n statSync,\n unlinkSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nconst DEFAULT_MAX_BYTES_PER_FILE = 1_048_576; // 1 MB\n\nlet activeLogPath: string =\n // Demo + integration default. Overridden by `configureDebugLogPath` once\n // the Electron main process is ready (see `tempo-client/electron/src/main/index.ts`).\n join(homedir(), \"tempo-devserver-debug.log\");\nlet maxBytesPerFile = DEFAULT_MAX_BYTES_PER_FILE;\nlet initialized = false;\n\nfunction ensureInitialized(): void {\n if (initialized) return;\n initialized = true;\n try {\n mkdirSync(dirname(activeLogPath), { recursive: true });\n } catch {\n // Best-effort — if we can't create the dir we'll silently no-op on append.\n }\n}\n\nfunction rotateIfNeeded(): void {\n let size: number;\n try {\n size = statSync(activeLogPath).size;\n } catch {\n return; // File doesn't exist yet — nothing to rotate.\n }\n if (size < maxBytesPerFile) return;\n const priorPath = `${activeLogPath}.1`;\n try {\n unlinkSync(priorPath);\n } catch {\n // No prior file — that's fine, this is the first rotation.\n }\n try {\n renameSync(activeLogPath, priorPath);\n } catch {\n // If rename fails (rare — usually a permissions glitch on\n // Windows), drop the current file's contents instead so the\n // active file doesn't grow unbounded.\n try {\n unlinkSync(activeLogPath);\n } catch {\n // Already gone; nothing to do.\n }\n }\n}\n\n/**\n * Point the writer at a specific file. Call once at app startup\n * before any `devLog` / `devTimer` callers. Subsequent calls are\n * ignored (the writer commits to the first path it sees so all\n * subprocesses agree).\n */\nexport function configureDebugLogPath(\n filePath: string,\n options?: { maxBytesPerFile?: number },\n): void {\n if (initialized) return;\n activeLogPath = filePath;\n if (options?.maxBytesPerFile && options.maxBytesPerFile > 0) {\n maxBytesPerFile = options.maxBytesPerFile;\n }\n}\n\n/**\n * Append one NDJSON entry to the active log. Never throws; never\n * touches stdout/stderr.\n */\nexport function devLog(kind: string, detail?: Record<string, unknown>): void {\n ensureInitialized();\n let line: string;\n try {\n line = `${JSON.stringify({\n ts: new Date().toISOString(),\n kind,\n ...(detail !== undefined ? { d: detail } : {}),\n })}\\n`;\n } catch {\n line = `${JSON.stringify({ ts: new Date().toISOString(), kind })}\\n`;\n }\n try {\n rotateIfNeeded();\n appendFileSync(activeLogPath, line);\n } catch {\n // Best-effort. Disk full, permission flake, etc.\n }\n}\n\ninterface DebugTimer {\n done(detail?: Record<string, unknown>): void;\n}\n\n/**\n * Convenience wrapper that emits a `:start` event immediately and\n * returns an object whose `done()` emits the matching `:done` event\n * with `elapsedMs` plus any caller-supplied detail. Useful for\n * spanning install / spawn / initialize / repair.\n */\nexport function devTimer(\n kind: string,\n startDetail?: Record<string, unknown>,\n): DebugTimer {\n const start = Date.now();\n devLog(`${kind}:start`, startDetail);\n return {\n done(detail?: Record<string, unknown>) {\n const elapsedMs = Date.now() - start;\n devLog(`${kind}:done`, { elapsedMs, ...(detail ?? {}) });\n },\n };\n}\n\n/**\n * Path the writer is currently configured to use — exposed for the\n * diagnostics-export handler so it can locate the file without\n * re-deriving the path independently.\n */\nexport function getDebugLogPath(): string {\n return activeLogPath;\n}\n\n/** Sibling rotated file. Diagnostics export grabs both when present. */\nexport function getPriorDebugLogPath(): string {\n return `${activeLogPath}.1`;\n}\n\n// Tests reach in and reset state between cases; not part of the public API.\nexport function __resetForTests(): void {\n initialized = false;\n activeLogPath = join(homedir(), \"tempo-devserver-debug.log\");\n maxBytesPerFile = DEFAULT_MAX_BYTES_PER_FILE;\n}\n\n/** For diagnostics-export use — return both paths if they exist on disk. */\nexport function listDebugLogPaths(): string[] {\n const paths: string[] = [];\n try {\n statSync(activeLogPath);\n paths.push(activeLogPath);\n } catch {\n // No active log file yet.\n }\n const prior = getPriorDebugLogPath();\n try {\n statSync(prior);\n paths.push(prior);\n } catch {\n // No prior.\n }\n return paths;\n}\n","/**\n * Next.js 16 introduced a per-DIRECTORY dev-server lock: starting a second\n * `next dev` for the same project directory aborts the new process (exit 1)\n * with a banner like\n *\n * ⨯ Another next dev server is already running.\n *\n * - Local: http://localhost:59300\n * - PID: 11440\n * - Dir: C:\\Users\\…\\Plataforma-Operacional\n * Run taskkill /PID 11440 /F to stop it.\n *\n * even when the new process bound a perfectly free port. The lock may be held\n * by another Tempo session or by a user-owned `next dev` process.\n *\n * The orphan can be on a different port than the one the new spawn uses, so\n * port-based reaping never sees it; and on Windows the `lsof`-based cwd/lock\n * probes the canvas host relies on don't exist at all. In every one of those\n * cases Next.js hands us useful diagnostics: the PID and sometimes the port of\n * the process holding the lock. Those values are never kill authority by\n * themselves. The canvas supervisor selects a different claimed distDir; the\n * app supervisor may signal the PID only when its persisted pid/birth identity\n * independently proves ownership.\n */\n\n/**\n * How long a RECORDED lock conflict (a persisted exit's banner, or\n * app-devserver's pending post-ready conflict) may drive a proof-gated reap.\n * crash→restart loops this recovery exists for re-enter within seconds;\n * anything older risks the banner's pid (or port) having been recycled by an\n * unrelated process — killing by a stale record is worse than skipping,\n * because a still-held lock reproduces a FRESH banner within one spawn cycle.\n */\nexport const DEV_LOCK_CONFLICT_REAP_MAX_AGE_MS = 15 * 60_000;\n\nconst ANSI_ESCAPE_RE = /\\x1b\\[[0-9;]*m/g;\n\n/**\n * The lock-conflict signature. Matching this (rather than a bare `PID:`) is\n * what keeps {@link parseDevServerLockConflictPid} from grabbing an unrelated\n * \"PID:\" that some other tool happened to print.\n */\nconst LOCK_CONFLICT_RE = /another next dev server is already running/i;\n\n/**\n * The PID Next prints for the lock holder. Next emits it two ways in the same\n * banner — a `- PID: <n>` line and a `Run taskkill /PID <n> /F` hint — so we\n * accept either spelling and take the first. The `[ \\t]*` (not `\\s*`) keeps the\n * match on a single line, so it can't leap a newline onto an unrelated number.\n */\nconst LOCK_CONFLICT_PID_RE =\n /(?:taskkill[ \\t]+\\/PID[ \\t]+|PID[ \\t]*:[ \\t]*)(\\d+)/i;\n\n/**\n * The port of the lock HOLDER, from the banner's `- Local: http://…:<port>`\n * line. The `[ \\t]*` (not `\\s*`) keeps the match on one line, same as the\n * PID regex.\n */\nconst LOCK_CONFLICT_PORT_RE = /Local[ \\t]*:[ \\t]*https?:\\/\\/[^\\s/]+:(\\d+)/i;\n\nexport interface DevServerLockConflict {\n /** PID of the `next dev` holding the directory's dev lock. */\n pid: number;\n /**\n * Port the holder is serving on (from the banner's `Local:` line), or\n * `null` when the banner omitted it. A second, independent handle on the\n * holder. This is diagnostic context only; a listener lookup is not an\n * ownership proof.\n */\n port: number | null;\n}\n\n/**\n * Parse a Next.js 16 lock-conflict abort banner into the holder's PID and\n * (when printed) its port, or `null` when the given output is not a\n * lock-conflict abort. Pure — safe to unit test exhaustively.\n */\nexport function parseDevServerLockConflict(\n output: string,\n): DevServerLockConflict | null {\n if (!output) return null;\n const stripped = output.replace(ANSI_ESCAPE_RE, \"\");\n if (!LOCK_CONFLICT_RE.test(stripped)) return null;\n const match = stripped.match(LOCK_CONFLICT_PID_RE);\n if (!match) return null;\n const pid = Number(match[1]);\n // A process can never legitimately be the current process, and pid 0 / 1 are\n // never a user `next dev`. Reject those rather than risk signalling them.\n if (!Number.isInteger(pid) || pid <= 1 || pid === process.pid) return null;\n\n // The banner interleaves with the NEW spawn's own startup output in the\n // same buffer, and both print a `Local:` line. Only trust a port that\n // appears AFTER the conflict signature — that region is holder-only.\n const conflictIdx = stripped.search(LOCK_CONFLICT_RE);\n const portMatch = stripped.slice(conflictIdx).match(LOCK_CONFLICT_PORT_RE);\n const port = portMatch ? Number(portMatch[1]) : null;\n return {\n pid,\n port:\n port != null && Number.isInteger(port) && port > 0 && port < 65536\n ? port\n : null,\n };\n}\n\n/**\n * Whether the given output carries the lock-conflict abort signature, WITHOUT\n * requiring the holder's PID to be present.\n *\n * {@link parseDevServerLockConflict} deliberately returns null when it can't\n * name a PID — there is no candidate for a proof-gated reap. Classification is the\n * opposite case: the banner and the `- PID:` line can land in two different\n * stdout chunks, and \"another dev server is holding this project\" is already\n * worth telling the user even when the pid half hasn't arrived yet.\n */\nexport function isDevServerLockConflictOutput(output: string): boolean {\n if (!output) return false;\n return LOCK_CONFLICT_RE.test(output.replace(ANSI_ESCAPE_RE, \"\"));\n}\n\n/**\n * Extract just the holder PID. Kept for callers that only need the PID —\n * delegates to {@link parseDevServerLockConflict}.\n */\nexport function parseDevServerLockConflictPid(output: string): number | null {\n return parseDevServerLockConflict(output)?.pid ?? null;\n}\n","/**\n * Shell-dialect detection for the commands Tempo runs through the platform\n * shell (`spawn(command, { shell: true })` — cmd.exe on Windows, /bin/sh\n * elsewhere): `scripts.install`, `scripts.start`, and the app dev command.\n *\n * Users (and AI agents) sometimes author these in POWERSHELL syntax —\n * `if (Test-Path …) { … } else { … }` — which NO shell Tempo spawns with can\n * parse: cmd.exe aborts with \"<token> was unexpected at this time.\" and\n * /bin/sh with its own syntax error, in both cases before running a single\n * step. See TEM-436.\n *\n * Two complementary detectors, used at different times:\n *\n * - {@link findPowerShellSyntax} inspects the COMMAND STRING so callers can\n * reject a doomed command BEFORE spawning it (and at author time —\n * `assertPortableAppStartCommand` in tempo-project). Deterministic and\n * platform-independent, so the guard behaves identically on every OS and\n * is testable on any runner.\n * - {@link isShellParseAbortOutput} inspects shell OUTPUT after a failure,\n * as the runtime safety net for shapes the static patterns don't cover\n * (e.g. a stray `-H` from a mangled command line).\n */\n\n/**\n * Whether shell output is the signature of a shell ABORTING WHILE PARSING the\n * command — i.e. nothing ever ran.\n *\n * Both signatures are unambiguous: cmd.exe's \"<token> was unexpected at this\n * time.\" and Windows PowerShell 5.1's \"'&&' is not a valid statement\n * separator in this version\" (which appears when a user's own script layer\n * re-enters PowerShell — Tempo itself never spawns PowerShell).\n *\n * Shared by the streaming hint classifier (hint-classifier.ts) and\n * `classifyInstallFailure` (tempo-devserver.ts), so the install and start\n * paths agree on what a shell-dialect mismatch looks like.\n */\nexport function isShellParseAbortOutput(output: string): boolean {\n return (\n /\\bwas unexpected at this time\\b/i.test(output) ||\n /is not a valid statement separator/i.test(output)\n );\n}\n\n/**\n * Command shapes that are valid in PowerShell but NOT in cmd.exe (nor in\n * /bin/sh). Deliberately matches only PowerShell-specific tokens — never the\n * cmd-native `if exist <file> (...) else (...)` form, `&&` chaining, or\n * `pushd`, all of which run fine on cmd.exe.\n */\nconst POWERSHELL_COMMAND_SHAPES: ReadonlyArray<{\n construct: string;\n pattern: RegExp;\n}> = [\n { construct: \"the `Test-Path` cmdlet\", pattern: /\\bTest-Path\\b/i },\n {\n // `if (...) {`, `foreach (...) {`, `while (...) {` — parenthesised\n // condition followed by a `{` block. cmd wraps bodies in `(`, never `{`,\n // and its `if exist <file> (...)` form is `if ` + a keyword, never `if (`.\n construct: \"a parenthesised condition with a `{ … }` block\",\n pattern: /\\b(?:if|foreach|while)\\s*\\([^)]*\\)\\s*\\{/i,\n },\n {\n // Brace-form else (`} else`, `else {`) — cmd's else is the paren form\n // `) else (`, which has no brace.\n construct: \"a brace-form `else` block\",\n pattern: /\\}\\s*else\\b|\\belse\\s*\\{/i,\n },\n {\n // PowerShell env / automatic variables: `$env:FOO`, `$PSItem`, `$_`.\n construct: \"a PowerShell variable (`$env:`/`$PSItem`/`$_`)\",\n pattern: /\\$(?:env:|PSItem\\b|_\\b)/i,\n },\n {\n // PowerShell cmdlets in Verb-Noun form: `Get-ChildItem`, `Set-Location`,\n // `Invoke-WebRequest`, … — under cmd.exe (and sh) these are \"not\n // recognized as a command\", so nothing runs. The verb list is CURATED to\n // the cmdlets an agent plausibly writes into a dev/install command, and\n // the match is CASE-SENSITIVE PascalCase on purpose: that's how cmdlets\n // are conventionally authored (the actual failure mode we've seen), and\n // it keeps lowercase real-world tokens like `apt-get` or a\n // `https://get-thing.dev` URL from ever matching. `Test-Path` has its own\n // shape above.\n construct: \"a PowerShell cmdlet (Verb-Noun form like `Get-ChildItem`)\",\n pattern:\n /\\b(?:Get|Set|New|Remove|Copy|Move|Rename|Start|Stop|Invoke|Write|Read|Out|Select|Where|ForEach|Import|Export|Add|Clear)-[A-Z][A-Za-z]+\\b/,\n },\n];\n\n/**\n * Blank out single- and double-quoted spans so the shape patterns only see\n * the parts of the command the SHELL parses as syntax. Quoted text is an\n * opaque argument to both cmd.exe and sh — `node -e \"if (x) { y() }\"` is a\n * perfectly portable command whose braces never reach the shell's grammar,\n * and `powershell -Command \"if (Test-Path …) { … }\"` is the legitimate way\n * to run PowerShell explicitly. Treating an unterminated quote as running to\n * the end of the string errs toward NOT rejecting.\n *\n * Backslash escapes are honored the way they bias toward NOT rejecting:\n * inside a double-quoted span `\\\"` does not close the span (so\n * `node -e \"console.log(\\\"Test-Path\\\")\"` stays one opaque argument), and\n * outside quotes an escaped character is consumed as a literal (so `\\\"`\n * doesn't OPEN a span). Single-quoted spans have no escapes, matching sh.\n * cmd.exe's own escape is `^`, not backslash, but a backslash-quote pair is\n * argv-level quoting there too (CommandLineToArgvW), so this reading is\n * safe for both shells.\n */\nfunction stripQuotedSpans(command: string): string {\n let result = \"\";\n let quote: '\"' | \"'\" | null = null;\n for (let i = 0; i < command.length; i += 1) {\n const ch = command[i];\n if (quote === '\"') {\n if (ch === \"\\\\\") {\n i += 1;\n continue;\n }\n if (ch === '\"') quote = null;\n continue;\n }\n if (quote === \"'\") {\n if (ch === \"'\") quote = null;\n continue;\n }\n if (ch === \"\\\\\") {\n result += \" \";\n i += 1;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n result += \" \";\n continue;\n }\n result += ch;\n }\n return result;\n}\n\n/**\n * If `command` contains PowerShell-only syntax (outside quoted spans), return\n * a human-readable description of the offending construct; otherwise null.\n *\n * A match means the command CANNOT run under any shell Tempo spawns with —\n * callers should refuse to spawn it and surface a rewrite-the-command error\n * instead of letting the shell abort with a cryptic parse error.\n */\nexport function findPowerShellSyntax(command: string): string | null {\n const unquoted = stripQuotedSpans(command);\n for (const shape of POWERSHELL_COMMAND_SHAPES) {\n if (shape.pattern.test(unquoted)) {\n return shape.construct;\n }\n }\n return null;\n}\n\n/**\n * The standard \"you wrote PowerShell\" detail sentence for pre-spawn\n * rejections, shared by the install / start / appStart guards so the three\n * surfaces describe the same problem the same way.\n */\nexport function describePowerShellRejection(construct: string): string {\n return `Tempo rejected it before running anything: the command contains PowerShell-only syntax (${construct}), which the shell Tempo spawns commands with (cmd.exe on Windows) cannot parse.`;\n}\n\n/**\n * Lift leading POSIX-style `NAME=value` environment prefixes off a command,\n * returning the remaining command plus the assignments as spawn-env entries.\n *\n * `PORT=\\${APP_PORT} npm run dev` is a natural app start for frameworks that\n * read their port from the environment (CRA, Remix). POSIX sh applies the\n * prefix itself; cmd.exe has no such syntax and parses `PORT=3001` as a\n * program name (\"'PORT=3001' is not recognized…\"), killing the command\n * before it starts. Doing the assignment here, in the spawner, makes the\n * form work identically under every shell instead of banning the only\n * natural spelling.\n *\n * Deliberately conservative: only plain-literal values (no quotes, `$`,\n * backticks, backslashes, or whitespace) are lifted, and lifting STOPS at\n * the first token that isn't one. Anything left un-lifted still reaches the\n * shell, where sh applies it exactly as before — so on POSIX this is always\n * semantics-preserving, and on Windows it converts the common forms from a\n * guaranteed parse failure into a working command. Callers must merge the\n * returned env OVER their base env: sh gives a command-line assignment\n * precedence over the inherited environment, and the lift must match.\n * (One knowing divergence: for `A=1 cmd1 && cmd2`, sh scopes A to cmd1\n * while the lifted env reaches cmd2 too — a strict superset that hasn't a\n * plausible breaking case in a dev/install command.)\n */\nexport function extractLeadingEnvAssignments(command: string): {\n command: string;\n env: Record<string, string>;\n} {\n const env: Record<string, string> = {};\n let rest = command;\n // `NAME=value ` followed by more command. The value charset excludes every\n // shell-interpreted character; `(?=\\S)` refuses to strip a command down to\n // nothing (a bare `FOO=bar` line is a valid sh command we leave alone).\n const prefixPattern = /^([A-Za-z_][A-Za-z0-9_]*)=([^\\s\"'`$\\\\]*)\\s+(?=\\S)/;\n for (;;) {\n const match = prefixPattern.exec(rest);\n if (!match) break;\n env[match[1]] = match[2];\n rest = rest.slice(match[0].length);\n }\n if (Object.keys(env).length === 0) {\n return { command, env };\n }\n return { command: rest, env };\n}\n","import {\n isDevServerLockConflictOutput,\n parseDevServerLockConflict,\n} from \"./dev-lock-conflict\";\nimport type { DevServerHint, DevServerHintClass } from \"./types\";\nimport { isShellParseAbortOutput } from \"./shell-dialect\";\n\n// Re-exported so existing importers of the output detector keep working; the\n// implementation lives in shell-dialect.ts next to the command-string guard.\nexport { isShellParseAbortOutput } from \"./shell-dialect\";\n\n/**\n * Inspect a stdout/stderr line from the user's dev server and, if it matches\n * a known user-actionable pattern, return a structured `DevServerHint` so\n * the renderer can surface a clear message and CTA instead of leaving the\n * canvas on indefinite shimmer.\n *\n * Returns `null` for the vast majority of lines (regular Vite/Next compile\n * output). Designed to be cheap to call on every stdout line.\n *\n * Patterns are intentionally biased toward \"the dev server is up but the\n * user's build is failing\" — the cases the UI needs to surface and the\n * user needs to recover from. Positive \"ready\" messages are NOT classified\n * here because the URL-detection path already covers them.\n */\nexport function classifyDevServerOutput(line: string): DevServerHint | null {\n const match = matchClass(line);\n if (!match) return null;\n return {\n class: match.class,\n hint: match.hint,\n ...(match.errorCode ? { errorCode: match.errorCode } : {}),\n ...(match.metadata ? { metadata: match.metadata } : {}),\n // Trim and clip the raw line so we can show it without flooding the UI.\n // 500 chars is enough for Vite's typical \"Failed to resolve import\" with\n // a snippet of the failing source.\n line: line.trim().slice(0, 500),\n observedAt: Date.now(),\n };\n}\n\n/**\n * The hint for a dev-server child that is still alive but has stopped\n * answering HTTP at its URL for a sustained streak of health probes. Not\n * classified from output — the supervisor observes it from outside the\n * process and records it through the same hint channel, so the renderer's\n * loading toolbar gets a concrete cause instead of an open-ended \"Starting\n * render host window\" (diagnostic t175n51yhjbemx3dxvf2a7n4j18dq9dy).\n */\nexport function buildHostUnresponsiveHint(\n url: string,\n unreachableSinceMs: number,\n nowMs: number = Date.now(),\n): DevServerHint {\n const unreachableForSeconds = Math.max(\n 0,\n Math.round((nowMs - unreachableSinceMs) / 1000),\n );\n return {\n class: \"host-unresponsive\",\n hint: `The canvas dev server is running but has stopped answering requests at ${url} (no response for ${unreachableForSeconds}s). It may be wedged after a dependency change or an interrupted install; retrying restarts it.`,\n errorCode: \"not_reachable\",\n line: `url health probe failed: ${url}`,\n observedAt: nowMs,\n };\n}\n\nfunction matchClass(\n line: string,\n): Pick<DevServerHint, \"class\" | \"hint\" | \"metadata\" | \"errorCode\"> | null {\n // The command never executed, so this is a config bug, not a build failure.\n if (isShellParseAbortOutput(line)) {\n return {\n class: \"shell-dialect-mismatch\",\n hint: \"The dev command failed to PARSE in the shell (Tempo runs commands through cmd.exe on Windows) — nothing actually ran. This almost always means the command was written in PowerShell syntax, which cmd.exe cannot parse: `if (...) { ... } else { ... }`, `Test-Path`, `$env:FOO`. Rewrite it as a single, plain, cross-platform invocation with no shell conditionals — e.g. `npm exec next dev -p ${APP_PORT}` or simply `npm run dev` when it does not need Tempo's optional allocated helper port (legacy `${PORT}` also works). Chaining with `&&` is fine; cmd supports it. If the app lives in a subdirectory, set `appDir` in tempo.config.json instead of `cd`/`pushd`-ing inside the command.\",\n };\n }\n\n // Next.js 16's per-directory dev-lock abort. Another `next dev` owns the\n // selected build directory, so this start can be rejected after printing\n // \"✓ Ready\". The PID is diagnostic only: the supervisor may act on it only\n // when persisted pid/birth identity proves it belongs to Tempo.\n if (isDevServerLockConflictOutput(line)) {\n const conflict = parseDevServerLockConflict(line);\n return {\n class: \"dev-lock-conflict\",\n hint: conflict\n ? `Another dev server (PID ${conflict.pid}) is already running in this project's canvas folder and is holding Next.js's dev lock. Tempo will retry in an isolated build directory; if the canvas doesn't come up, close the other window or terminal running this project.`\n : \"Another dev server is already running in this project's canvas folder and is holding Next.js's dev lock. Tempo is retrying; if the canvas doesn't come up, close other windows or terminals running this project.\",\n ...(conflict\n ? {\n metadata: {\n lockHolderPid: conflict.pid,\n ...(conflict.port != null\n ? { lockHolderPort: conflict.port }\n : {}),\n },\n }\n : {}),\n };\n }\n\n // Port collision. Vite logs each scanned port; the user-actionable signal\n // is the FIRST `Port N is in use` — once the cascade stops, Vite has\n // found a free port and is fine, so we don't escalate the cascade itself.\n if (/EADDRINUSE|address already in use/i.test(line)) {\n return {\n class: \"port-in-use\",\n hint: \"Another process is holding the dev server's port. Tempo will retry standard Vite/Next starts on a free local port; custom start scripts may need their port changed.\",\n };\n }\n\n if (\n /(?:'|\")?(?:npm|pnpm|yarn|bun)(?:'|\")? is not recognized as an internal or external command|command not found:\\s*(?:npm|pnpm|yarn|bun)|(?:npm|pnpm|yarn|bun): command not found/i.test(\n line,\n )\n ) {\n return {\n class: \"tool-missing\",\n hint: \"The package manager required by this project was not found on PATH. Install it or enable it with Corepack, then retry the canvas.\",\n };\n }\n\n if (isUnsupportedHostNodeOutput(line)) {\n return {\n class: \"node-version-unsupported\",\n hint: \"The dev server is using an unsupported Node.js version. Install the version required by Vite/Next or update PATH so Tempo resolves it.\",\n errorCode: \"node_version_unsupported\",\n };\n }\n\n const packageSubpathExportMatch = line.match(\n /Missing\\s+[\"'](\\.\\/[^\"']+)[\"']\\s+specifier\\s+in\\s+[\"']([^\"']+)[\"']/i,\n );\n if (packageSubpathExportMatch) {\n const [, subpath, packageName] = packageSubpathExportMatch;\n const importPath = `${packageName}${subpath.slice(1)}`;\n return {\n class: \"package-subpath-export-missing\",\n hint: `Vite could not resolve package subpath ${importPath}. Fix the import path or the package export map; this is a package export issue, not a local install strategy issue.`,\n };\n }\n\n if (\n /ERESOLVE|unable to resolve dependency tree|could not resolve/i.test(line)\n ) {\n return {\n class: \"dependency-conflict\",\n hint: \"Package installation hit a dependency resolution conflict. Tempo avoids strict CI installs locally; if this persists, package.json or the lockfile needs a targeted dependency fix.\",\n };\n }\n\n if (\n /(?:EPERM|EACCES).*?(?:unlink|rename|rmdir).*?(?:node_modules|\\.node|lightningcss|esbuild|swc)|operation not permitted.*?(?:node_modules|\\.node|lightningcss|esbuild|swc)/i.test(\n line,\n )\n ) {\n return {\n class: \"native-binary-locked\",\n hint: \"A native dependency binary in node_modules is locked by another process. Close running dev servers/editors using it and retry; Tempo will avoid unnecessary root reinstalls.\",\n };\n }\n\n if (\n /npm ci|frozen-lockfile|immutable|ERR_PNPM_OUTDATED_LOCKFILE|package-lock\\.json.*out of sync/i.test(\n line,\n )\n ) {\n return {\n class: \"lockfile-install-strict\",\n hint: \"A strict CI-style install failed because the lockfile is stale. Tempo local canvas startup should use non-destructive install commands instead.\",\n };\n }\n\n // Module not found — the highest-leverage class. Vite/Next phrasings:\n // \"Failed to resolve import \\\"<pkg>\\\" from <file>\"\n // \"Module not found: Can't resolve '<pkg>'\"\n // \"Module not found: Error: Can't resolve '<pkg>'\"\n const moduleNotFoundMatch = line.match(\n /(?:Failed to resolve import|Module not found:?\\s+(?:Can't resolve|Error:?\\s+Can't resolve))\\s+[\"']?([^\"'\\s\\n]+)/i,\n );\n if (moduleNotFoundMatch) {\n const pkg = moduleNotFoundMatch[1];\n const sourceImportKind = classifySourceImportSpecifier(pkg);\n if (sourceImportKind) {\n const importer = extractImporterFromLine(line);\n return {\n class: \"source-import-unresolved\",\n hint: `Unresolved source import: ${pkg}${importer ? ` from ${importer}` : \"\"}. This is not an npm package; fix the import path, restore the missing source file, or update the canvas wrapper to import an existing file.`,\n metadata: {\n importSpecifier: pkg,\n importer,\n importKind: sourceImportKind,\n },\n };\n }\n if (isTailwindDependency(pkg)) {\n return {\n class: \"module-not-found\",\n hint: `Tailwind dependency missing: ${pkg}. Run install from Tempo or reinstall project dependencies, then retry the canvas.`,\n };\n }\n return {\n class: \"module-not-found\",\n hint: `Missing dependency: ${pkg}. Install it at the project root (e.g. \\`bun install\\`, \\`pnpm install\\`, \\`npm install\\`) or add it to package.json and retry.`,\n };\n }\n\n // Tailwind config change — the LSP index Tempo's tailwind-class-provider\n // built at startup is now stale and needs to rebuild. Users hitting this\n // after editing tailwind.config.* will see autocomplete drift.\n if (\n /tailwind\\.config\\.(?:js|ts|cjs|mjs)/i.test(line) &&\n /(?:changed|updated|reloaded|invalidat)/i.test(line)\n ) {\n return {\n class: \"tailwind-config-change\",\n hint: \"tailwind.config.* changed. Class autocomplete and color picker may show stale results until the dev server is restarted.\",\n };\n }\n\n if (\n /(?:Cannot apply unknown utility class|class [`\"'][^`\"']+[`\"'] does not exist|Unknown utility class)/i.test(\n line,\n )\n ) {\n return {\n class: \"tailwind-css-error\",\n hint: \"Tailwind CSS could not resolve a utility from global CSS. Tempo checks the app's global CSS, tailwind.config.*, and Tailwind source paths during setup; restart the canvas after repair or inspect the CSS error line.\",\n };\n }\n\n if (/No utility classes were detected in your source files/i.test(line)) {\n return {\n class: \"tailwind-source-missing\",\n hint: \"Tailwind did not find any source files to scan. Tempo's setup should write @source entries for src/app/components/packages and canvas files; restart the canvas so the repaired stylesheet is used.\",\n };\n }\n\n if (\n /Failed to load font|downloadable font|OTS parsing error|(?:font|woff2?|ttf|otf).*ERR_/i.test(\n line,\n )\n ) {\n return {\n class: \"font-load-failure\",\n hint: \"Font failed to load. Check the font URL/path and CORS headers; Tempo allows stylesheet and font CORS in the projection, but missing files or blocked remote font hosts still need app-side fixes.\",\n };\n }\n\n if (/Syntax\\s*Error|SyntaxError:|Unexpected token/i.test(line)) {\n return {\n class: \"syntax-error\",\n hint: \"A source file has a syntax error. Check the dev server output (or the error overlay on the affected storyboard) for the file and position.\",\n };\n }\n\n if (\n /JavaScript heap out of memory|FATAL ERROR.*Allocation failed|ENOMEM/i.test(\n line,\n )\n ) {\n return {\n class: \"out-of-memory\",\n hint: \"The dev server ran out of memory. Bump NODE_OPTIONS=--max-old-space-size=8192 or reduce the repo's scanned scope (tsconfig include paths).\",\n };\n }\n\n if (/EACCES|EPERM|permission denied/i.test(line)) {\n return {\n class: \"permission-denied\",\n hint: \"A file or port required by the dev server is not accessible to this process. Check ownership of node_modules and the dev server's port.\",\n };\n }\n\n // pnpm refusing to manipulate node_modules without a TTY. `buildManagedCommandEnv`\n // now sets CI=1 to prevent this; the classifier remains as a safety net\n // (and to give the user a clear message if it ever reappears).\n if (/ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY/.test(line)) {\n return {\n class: \"pnpm-needs-ci\",\n hint: \"pnpm refused to manipulate node_modules without a TTY. Tempo normally sets CI=1 — if you see this, the env was wiped. File a bug.\",\n };\n }\n\n // Generic compile error catch-all (Vite/Next emit lines starting with\n // \"error \" or \"✘\"/\"❌\"/\"✗\"). Less actionable than the specific classes\n // above, but better than silence for novel error shapes.\n if (/^\\s*(?:error|✘|❌|✗)\\s/i.test(line) && line.length > 20) {\n return {\n class: \"compile-error\",\n hint: \"The dev server reported a compile error. Open the affected storyboard to see the error overlay, or check the terminal for the full stack.\",\n };\n }\n\n return null;\n}\n\nconst DIRECT_HOST_NODE_FAILURES = [\n /You are using Node\\.js .*Vite requires Node\\.js version/i,\n /requires Node\\.js version/i,\n /Unsupported engine/i,\n /crypto\\.hash is not a function/i,\n /(?:node|node\\.exe): (?:command not found|not found)/i,\n /['\"]node['\"] is not recognized as an internal or external command/i,\n /spawn node ENOENT/i,\n /\\/usr\\/bin\\/env:\\s*['\"]?node['\"]?:\\s*No such file or directory/i,\n];\n\nfunction isViteRuntimeFrame(line: string): boolean {\n return /[\\\\/]node_modules[\\\\/]vite[\\\\/]|[\\\\/]vite[\\\\/]dist[\\\\/]node/i.test(\n line,\n );\n}\n\nfunction isUnsupportedHostNodeOutput(output: string): boolean {\n if (DIRECT_HOST_NODE_FAILURES.some((pattern) => pattern.test(output))) {\n return true;\n }\n\n const lines = output.split(/\\r?\\n/);\n if (\n lines.some(\n (candidate, index) =>\n /ERR_REQUIRE_ESM/i.test(candidate) &&\n [candidate, ...lines.slice(index + 1, index + 4)].some(\n isViteRuntimeFrame,\n ),\n )\n ) {\n return true;\n }\n\n // Older Node releases fail while parsing Vite's own modern syntax. Require\n // both a modern-token parse error and a nearby Vite runtime stack frame;\n // an arbitrary SyntaxError elsewhere in an application is not toolchain\n // evidence and must remain a normal compile error.\n return lines.some(\n (candidate, index) =>\n /^\\s*SyntaxError:\\s*Unexpected (?:token|reserved word)/i.test(\n candidate,\n ) &&\n /\\?\\?=|\\?\\.|&&=|\\|\\|=|Unexpected reserved word/i.test(candidate) &&\n lines.slice(index + 1, index + 4).some(isViteRuntimeFrame),\n );\n}\n\nfunction isTailwindDependency(pkg: string): boolean {\n return pkg === \"tailwindcss\" || pkg.startsWith(\"@tailwindcss/\");\n}\n\nfunction classifySourceImportSpecifier(\n specifier: string,\n): \"source-alias\" | \"relative\" | \"absolute\" | null {\n if (specifier.startsWith(\"@/\") || specifier.startsWith(\"~/\")) {\n return \"source-alias\";\n }\n if (specifier.startsWith(\"./\") || specifier.startsWith(\"../\")) {\n return \"relative\";\n }\n if (specifier.startsWith(\"/\")) {\n return \"absolute\";\n }\n return null;\n}\n\nfunction extractImporterFromLine(line: string): string | null {\n const fromMatch = /\\sfrom\\s+[\"']([^\"']+)[\"']/i.exec(line);\n if (fromMatch?.[1]) {\n return normalizeImporterPath(fromMatch[1]);\n }\n\n const stackMatch =\n /(?:^|\\n|[\\s([])(\\.{0,2}\\/[^:\\n]+?\\.(?:[cm]?[jt]sx?|css|scss|sass|less|mdx?)):\\d+:\\d+/i.exec(\n line,\n );\n if (stackMatch?.[1]) {\n return normalizeImporterPath(stackMatch[1]);\n }\n\n return null;\n}\n\nfunction normalizeImporterPath(path: string): string {\n return path.replace(/^\\.\\//, \"\");\n}\n","/**\n * Resolve which process is LISTENING on a local TCP port.\n *\n * Why the supervisor needs this: on Windows the app-dev-server child is\n * spawned through a shell (`cmd.exe` → `bunx` → `node next dev`), and the\n * only pid Node hands back is the shell's. The pid that actually owns the\n * listening socket — the one that must die for the port (and Next.js 16's\n * per-directory dev lock) to be released — is a grandchild the supervisor\n * can't see. Resolving the listener by port at ready-time gives the\n * supervisor a durable handle on the REAL server process, which it persists\n * so both in-session teardown and a future session's orphan reaper can kill\n * the tree even after the shell wrapper is long gone.\n *\n * Pure parsers are exported separately from the process-spawning resolver so\n * they can be unit-tested exhaustively against captured fixtures.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Parse `netstat -ano` output (win32) for the pid LISTENING on `port`.\n * Handles IPv4 (`0.0.0.0:3000`, `127.0.0.1:3000`) and bracketed IPv6\n * (`[::]:3000`, `[::1]:3000`) local addresses — Windows labels both row\n * families \"TCP\". UDP rows are skipped by protocol and column shape.\n * Returns the first LISTENING match, or `null` when nothing is listening\n * on that port.\n *\n * Sample row (the column layout is locale-stable; the state word is not,\n * which is why we also accept ABHÖREN etc. by matching on column count +\n * a trailing integer pid — see the fallback branch):\n *\n * TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 16896\n */\nexport function parseNetstatListenerPid(\n output: string,\n port: number,\n): number | null {\n if (!output) return null;\n for (const line of output.split(/\\r?\\n/)) {\n const cols = line.trim().split(/\\s+/);\n // TCP rows: proto, local, foreign, state, pid (5 columns).\n if (cols.length !== 5) continue;\n if (cols[0].toUpperCase() !== \"TCP\") continue;\n const local = cols[1];\n // Local address ends in `:<port>` — works for `0.0.0.0:3000` and\n // `[::]:3000` alike.\n if (!local.endsWith(`:${port}`)) continue;\n // Guard against port-suffix false positives (`:13000` ends with\n // `:3000`? no — endsWith includes the colon, so `:13000` does NOT end\n // with `:3000`. The colon anchor makes this exact.)\n const pid = Number(cols[4]);\n if (!Number.isInteger(pid) || pid <= 0) continue;\n // Only LISTENING rows own the port; ESTABLISHED rows on the same port\n // are clients. The state word is locale-dependent on Windows, but every\n // locale's \"listening\" translation is the only state that appears with\n // a foreign address of `0.0.0.0:0` / `[::]:0`. Accept the English word\n // OR the zero foreign-address shape.\n const state = cols[3].toUpperCase();\n const foreign = cols[2];\n const isListening =\n state === \"LISTENING\" ||\n foreign === \"0.0.0.0:0\" ||\n foreign === \"[::]:0\" ||\n foreign === \"*:*\";\n if (!isListening) continue;\n return pid;\n }\n return null;\n}\n\n/**\n * Parse `lsof -t` output (POSIX) — one pid per line; take the first.\n */\nexport function parseLsofListenerPid(output: string): number | null {\n if (!output) return null;\n for (const line of output.split(/\\r?\\n/)) {\n const pid = Number(line.trim());\n if (Number.isInteger(pid) && pid > 0) return pid;\n }\n return null;\n}\n\n/**\n * Resolve the pid LISTENING on local TCP `port`, or `null` when the port is\n * free / the probe tool is unavailable. Best-effort by design: callers use\n * the result to reap orphans, and a `null` simply means \"no handle — fall\n * back to port probing\".\n */\nexport async function findPortListenerPid(\n port: number,\n): Promise<number | null> {\n try {\n if (process.platform === \"win32\") {\n // No `-p tcp` filter: on Windows that restricts output to IPv4 TCP\n // only, making an IPv6-bound listener ([::1]:<port>) invisible to the\n // reap. Unfiltered output includes TCPv6 rows (still labeled \"TCP\",\n // bracketed local address — the parser handles both) and UDP rows,\n // which the parser skips by protocol/column shape.\n const { stdout } = await execFileAsync(\"netstat\", [\"-ano\"], {\n windowsHide: true,\n maxBuffer: 4 * 1024 * 1024,\n });\n return parseNetstatListenerPid(stdout, port);\n }\n const { stdout } = await execFileAsync(\n \"lsof\",\n [\"-nP\", \"-t\", `-iTCP:${port}`, \"-sTCP:LISTEN\"],\n { windowsHide: true },\n );\n return parseLsofListenerPid(stdout);\n } catch {\n // netstat/lsof missing, exited non-zero (lsof exits 1 on no matches),\n // or permission trouble — treat all as \"no listener found\".\n return null;\n }\n}\n","import {\n appendFile,\n mkdir,\n open,\n rename,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\ninterface RotatingFileLogOptions {\n filePath: string;\n maxBytesPerFile: number;\n}\n\ntype PendingOperation =\n | { kind: \"append\"; data: Buffer }\n | { kind: \"dropped\"; bytes: number }\n | { kind: \"reset\"; done: () => void }\n | { kind: \"flush\"; done: () => void }\n | {\n kind: \"read\";\n tailBytes: number;\n done: (result: RotatingLogReadResult) => void;\n };\n\n/**\n * A best-effort, two-file output journal with one owner and one ordered write\n * queue. Callers never coordinate rotation with appends themselves: `append`,\n * `reset`, and `flush` all cross the same seam, so their ordering is the order\n * in which they were called.\n *\n * Pending append payload is also bounded to one file's worth of data. If the\n * disk falls behind a noisy child process, the oldest not-yet-written chunks\n * are discarded in favour of the newest diagnostic output instead of allowing\n * logging to consume unbounded memory or backpressure the supervised process.\n */\nexport class RotatingFileLog {\n private readonly filePath: string;\n private readonly priorFilePath: string;\n private readonly maxBytesPerFile: number;\n private readonly pending: PendingOperation[] = [];\n private pendingAppendBytes = 0;\n private draining = false;\n private currentSizeBytes: number | null = null;\n\n constructor({ filePath, maxBytesPerFile }: RotatingFileLogOptions) {\n if (!Number.isSafeInteger(maxBytesPerFile) || maxBytesPerFile <= 0) {\n throw new RangeError(\"maxBytesPerFile must be a positive safe integer\");\n }\n this.filePath = filePath;\n this.priorFilePath = `${filePath}.1`;\n this.maxBytesPerFile = maxBytesPerFile;\n }\n\n /** Queue raw output without blocking the producer. Never throws. */\n append(data: string | Buffer): void {\n const buffer = Buffer.from(data);\n if (buffer.length === 0) return;\n\n // A single child-process chunk can be arbitrarily large. Retaining its tail\n // makes the per-file limit exact while keeping the newest diagnostics.\n if (buffer.length > this.maxBytesPerFile) {\n this.pending.push({\n kind: \"dropped\",\n bytes: buffer.length - this.maxBytesPerFile,\n });\n }\n const bounded =\n buffer.length > this.maxBytesPerFile\n ? buffer.subarray(buffer.length - this.maxBytesPerFile)\n : buffer;\n this.pending.push({ kind: \"append\", data: bounded });\n this.pendingAppendBytes += bounded.length;\n this.trimPendingAppends();\n this.startDrain();\n }\n\n /**\n * Delete active + prior history after every operation already queued, before\n * any operation queued later. Always resolves; logging remains best-effort.\n */\n reset(): Promise<void> {\n return new Promise((done) => {\n this.pending.push({ kind: \"reset\", done });\n this.startDrain();\n });\n }\n\n /** Resolve once every operation queued before this call has settled. */\n flush(): Promise<void> {\n return new Promise((done) => {\n this.pending.push({ kind: \"flush\", done });\n this.startDrain();\n });\n }\n\n /** Read a snapshot ordered after prior writes and before later writes. */\n readTail(tailBytes: number): Promise<RotatingLogReadResult> {\n return new Promise((done) => {\n this.pending.push({ kind: \"read\", tailBytes, done });\n this.startDrain();\n });\n }\n\n private trimPendingAppends(): void {\n while (this.pendingAppendBytes > this.maxBytesPerFile) {\n const oldestAppendIndex = this.pending.findIndex(\n (operation) => operation.kind === \"append\",\n );\n if (oldestAppendIndex === -1) return;\n const [removed] = this.pending.splice(oldestAppendIndex, 1);\n if (removed.kind === \"append\") {\n this.pendingAppendBytes -= removed.data.length;\n const preceding = this.pending[oldestAppendIndex - 1];\n if (preceding?.kind === \"dropped\") {\n preceding.bytes += removed.data.length;\n } else {\n this.pending.splice(oldestAppendIndex, 0, {\n kind: \"dropped\",\n bytes: removed.data.length,\n });\n }\n }\n }\n }\n\n private startDrain(): void {\n if (this.draining) return;\n this.draining = true;\n void this.drain();\n }\n\n private async drain(): Promise<void> {\n try {\n while (this.pending.length > 0) {\n const operation = this.pending.shift()!;\n if (operation.kind === \"append\") {\n this.pendingAppendBytes -= operation.data.length;\n await this.appendNow(operation.data);\n } else if (operation.kind === \"dropped\") {\n await this.appendNow(\n Buffer.from(\n `\\n[Tempo output journal dropped ${operation.bytes} bytes to stay memory-bounded]\\n`,\n ),\n );\n } else if (operation.kind === \"reset\") {\n await this.resetNow();\n operation.done();\n } else if (operation.kind === \"flush\") {\n operation.done();\n } else {\n operation.done(\n await readRotatingLogTail(this.filePath, operation.tailBytes),\n );\n }\n }\n } finally {\n this.draining = false;\n // An append can arrive after the loop observes an empty queue but before\n // the async continuation reaches this finally block.\n if (this.pending.length > 0) this.startDrain();\n }\n }\n\n private async appendNow(data: Buffer): Promise<void> {\n try {\n // Internal records (notably the dropped-output marker) pass through this\n // method too, so enforce the physical file bound at the final write seam\n // rather than relying only on the public `append` input path.\n const bounded =\n data.length > this.maxBytesPerFile\n ? data.subarray(data.length - this.maxBytesPerFile)\n : data;\n await this.initializeCurrentSize();\n if (this.currentSizeBytes! + bounded.length > this.maxBytesPerFile) {\n if (!(await this.rotateNow())) return;\n }\n await appendFile(this.filePath, bounded);\n this.currentSizeBytes = (this.currentSizeBytes ?? 0) + bounded.length;\n } catch {\n // Re-read on the next append rather than trusting stale accounting after\n // a partial or externally interrupted filesystem operation.\n this.currentSizeBytes = null;\n }\n }\n\n private async initializeCurrentSize(): Promise<void> {\n if (this.currentSizeBytes !== null) return;\n await mkdir(dirname(this.filePath), { recursive: true });\n try {\n const current = await stat(this.filePath);\n if (current.size <= this.maxBytesPerFile) {\n this.currentSizeBytes = current.size;\n return;\n }\n\n // Normalize an oversized file left by an older build before it can become\n // the rotated half of this writer's bounded footprint.\n const tail = await readFileTail(this.filePath, this.maxBytesPerFile);\n await writeFile(this.filePath, tail);\n this.currentSizeBytes = tail.length;\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n this.currentSizeBytes = 0;\n return;\n }\n throw error;\n }\n }\n\n private async rotateNow(): Promise<boolean> {\n try {\n await rm(this.priorFilePath, { force: true });\n } catch {\n // The rename below is the authoritative check. Some platforms can still\n // replace the destination even if an explicit removal was denied.\n }\n try {\n await rename(this.filePath, this.priorFilePath);\n this.currentSizeBytes = 0;\n return true;\n } catch {\n // If rotation is blocked, discard the active half. If that is blocked as\n // well, discard this new diagnostic chunk instead of exceeding the cap.\n try {\n await rm(this.filePath, { force: true });\n this.currentSizeBytes = 0;\n return true;\n } catch {\n this.currentSizeBytes = null;\n return false;\n }\n }\n }\n\n private async resetNow(): Promise<void> {\n try {\n await mkdir(dirname(this.filePath), { recursive: true });\n } catch {}\n const [activeRemoval] = await Promise.allSettled([\n rm(this.filePath, { force: true }),\n rm(this.priorFilePath, { force: true }),\n ]);\n this.currentSizeBytes = activeRemoval.status === \"fulfilled\" ? 0 : null;\n }\n}\n\nexport interface RotatingLogReadResult {\n content: string | null;\n totalSizeBytes: number | null;\n updatedAt: number | null;\n}\n\n/** Read the tail of `<path>.1` + `<path>` as one chronological log. */\nexport async function readRotatingLogTail(\n filePath: string,\n tailBytes: number,\n): Promise<RotatingLogReadResult> {\n try {\n return await readRotatingLogTailOnce(filePath, tailBytes);\n } catch {\n // Rotation can rename both paths between stat and open when no live writer\n // owns the read (for example diagnostics from a prior session). Retry once\n // against the new pair, then degrade to an unavailable journal.\n try {\n return await readRotatingLogTailOnce(filePath, tailBytes);\n } catch {\n return { content: null, totalSizeBytes: null, updatedAt: null };\n }\n }\n}\n\nasync function readRotatingLogTailOnce(\n filePath: string,\n tailBytes: number,\n): Promise<RotatingLogReadResult> {\n const requestedBytes = Number.isFinite(tailBytes)\n ? Math.max(0, Math.floor(tailBytes))\n : Number.MAX_SAFE_INTEGER;\n const candidates = [`${filePath}.1`, filePath];\n const existing: Array<{ path: string; size: number; mtimeMs: number }> = [];\n for (const candidate of candidates) {\n try {\n const fileStat = await stat(candidate);\n if (fileStat.isFile()) {\n existing.push({\n path: candidate,\n size: fileStat.size,\n mtimeMs: fileStat.mtimeMs,\n });\n }\n } catch {}\n }\n if (existing.length === 0) {\n return { content: null, totalSizeBytes: null, updatedAt: null };\n }\n\n let remaining = Math.min(\n requestedBytes,\n existing.reduce((total, file) => total + file.size, 0),\n );\n const chunks: Buffer[] = [];\n for (const file of [...existing].reverse()) {\n if (remaining === 0) break;\n const length = Math.min(remaining, file.size);\n chunks.unshift(await readFileTail(file.path, length));\n remaining -= length;\n }\n\n return {\n content: Buffer.concat(chunks).toString(\"utf-8\"),\n totalSizeBytes: existing.reduce((total, file) => total + file.size, 0),\n updatedAt: Math.max(...existing.map((file) => file.mtimeMs)),\n };\n}\n\nasync function readFileTail(\n filePath: string,\n byteCount: number,\n): Promise<Buffer> {\n if (byteCount <= 0) return Buffer.alloc(0);\n const fileStat = await stat(filePath);\n const length = Math.min(byteCount, fileStat.size);\n const buffer = Buffer.alloc(length);\n const file = await open(filePath, \"r\");\n try {\n await file.read(buffer, 0, length, fileStat.size - length);\n return buffer;\n } finally {\n await file.close();\n }\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n","/**\n * Source of the generated `.tempo/devserver-watchdog.cjs` — the win32\n * lifetime coupling between the Tempo app and the canvas devserver.\n *\n * Windows never kills children when their parent dies, so a hard death of\n * the Electron main (crash, taskkill, logoff, dev-mode restart) orphans the\n * devserver tree — which keeps holding Next.js's per-directory dev lock and\n * bricks every later canvas start until something reaps it (bundle\n * t17cesvbqfwmrdwbc2yjnwf5958bxva2). The watchdog closes that hole with the\n * one death signal a crash cannot skip: the supervisor holds the watchdog's\n * STDIN pipe open, the OS closes the pipe's far end during process teardown\n * unconditionally, and the watchdog answers EOF by force-killing the\n * devserver tree and exiting.\n *\n * The watchdog is deliberately a dumb pipe otherwise: the devserver's\n * stdout/stderr are inherited (they flow through the watchdog's own piped\n * stdio to the supervisor, so ready-URL detection and hint classification\n * are unchanged), and the child's exit code is forwarded verbatim —\n * Windows preserves full DWORD exit codes through ExitProcess, which the\n * supervisor's teardown-code classifier depends on.\n *\n * Kept as a string constant (same pattern as CANVAS_HOST_RECOVERY_MODULE_SOURCE)\n * because the supervisor is bundled into the Electron main bundle — a\n * standalone module file would not exist at a spawnable path in a packaged\n * app. The supervisor writes it under `<tempoRoot>/.tempo/` before each\n * spawn and launches it with ELECTRON_RUN_AS_NODE.\n */\n\nexport const DEVSERVER_WATCHDOG_FILE_NAME = \"devserver-watchdog.cjs\";\n\nexport const DEVSERVER_WATCHDOG_SOURCE = `// Generated by Tempo — do not edit.\n// Ties the canvas devserver's lifetime to the Tempo app: the app holds this\n// process's stdin open; when the app dies (cleanly or not) the OS closes the\n// pipe, and the watchdog kills the devserver tree so no orphan is left\n// holding Next.js's per-directory dev lock.\n\"use strict\";\nconst { spawn, spawnSync } = require(\"node:child_process\");\n\nconst command = process.argv[2];\nif (!command) {\n process.stderr.write(\"devserver-watchdog: missing command argument\\\\n\");\n process.exit(64);\n}\n\nconst childEnv = { ...process.env };\nconst childRunAsNode = childEnv.TEMPO_WATCHDOG_CHILD_ELECTRON_RUN_AS_NODE;\ndelete childEnv.TEMPO_WATCHDOG_CHILD_ELECTRON_RUN_AS_NODE;\nif (childRunAsNode === \"__TEMPO_UNSET__\") {\n delete childEnv.ELECTRON_RUN_AS_NODE;\n} else if (childRunAsNode !== undefined) {\n childEnv.ELECTRON_RUN_AS_NODE = childRunAsNode;\n}\n\nconst child = spawn(command, {\n shell: true,\n detached: process.platform !== \"win32\",\n windowsHide: true,\n stdio: [\"ignore\", \"inherit\", \"inherit\"],\n env: childEnv,\n});\n\n// Tell the supervisor which process is the real managed process-group root.\n// The watchdog itself is only a lifetime relay; persisting its pid would make\n// the next app instance unable to prove that the port listener belongs to the\n// tree Tempo launched. IPC is intentionally optional so the generated script\n// remains independently runnable in focused watchdog tests.\nif (typeof process.send === \"function\") {\n process.send({ type: \"tempo-watchdog-child\", pid: child.pid ?? null });\n}\n\nlet killed = false;\nfunction killTree() {\n if (killed || child.pid == null) return;\n killed = true;\n if (process.platform === \"win32\") {\n spawnSync(\"taskkill\", [\"/pid\", String(child.pid), \"/t\", \"/f\"], {\n stdio: \"ignore\",\n windowsHide: true,\n });\n } else {\n try { process.kill(-child.pid, \"SIGKILL\"); } catch {}\n try { process.kill(child.pid, \"SIGKILL\"); } catch {}\n }\n}\n\nchild.on(\"exit\", (code) => {\n // Forward the devserver's exit code verbatim (full DWORD range on\n // Windows) so the supervisor's crash classification sees the real code.\n process.exit(code === null ? 1 : code);\n});\n\n// The death signal: stdin EOF means the Tempo app is gone.\nprocess.stdin.resume();\nconst onParentGone = () => {\n killTree();\n process.exit(0);\n};\nprocess.stdin.on(\"end\", onParentGone);\nprocess.stdin.on(\"close\", onParentGone);\nprocess.stdin.on(\"error\", onParentGone);\n\n// Graceful teardown paths (the supervisor's stop / signal flows) must also\n// take the tree down, not just the watchdog.\nfor (const signal of [\"SIGTERM\", \"SIGINT\", \"SIGHUP\"]) {\n process.on(signal, onParentGone);\n}\n`;\n","/**\n * Hardened atomic text write — the single shared writer for both\n * `tempo-devserver` and `tempo-project` (TEM-432 consolidation).\n *\n * Writes `content` to `filePath` via a temp file + `rename` so a reader never\n * observes a half-written file. On Windows the `rename` over an existing\n * target fails with EPERM/EACCES/EBUSY whenever the destination (or the temp\n * source) is briefly held by another handle — the Vite watcher serving the\n * file, antivirus (Defender on a project under `Downloads/`), the search\n * indexer, or OneDrive. On POSIX a temp->target rename atomically replaces and\n * never hits this.\n *\n * Strategy: retry the rename a few times with a short backoff (covers the\n * common sub-second lock), and if it still fails on a transient lock, fall\n * back to a non-atomic in-place `writeFile` — an in-place overwrite is far\n * likelier to succeed than a delete-and-replace rename, and a brief partial\n * read beats never bringing the canvas host up at all. The temp file is always\n * cleaned up. Non-transient errors propagate raw.\n *\n * History: PR #1021 hardened only the `tempo-project` copy (`globals.css` and\n * the other scaffold files). The copy that actually failed in the field was\n * `tempo-devserver`'s generated-host writer, so Windows EPERM kept aborting\n * host startup. TEM-432 collapsed all generated-file writes onto this\n * implementation. Callers that need the `IoFailedError` (`io_failed`) contract\n * wrap the raw throw at their boundary (see `canvas-registry.ts`).\n */\nimport { mkdir, rename, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport function ensureTrailingNewline(value: string): string {\n return value.endsWith(\"\\n\") ? value : `${value}\\n`;\n}\n\n/**\n * Backoff schedule (ms) for retrying a temp-file rename. One entry per retry\n * after the initial attempt.\n */\nconst RENAME_RETRY_DELAYS_MS = [10, 25, 50, 100, 200] as const;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * True for the transient filesystem-lock errors Windows raises when a file is\n * momentarily held by another handle — the Vite watcher serving the file,\n * antivirus, or the search indexer scanning the freshly written temp file.\n * On POSIX a temp->target rename atomically replaces and never hits these.\n */\nexport function isTransientFsLockError(error: unknown): boolean {\n const code = (error as NodeJS.ErrnoException | null | undefined)?.code;\n return code === \"EPERM\" || code === \"EACCES\" || code === \"EBUSY\";\n}\n\n/**\n * Filesystem operations a Windows lock holder can block. A package manager\n * reifying a tree does all of these against files a running dev server may\n * have open — most notably `copyfile` of a loaded native addon\n * (`lightningcss.win32-x64-msvc.node`), which Windows refuses outright while\n * the image is mapped.\n */\nconst TRANSIENT_LOCK_SYSCALLS = \"copyfile|unlink|rename|rmdir|open|scandir\";\n\n/**\n * The {@link isTransientFsLockError} predicate for a subprocess's OUTPUT\n * rather than a caught `ErrnoException`.\n *\n * A package-manager install runs in a child process, so its transient Windows\n * lock failure only ever reaches us as text on stderr:\n *\n * ```\n * npm error code EBUSY\n * npm error syscall copyfile\n * npm error EBUSY: resource busy or locked, copyfile '…' -> '…'\n * ```\n *\n * Matching requires BOTH an errno and a filesystem syscall (in either order,\n * within a few lines) so an unrelated mention of `EACCES` in a package's own\n * build log doesn't buy three pointless reinstalls. `resource busy or locked`\n * is accepted on its own — it is unambiguous. See TEM-1349.\n */\nexport function isTransientFsLockOutput(output: string): boolean {\n if (/resource busy or locked/i.test(output)) return true;\n const errno = \"EBUSY|EPERM|EACCES\";\n const near = \"[\\\\s\\\\S]{0,400}?\";\n return (\n new RegExp(\n `\\\\b(?:${errno})\\\\b${near}\\\\b(?:${TRANSIENT_LOCK_SYSCALLS})\\\\b`,\n \"i\",\n ).test(output) ||\n new RegExp(\n `\\\\b(?:${TRANSIENT_LOCK_SYSCALLS})\\\\b${near}\\\\b(?:${errno})\\\\b`,\n \"i\",\n ).test(output)\n );\n}\n\n/**\n * `rename` over an existing target fails with EPERM/EACCES/EBUSY on Windows\n * whenever the destination (or the temp source) is briefly locked by a\n * watcher / antivirus / indexer. Those locks are transient, so retry a few\n * times with a short backoff before giving up. Non-transient errors propagate\n * immediately.\n */\nasync function renameWithRetry(from: string, to: string): Promise<void> {\n let lastError: unknown;\n for (\n let attempt = 0;\n attempt <= RENAME_RETRY_DELAYS_MS.length;\n attempt += 1\n ) {\n try {\n await rename(from, to);\n return;\n } catch (error) {\n if (!isTransientFsLockError(error)) throw error;\n lastError = error;\n const delay = RENAME_RETRY_DELAYS_MS[attempt];\n if (delay === undefined) break;\n await sleep(delay);\n }\n }\n throw lastError;\n}\n\n/**\n * Write `content` to `filePath`, preferring an atomic temp-file + rename so a\n * reader never observes a half-written file. Retries a transient-lock rename\n * and falls back to a non-atomic in-place write if the lock persists. See the\n * module header for the full rationale.\n */\nexport async function atomicWriteText(\n filePath: string,\n content: string,\n): Promise<void> {\n const directory = path.dirname(filePath);\n await mkdir(directory, { recursive: true });\n\n const body = ensureTrailingNewline(content);\n const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random()\n .toString(16)\n .slice(2)}`;\n await writeFile(tempPath, body, \"utf-8\");\n try {\n await renameWithRetry(tempPath, filePath);\n } catch (error) {\n await rm(tempPath, { force: true }).catch(() => {});\n if (isTransientFsLockError(error)) {\n await writeFile(filePath, body, \"utf-8\");\n return;\n }\n throw error;\n }\n}\n","import {\n type AbortableShellCommandResult,\n runAbortableShellCommand,\n} from \"./abortable-shell-command\";\nimport type { HttpUrlSource } from \"./types\";\n\nconst ANSI_ESCAPE_RE = /\\x1b\\[[0-9;]*m/g;\n\nexport interface ResolveHttpUrlSourceOptions {\n source: HttpUrlSource;\n label: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n port: number;\n timeoutMs: number;\n signal?: AbortSignal;\n substitutePort(commandOrUrl: string, port: number): string;\n}\n\n/**\n * Resolve a declared HTTP URL through one explicit, bounded interface.\n * String sources are pure substitution; command sources execute exactly once\n * and must return one complete HTTP(S) URL as their entire stdout value.\n * Supervisors decide WHEN to cross this seam (immediately for foreground\n * processes, after launcher handoff when stop owns an external runtime).\n */\nexport async function resolveHttpUrlSource(\n options: ResolveHttpUrlSourceOptions,\n): Promise<string> {\n if (typeof options.source === \"string\") {\n return options.substitutePort(options.source, options.port);\n }\n\n const controller = new AbortController();\n const onAbort = () => controller.abort();\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n const timeout = setTimeout(() => controller.abort(), options.timeoutMs);\n let result: AbortableShellCommandResult;\n try {\n result = await runAbortableShellCommand({\n command: options.substitutePort(options.source.command, options.port),\n cwd: options.cwd,\n env: options.env,\n signal: controller.signal,\n maxOutputBytes: 4_000,\n });\n } finally {\n clearTimeout(timeout);\n options.signal?.removeEventListener(\"abort\", onAbort);\n }\n\n if (result.aborted) {\n if (options.signal?.aborted) {\n throw new Error(`${options.label}.command was aborted`);\n }\n throw new Error(\n `${options.label}.command timed out after ${options.timeoutMs}ms`,\n );\n }\n if (result.spawnError) {\n throw new Error(\n `${options.label}.command could not start: ${result.spawnError.message}`,\n );\n }\n if (result.exitCode !== 0) {\n const detail = result.stderr.trim();\n throw new Error(\n `${options.label}.command exited with code ${result.exitCode ?? \"unknown\"}${detail ? `: ${detail}` : \"\"}`,\n );\n }\n\n const output = result.stdout.replace(ANSI_ESCAPE_RE, \"\").trim();\n let parsed: URL;\n try {\n parsed = new URL(output);\n } catch {\n throw invalidOutputError(options.label);\n }\n if (\n (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") ||\n output.includes(\"\\n\") ||\n output.includes(\"\\r\")\n ) {\n throw invalidOutputError(options.label);\n }\n return output;\n}\n\nfunction invalidOutputError(label: string): Error {\n return new Error(\n `${label}.command must print exactly one valid HTTP(S) URL to stdout`,\n );\n}\n","import { randomUUID } from \"node:crypto\";\nimport {\n canonicalizeDevServerKey,\n serializeDevServerKey,\n type DevServerKey,\n type DevServerKind,\n} from \"./dev-server-key\";\n\nexport { canonicalizeDevServerKey } from \"./dev-server-key\";\nexport type { DevServerKey, DevServerKind } from \"./dev-server-key\";\n\nexport type DevServerStopReason =\n | \"manual_stop\"\n | \"workspace_deleted\"\n | \"workspace_archived\"\n | \"idle_timeout\"\n | \"evicted_capacity\"\n | \"normal_quit\"\n | \"startup_reconciliation\"\n | \"dependency_change\"\n | \"start_failed\";\n\n/** Why a claim exists. Diagnostics vocabulary only — never drives admission. */\nexport type DevServerLeaseReason =\n | \"visible_canvas\"\n | \"visible_route_storyboard\"\n | \"background_canvas_tab\"\n | \"mcp_capture\"\n | \"mcp_share\"\n | \"mcp_debug\"\n | \"mcp_explicit_start\";\n\nexport type DevServerPoolEntryState =\n | \"stopped\"\n | \"starting\"\n | \"running\"\n | \"stopping\"\n | \"stop_failed\";\n\nexport interface DevServerRuntimeExit {\n error: string;\n /** False means teardown could not prove death; capacity stays occupied. */\n verifiedStopped: boolean;\n}\n\nexport interface DevServerRuntimeHandle {\n /** Resolves once ready. On rejection the pool verifies cleanup via stop(). */\n ready: Promise<void>;\n /** Resolves once when a ready runtime exits outside manager-owned stop. */\n exited: Promise<DevServerRuntimeExit>;\n /** Resolve only after process death and listener closure are verified. */\n stop(reason: DevServerStopReason): Promise<void>;\n}\n\nexport interface DevServerRuntimeLaunch {\n key: DevServerKey;\n runtimeId: string;\n signal: AbortSignal;\n}\n\n/** Canvas and app implementations occupy this real internal Seam. */\nexport interface DevServerRuntimeAdapter {\n launch(context: DevServerRuntimeLaunch): DevServerRuntimeHandle;\n}\n\nexport type DevServerRuntimeAdapterResolver = (\n key: DevServerKey,\n) => DevServerRuntimeAdapter;\n\nexport interface DevServerPoolEntrySnapshot {\n key: DevServerKey;\n runtimeId: string | null;\n state: DevServerPoolEntryState;\n /** Manual Stop fence: ownership remains, but no runtime may start until activation. */\n suspended: boolean;\n generation: number;\n hardLeaseCount: number;\n softLeaseCount: number;\n /** Distinct reasons across current claims, in claim-arrival order. */\n leaseReasons: DevServerLeaseReason[];\n /** Reason of the most recent runtime stop attempt for this resource. */\n lastStopReason: DevServerStopReason | null;\n lastUsedAt: number;\n error: string | null;\n}\n\nexport interface DevServerPoolSnapshot {\n revision: number;\n limit: number;\n activeCount: number;\n queuedCount: number;\n /** Canonical keys of capacity-waiting claims, in FIFO order. */\n queuedKeys: DevServerKey[];\n entries: DevServerPoolEntrySnapshot[];\n}\n\nexport interface DevServerLease {\n key: DevServerKey;\n leaseId: string;\n generation: number;\n release(): Promise<void>;\n}\n\n/**\n * Demand is installed synchronously. Callers that need readiness await\n * `ready`; callers that disappear can release the claim at any point without\n * racing a separate pending-acquisition registry.\n */\nexport interface DevServerClaim {\n key: DevServerKey;\n leaseId: string;\n ready: Promise<DevServerLease>;\n release(): Promise<void>;\n}\n\nexport interface DevServerClaimOptions {\n mode?: \"fail\" | \"wait\";\n hard?: boolean;\n signal?: AbortSignal;\n leaseId?: string;\n reason?: DevServerLeaseReason;\n}\n\nexport type DevServerCapacityErrorCode =\n | \"capacity_exceeded\"\n | \"capacity_wait_timeout\"\n | \"runtime_stranded\"\n | \"cancelled\";\n\nexport class DevServerCapacityError extends Error {\n readonly code: DevServerCapacityErrorCode;\n /** Pool numbers at rejection time, for typed MCP results and diagnostics. */\n readonly limit?: number;\n readonly activeCount?: number;\n readonly queuedCount?: number;\n\n constructor(\n code: DevServerCapacityError[\"code\"],\n message: string,\n details?: { limit: number; activeCount: number; queuedCount: number },\n ) {\n super(message);\n this.name = \"DevServerCapacityError\";\n this.code = code;\n this.limit = details?.limit;\n this.activeCount = details?.activeCount;\n this.queuedCount = details?.queuedCount;\n }\n}\n\n/** One agent-facing vocabulary for every pool admission failure. */\nexport function formatDevServerCapacityError(\n error: DevServerCapacityError,\n): string {\n const counts =\n error.limit == null\n ? \"\"\n : ` (limit ${error.limit}, ${error.activeCount ?? \"?\"} active, ${error.queuedCount ?? \"?\"} queued)`;\n switch (error.code) {\n case \"capacity_exceeded\":\n return `${error.code}: ${error.message}${counts}. Continue without this operation, stop an unused devserver, or retry once capacity frees. Do not retry in a loop.`;\n case \"capacity_wait_timeout\":\n return `${error.code}: ${error.message}${counts}. Continue without this operation or retry once later. Do not retry in a loop.`;\n case \"runtime_stranded\":\n return `${error.code}: ${error.message} Retry cleanup once; do not start a replacement or retry in a loop.`;\n case \"cancelled\":\n return `${error.code}: ${error.message}`;\n }\n}\n\ninterface ClaimRecord {\n leaseId: string;\n hard: boolean;\n mode: \"fail\" | \"wait\";\n /** Fill-only claims never evict and are rejected when no slot is free. */\n fillOnly: boolean;\n reason: DevServerLeaseReason | null;\n order: number;\n queued: boolean;\n settled: boolean;\n handle: DevServerClaim;\n resolve: (lease: DevServerLease) => void;\n reject: (error: unknown) => void;\n detachAbort: () => void;\n}\n\ninterface RuntimeIncarnation {\n runtimeId: string;\n generation: number;\n handle: DevServerRuntimeHandle | null;\n abortController: AbortController;\n state: DevServerPoolEntryState;\n stopPromise: Promise<void> | null;\n}\n\ninterface RuntimeReplacement {\n promise: Promise<number>;\n resolve: (generation: number) => void;\n reject: (error: unknown) => void;\n}\n\ninterface ResourceRecord {\n key: DevServerKey;\n id: string;\n claims: Map<string, ClaimRecord>;\n incarnation: RuntimeIncarnation | null;\n replacement: RuntimeReplacement | null;\n /**\n * Admission fence and its origin. A \"manual_stop\" fence is user intent and\n * survives even with zero claims — only resume() finishes it. A \"crash\"\n * fence parks the hard surface claims that outlived a verified exit, so it\n * lasts exactly as long as those claims do.\n */\n suspendedBy: \"manual_stop\" | \"crash\" | null;\n lastStopReason: DevServerStopReason | null;\n lastUsedAt: number;\n error: string | null;\n idleTimer: ReturnType<typeof setTimeout> | null;\n}\n\ninterface CapacityEviction {\n victimId: string;\n victimRuntimeId: string;\n beneficiaryId: string;\n}\n\nexport interface DevServerPoolOptions {\n resolveRuntimeAdapter: DevServerRuntimeAdapterResolver;\n limit?: number;\n idleTtlMs?: number;\n now?: () => number;\n makeLeaseId?: () => string;\n makeRuntimeId?: () => string;\n}\n\nconst DEFAULT_LIMIT = 5;\nconst DEFAULT_IDLE_TTL_MS = 60 * 60 * 1000;\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * The single lifecycle Module for Canvas and app devservers.\n *\n * A resource record owns both demand (claims) and at most one replaceable\n * process incarnation. Claims are installed synchronously; asynchronous\n * start/stop completions carry a runtime id and can mutate only the matching\n * incarnation. Capacity is therefore reserved before spawn, and a stale\n * completion cannot affect a newer runtime.\n */\nexport class DevServerPool {\n private readonly resolveRuntimeAdapter: DevServerRuntimeAdapterResolver;\n private readonly limit: number;\n private readonly idleTtlMs: number;\n private readonly now: () => number;\n private readonly makeLeaseId: () => string;\n private readonly makeRuntimeId: () => string;\n private readonly resources = new Map<string, ResourceRecord>();\n private readonly listeners = new Set<\n (snapshot: DevServerPoolSnapshot) => void\n >();\n private readonly stoppingKeys = new Set<string>();\n private readonly stoppingWorkspacePaths = new Set<string>();\n private revision = 0;\n private generation = 0;\n private claimOrder = 0;\n private eviction: CapacityEviction | null = null;\n private closed = false;\n private reconcileRequested = false;\n private reconciling = false;\n\n constructor(options: DevServerPoolOptions) {\n this.resolveRuntimeAdapter = options.resolveRuntimeAdapter;\n this.limit = options.limit ?? DEFAULT_LIMIT;\n this.idleTtlMs = options.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;\n this.now = options.now ?? Date.now;\n this.makeLeaseId = options.makeLeaseId ?? randomUUID;\n this.makeRuntimeId = options.makeRuntimeId ?? randomUUID;\n if (!Number.isInteger(this.limit) || this.limit <= 0) {\n throw new Error(\"DevServerPool limit must be a positive integer\");\n }\n }\n\n subscribe(listener: (snapshot: DevServerPoolSnapshot) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n getSnapshot(): DevServerPoolSnapshot {\n const queuedClaims = [...this.resources.values()]\n .flatMap((resource) =>\n [...resource.claims.values()]\n .filter((claim) => claim.queued)\n .map((claim) => ({ claim, key: resource.key })),\n )\n .sort((a, b) => a.claim.order - b.claim.order);\n const visible = [...this.resources.values()].filter(\n (resource) =>\n resource.incarnation !== null ||\n resource.suspendedBy !== null ||\n resource.error !== null,\n );\n const claimLeaseReasons = (claims: ClaimRecord[]) => [\n ...new Set(\n claims\n .sort((a, b) => a.order - b.order)\n .flatMap((claim) => (claim.reason ? [claim.reason] : [])),\n ),\n ];\n const resourceEntry = (resource: ResourceRecord) => {\n const incarnation = resource.incarnation;\n const claims = [...resource.claims.values()];\n return {\n key: resource.key,\n runtimeId: incarnation?.runtimeId ?? null,\n state: incarnation?.state ?? (\"stopped\" as const),\n suspended: resource.suspendedBy !== null,\n generation: incarnation?.generation ?? 0,\n hardLeaseCount: claims.filter((claim) => claim.hard).length,\n softLeaseCount: claims.filter((claim) => !claim.hard).length,\n leaseReasons: claimLeaseReasons(claims),\n lastStopReason: resource.lastStopReason,\n lastUsedAt: resource.lastUsedAt,\n error: resource.error,\n };\n };\n return {\n revision: this.revision,\n limit: this.limit,\n activeCount: this.capacityUsed(),\n queuedCount: queuedClaims.length,\n queuedKeys: queuedClaims.map(({ key }) => key),\n entries: visible.map(resourceEntry),\n };\n }\n\n /** Read one canonical resource without exposing snapshot traversal to callers. */\n getEntry(rawKey: DevServerKey): DevServerPoolEntrySnapshot | null {\n const key = canonicalizeDevServerKey(rawKey);\n const id = serializeDevServerKey(key);\n return (\n this.getSnapshot().entries.find(\n (entry) => serializeDevServerKey(entry.key) === id,\n ) ?? null\n );\n }\n\n /** Install demand synchronously and return its stable handle. */\n claim(rawKey: DevServerKey, options: DevServerClaimOptions): DevServerClaim {\n return this.createClaim(rawKey, options, false);\n }\n\n /** Explicit activation/start clears a suspended claim set. */\n resume(rawKey: DevServerKey): void {\n const key = canonicalizeDevServerKey(rawKey);\n const resource = this.resources.get(serializeDevServerKey(key));\n if (!resource) return;\n if (!resource.suspendedBy) return;\n resource.suspendedBy = null;\n resource.error = null;\n // A fence that held no other state is finished once cleared.\n this.deleteEmptyResource(resource);\n this.changed();\n this.requestReconcile();\n }\n\n /** Attach only when a runtime is already admitted; never starts or evicts. */\n claimExisting(\n rawKey: DevServerKey,\n options: Pick<DevServerClaimOptions, \"hard\" | \"reason\">,\n ): DevServerClaim | null {\n const key = canonicalizeDevServerKey(rawKey);\n if (this.admissionBlocked(key)) return null;\n const resource = this.resources.get(serializeDevServerKey(key));\n if (\n !resource?.incarnation ||\n (resource.incarnation.state !== \"starting\" &&\n resource.incarnation.state !== \"running\")\n ) {\n return null;\n }\n return this.createClaim(\n key,\n {\n hard: options.hard,\n reason: options.reason,\n mode: \"fail\",\n },\n false,\n );\n }\n\n async warm(\n rawKey: DevServerKey,\n reason?: DevServerLeaseReason,\n ): Promise<\"started\" | \"already_running\" | \"skipped_capacity\"> {\n if (this.closed) return \"skipped_capacity\";\n const key = canonicalizeDevServerKey(rawKey);\n const existing = this.resources.get(\n serializeDevServerKey(key),\n )?.incarnation;\n const alreadyRunning =\n existing?.state === \"starting\" || existing?.state === \"running\";\n const claim = this.createClaim(\n key,\n { mode: \"fail\", hard: false, reason },\n true,\n );\n try {\n const lease = await claim.ready;\n await lease.release();\n return alreadyRunning ? \"already_running\" : \"started\";\n } catch {\n return \"skipped_capacity\";\n }\n }\n\n async release(leaseId: string): Promise<void> {\n const owner = this.findClaimOwner(leaseId);\n if (!owner) return;\n const claim = owner.claims.get(leaseId);\n if (!claim) return;\n this.releaseClaimInstance(owner, claim);\n }\n\n private releaseClaimInstance(\n owner: ResourceRecord,\n claim: ClaimRecord,\n ): void {\n // A stale handle for an earlier claim with a reused stable owner id must\n // never release the current claim (the ABA form of the old pending race).\n if (owner.claims.get(claim.leaseId) !== claim) return;\n owner.claims.delete(claim.leaseId);\n claim.detachAbort();\n claim.queued = false;\n if (!claim.settled) {\n claim.settled = true;\n claim.reject(\n new DevServerCapacityError(\n \"cancelled\",\n \"Devserver acquisition was released before it completed\",\n ),\n );\n }\n owner.lastUsedAt = this.now();\n this.scheduleIdleStop(owner);\n this.deleteEmptyResource(owner);\n this.changed();\n this.requestReconcile();\n }\n\n async stop(rawKey: DevServerKey, reason: DevServerStopReason): Promise<void> {\n const key = canonicalizeDevServerKey(rawKey);\n const id = serializeDevServerKey(key);\n this.stoppingKeys.add(id);\n try {\n const resource = this.resources.get(id);\n if (resource) {\n // Explicit Stop acknowledges any prior terminal launch failure.\n resource.error = null;\n resource.suspendedBy = reason === \"manual_stop\" ? \"manual_stop\" : null;\n const cancellation = new DevServerCapacityError(\n \"cancelled\",\n \"Devserver replacement was cancelled by stop\",\n );\n this.rejectReplacement(resource, cancellation);\n // Manual Stop changes suspension, not ownership. Existing surface\n // claims remain bound until an explicit activation. Terminal stops\n // revoke demand.\n if (reason !== \"manual_stop\") {\n this.removeAllClaims(resource, cancellation);\n }\n }\n if (!resource) return;\n await this.stopIncarnation(resource, reason);\n this.deleteEmptyResource(resource);\n this.changed();\n } finally {\n this.stoppingKeys.delete(id);\n this.requestReconcile();\n }\n }\n\n async stopWorkspace(\n workspacePath: string,\n reason: DevServerStopReason,\n ): Promise<void> {\n const canonical = canonicalizeDevServerKey({\n workspacePath,\n kind: \"canvas\",\n }).workspacePath;\n this.stoppingWorkspacePaths.add(canonical);\n try {\n const matching = [...this.resources.values()].filter(\n (resource) => resource.key.workspacePath === canonical,\n );\n for (const resource of matching) {\n const cancellation = new DevServerCapacityError(\n \"cancelled\",\n \"Workspace was removed before devserver replacement completed\",\n );\n this.rejectReplacement(resource, cancellation);\n resource.suspendedBy = null;\n resource.error = null;\n this.removeAllClaims(resource, cancellation);\n }\n const outcomes = await Promise.allSettled(\n matching.map((resource) => this.stopIncarnation(resource, reason)),\n );\n for (const resource of matching) this.deleteEmptyResource(resource);\n const failures = outcomes.flatMap((outcome) =>\n outcome.status === \"rejected\" ? [outcome.reason] : [],\n );\n if (failures.length > 0) {\n throw new AggregateError(\n failures,\n `Failed to stop ${failures.length} workspace devserver runtime(s)`,\n );\n }\n } finally {\n this.stoppingWorkspacePaths.delete(canonical);\n this.requestReconcile();\n }\n }\n\n async shutdown(): Promise<void> {\n this.closed = true;\n const cancellation = new DevServerCapacityError(\n \"cancelled\",\n \"Devserver pool is shutting down\",\n );\n for (const resource of this.resources.values()) {\n this.rejectReplacement(resource, cancellation);\n resource.suspendedBy = null;\n resource.error = null;\n this.removeAllClaims(resource, cancellation);\n }\n await Promise.allSettled(\n [...this.resources.values()].map((resource) =>\n this.stopIncarnation(resource, \"normal_quit\"),\n ),\n );\n for (const resource of [...this.resources.values()]) {\n this.deleteEmptyResource(resource);\n }\n }\n\n /**\n * Replace one running incarnation without releasing its capacity or claims.\n * Concurrent replacement requests coalesce onto the same next generation.\n *\n * A request that lands while the incarnation is still `starting` waits for\n * that start to settle first. The Adapter's `ready` and the pool's own\n * `onStartSucceeded` are separate promise reactions, so a caller that\n * awaited the Adapter-side start can observe the pool one microtask behind\n * and would otherwise be refused for a host that is healthy (diagnostic\n * t177v8ds8x1yyzzqv0ffdmw2xx8dpfws).\n */\n async replace(rawKey: DevServerKey): Promise<number> {\n const key = canonicalizeDevServerKey(rawKey);\n const id = serializeDevServerKey(key);\n // A replacement already in flight is joined whatever the incarnation's\n // state: its stop has begun, so the state gate below would refuse the\n // second caller instead of coalescing it.\n const inFlight = this.resources.get(id)?.replacement;\n if (inFlight) return await inFlight.promise;\n // Only a pending start is worth yielding for: a running incarnation must\n // begin stopping synchronously, as callers observe.\n if (this.resources.get(id)?.incarnation?.state === \"starting\") {\n await this.awaitStartSettled(id);\n const joined = this.resources.get(id)?.replacement;\n if (joined) return await joined.promise;\n }\n const resource = this.resources.get(id);\n const incarnation = resource?.incarnation;\n // A stranded incarnation is the typed failure callers already know how\n // to relay (`runtime_stranded`), not a generic refusal.\n if (resource && incarnation?.state === \"stop_failed\") {\n throw this.stopFailedError(resource);\n }\n if (!resource || !incarnation || incarnation.state !== \"running\") {\n throw new DevServerCapacityError(\n \"cancelled\",\n \"Devserver is not running and cannot be replaced\",\n );\n }\n\n let resolveReplacement!: (generation: number) => void;\n let rejectReplacement!: (error: unknown) => void;\n const promise = new Promise<number>((resolve, reject) => {\n resolveReplacement = resolve;\n rejectReplacement = reject;\n });\n // The stop effect may reject the replacement before this async method\n // reaches its final await; mark the inner promise handled immediately.\n void promise.catch(() => undefined);\n resource.replacement = {\n promise,\n resolve: resolveReplacement,\n reject: rejectReplacement,\n };\n try {\n await this.stopIncarnation(resource, \"dependency_change\");\n } catch (error) {\n this.rejectReplacement(resource, error);\n }\n return await promise;\n }\n\n /**\n * Resolves once the resource has no `starting` incarnation. The pool\n * registers its own `ready` reactions in `startIncarnation`, before any\n * caller can reach this method, so a reaction registered here runs after\n * `onStartSucceeded`/`onStartFailed` has already moved the state on\n * (promise reactions run in registration order). Re-checking in a loop\n * covers a failed start that a queued claim immediately re-launched; a\n * handle that was already waited on ends the loop regardless.\n */\n private async awaitStartSettled(id: string): Promise<void> {\n let waited: DevServerRuntimeHandle | null = null;\n for (;;) {\n const incarnation = this.resources.get(id)?.incarnation;\n const handle = incarnation?.handle ?? null;\n if (\n !incarnation ||\n incarnation.state !== \"starting\" ||\n !handle ||\n handle === waited\n ) {\n return;\n }\n waited = handle;\n await handle.ready.then(\n () => undefined,\n () => undefined,\n );\n }\n }\n\n private createClaim(\n rawKey: DevServerKey,\n options: DevServerClaimOptions,\n fillOnly: boolean,\n ): DevServerClaim {\n const key = canonicalizeDevServerKey(rawKey);\n const id = serializeDevServerKey(key);\n const leaseId = options.leaseId ?? this.makeLeaseId();\n const existingOwner = this.findClaimOwner(leaseId);\n if (existingOwner) {\n if (existingOwner.id !== id) {\n return this.rejectedClaim(\n key,\n leaseId,\n new DevServerCapacityError(\n \"cancelled\",\n \"A devserver lease id cannot be shared across different resources\",\n ),\n );\n }\n return existingOwner.claims.get(leaseId)!.handle;\n }\n if (options.signal?.aborted || this.admissionBlocked(key)) {\n return this.rejectedClaim(\n key,\n leaseId,\n new DevServerCapacityError(\n \"cancelled\",\n this.closed\n ? \"Devserver pool is shutting down\"\n : \"Devserver is being stopped\",\n ),\n );\n }\n\n let resource = this.resources.get(id);\n if (!resource) {\n resource = this.createResource(key);\n }\n\n let resolveReady!: (lease: DevServerLease) => void;\n let rejectReady!: (error: unknown) => void;\n const ready = new Promise<DevServerLease>((resolve, reject) => {\n resolveReady = resolve;\n rejectReady = reject;\n });\n let released = false;\n let claim!: ClaimRecord;\n const release = async () => {\n if (released) return;\n released = true;\n this.releaseClaimInstance(resource!, claim);\n };\n const handle: DevServerClaim = { key, leaseId, ready, release };\n claim = {\n leaseId,\n hard: options.hard ?? true,\n mode: options.mode ?? \"fail\",\n fillOnly,\n reason: options.reason ?? null,\n order: ++this.claimOrder,\n // Queue membership is a reconciler decision after capacity is known.\n // A fresh wait-mode claim in an empty pool is not actually queued.\n queued: false,\n settled: false,\n handle,\n resolve: resolveReady,\n reject: rejectReady,\n detachAbort: () => {},\n };\n const onAbort = () => {\n const currentOwner = this.findClaimOwner(leaseId);\n const current = currentOwner?.claims.get(leaseId);\n // The signal bounds only capacity waiting. Once an incarnation has been\n // granted, explicit release/stop owns cancellation.\n if (!currentOwner || current !== claim || currentOwner.incarnation)\n return;\n currentOwner.claims.delete(leaseId);\n claim.detachAbort();\n claim.queued = false;\n if (!claim.settled) {\n claim.settled = true;\n claim.reject(\n new DevServerCapacityError(\n \"cancelled\",\n \"Queued devserver acquisition was cancelled\",\n ),\n );\n }\n this.deleteEmptyResource(currentOwner);\n this.changed();\n this.requestReconcile();\n };\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n claim.detachAbort = () =>\n options.signal?.removeEventListener(\"abort\", onAbort);\n resource.claims.set(leaseId, claim);\n this.clearIdleTimer(resource);\n resource.lastUsedAt = this.now();\n this.changed();\n this.requestReconcile();\n return handle;\n }\n\n private createResource(key: DevServerKey): ResourceRecord {\n const id = serializeDevServerKey(key);\n const resource: ResourceRecord = {\n key,\n id,\n claims: new Map(),\n incarnation: null,\n replacement: null,\n suspendedBy: null,\n lastStopReason: null,\n lastUsedAt: this.now(),\n error: null,\n idleTimer: null,\n };\n this.resources.set(id, resource);\n return resource;\n }\n\n private rejectedClaim(\n key: DevServerKey,\n leaseId: string,\n error: unknown,\n ): DevServerClaim {\n let rejectReady!: (reason: unknown) => void;\n const ready = new Promise<DevServerLease>((_resolve, reject) => {\n rejectReady = reject;\n });\n queueMicrotask(() => rejectReady(error));\n return { key, leaseId, ready, release: async () => {} };\n }\n\n private requestReconcile(): void {\n this.reconcileRequested = true;\n if (this.reconciling) return;\n this.reconciling = true;\n void this.runReconcileLoop();\n }\n\n private async runReconcileLoop(): Promise<void> {\n try {\n while (this.reconcileRequested) {\n this.reconcileRequested = false;\n this.reconcilePass();\n }\n } finally {\n this.reconciling = false;\n if (this.reconcileRequested) this.requestReconcile();\n }\n }\n\n private reconcilePass(): void {\n for (const resource of this.resources.values()) {\n const incarnation = resource.incarnation;\n if (!incarnation) continue;\n if (incarnation.state === \"starting\") {\n if (this.markClaimsAdmitted(resource)) this.changed();\n } else if (incarnation.state === \"running\") {\n const queueChanged = this.markClaimsAdmitted(resource);\n this.resolveClaims(resource, incarnation.generation);\n if (queueChanged) this.changed();\n } else {\n this.rejectNonWaitingClaims(resource);\n }\n }\n\n const awaiting = [...this.resources.values()]\n .filter((resource) => !resource.incarnation && resource.claims.size > 0)\n .sort((a, b) => this.firstClaimOrder(a) - this.firstClaimOrder(b));\n let blockedByEarlierWait = false;\n for (const resource of awaiting) {\n if (this.closed) {\n this.removeAllClaims(\n resource,\n new DevServerCapacityError(\n \"cancelled\",\n \"Devserver acquisition was cancelled\",\n ),\n );\n this.deleteEmptyResource(resource);\n continue;\n }\n if (resource.suspendedBy) {\n // Manual Stop is itself an admission fence. Keep stable surface\n // ownership while that fence is active, including during the stop's\n // final reconciliation pass.\n this.holdSuspendedClaims(resource);\n continue;\n }\n if (this.admissionBlocked(resource.key)) {\n this.removeAllClaims(\n resource,\n new DevServerCapacityError(\n \"cancelled\",\n \"Devserver acquisition was cancelled\",\n ),\n );\n this.deleteEmptyResource(resource);\n continue;\n }\n if (blockedByEarlierWait) {\n this.rejectNonWaitingClaims(resource);\n continue;\n }\n if (this.capacityUsed() < this.limit) {\n this.startIncarnation(resource);\n continue;\n }\n\n if (this.eviction?.beneficiaryId === resource.id) {\n // This resource already owns the slot being freed. Unrelated state\n // changes during verified teardown must not reject its fail-mode\n // claim or start a second eviction.\n blockedByEarlierWait = true;\n continue;\n }\n\n const canEvict = [...resource.claims.values()].some(\n (claim) => !claim.fillOnly,\n );\n if (canEvict && !this.hasEvictionInFlight()) {\n const victim = this.findEvictionCandidate(resource.id);\n if (victim) {\n this.expireSoftClaims(victim);\n this.eviction = {\n victimId: victim.id,\n victimRuntimeId: victim.incarnation!.runtimeId,\n beneficiaryId: resource.id,\n };\n void this.stopIncarnation(victim, \"evicted_capacity\").catch(() => {});\n blockedByEarlierWait = true;\n continue;\n }\n }\n\n this.rejectNonWaitingClaims(resource);\n if (\n [...resource.claims.values()].some((claim) => claim.mode === \"wait\")\n ) {\n blockedByEarlierWait = true;\n }\n }\n }\n\n private startIncarnation(resource: ResourceRecord): void {\n if (\n resource.incarnation ||\n (resource.claims.size === 0 && !resource.replacement)\n ) {\n return;\n }\n this.clearIdleTimer(resource);\n resource.lastUsedAt = this.now();\n resource.error = null;\n\n const runtimeId = this.makeRuntimeId();\n const abortController = new AbortController();\n const incarnation: RuntimeIncarnation = {\n runtimeId,\n generation: ++this.generation,\n handle: null,\n abortController,\n state: \"starting\",\n stopPromise: null,\n };\n // Reserve capacity before the Adapter can begin any launch work.\n resource.incarnation = incarnation;\n\n let handle: DevServerRuntimeHandle;\n try {\n const adapter = this.resolveRuntimeAdapter(resource.key);\n handle = adapter.launch({\n key: resource.key,\n runtimeId,\n signal: abortController.signal,\n });\n } catch (error) {\n resource.incarnation = null;\n resource.error = toErrorMessage(error);\n this.rejectReplacement(resource, error);\n this.removeAllClaims(resource, error);\n this.deleteEmptyResource(resource);\n this.changed();\n this.requestReconcile();\n return;\n }\n\n incarnation.handle = handle;\n this.markClaimsAdmitted(resource);\n this.changed();\n void handle.ready.then(\n () => this.onStartSucceeded(resource.id, runtimeId),\n (error) => this.onStartFailed(resource.id, runtimeId, error),\n );\n void handle.exited.then(\n (outcome) => this.onRuntimeExited(resource.id, runtimeId, outcome),\n (error) =>\n this.onRuntimeExited(resource.id, runtimeId, {\n error: toErrorMessage(error),\n verifiedStopped: false,\n }),\n );\n }\n\n private onStartSucceeded(resourceId: string, runtimeId: string): void {\n const resource = this.resources.get(resourceId);\n const incarnation = resource?.incarnation;\n if (!resource || !incarnation || incarnation.runtimeId !== runtimeId)\n return;\n if (incarnation.state === \"starting\") {\n incarnation.state = \"running\";\n resource.error = null;\n this.resolveClaims(resource, incarnation.generation);\n if (resource.replacement) {\n const replacement = resource.replacement;\n resource.replacement = null;\n replacement.resolve(incarnation.generation);\n }\n this.scheduleIdleStop(resource);\n this.changed();\n }\n this.requestReconcile();\n }\n\n private onStartFailed(\n resourceId: string,\n runtimeId: string,\n error: unknown,\n ): void {\n const resource = this.resources.get(resourceId);\n const incarnation = resource?.incarnation;\n if (!resource || !incarnation || incarnation.runtimeId !== runtimeId)\n return;\n // Once stop owns this incarnation, only its verified outcome may release\n // capacity. A launch rejection is expected after abort and carries no\n // independent proof that a partially spawned child is gone.\n if (\n incarnation.state === \"stopping\" ||\n incarnation.state === \"stop_failed\"\n ) {\n return;\n }\n resource.error = toErrorMessage(error);\n this.rejectReplacement(resource, error);\n this.removeAllClaims(resource, error);\n // A rejected launch can still own a child when cleanup failed. Keep the\n // incarnation and durable ownership until the adapter verifies teardown.\n void this.stopIncarnation(resource, \"start_failed\").catch(() => {});\n }\n\n private onRuntimeExited(\n resourceId: string,\n runtimeId: string,\n outcome: DevServerRuntimeExit,\n ): void {\n const resource = this.resources.get(resourceId);\n const incarnation = resource?.incarnation;\n if (!resource || !incarnation || incarnation.runtimeId !== runtimeId)\n return;\n if (incarnation.state === \"stopping\") return;\n if (incarnation.state === \"starting\") {\n if (!outcome.verifiedStopped) {\n incarnation.state = \"stop_failed\";\n resource.error = outcome.error;\n this.changed();\n return;\n }\n this.onStartFailed(resourceId, runtimeId, new Error(outcome.error));\n return;\n }\n if (!outcome.verifiedStopped) {\n incarnation.state = \"stop_failed\";\n resource.error = outcome.error;\n this.changed();\n return;\n }\n\n resource.incarnation = null;\n resource.error = outcome.error;\n this.clearIdleTimer(resource);\n this.rejectReplacement(resource, new Error(outcome.error));\n this.expireSoftClaims(resource, false);\n // Hard surface demand survives a verified crash but does not restart by\n // itself. The renderer's explicit Retry/activation is the sole recovery\n // owner and clears this fence through resume().\n resource.suspendedBy = [...resource.claims.values()].some(\n (claim) => claim.hard,\n )\n ? \"crash\"\n : null;\n this.deleteEmptyResource(resource);\n this.changed();\n this.requestReconcile();\n }\n\n private async stopIncarnation(\n resource: ResourceRecord,\n reason: DevServerStopReason,\n ): Promise<void> {\n const incarnation = resource.incarnation;\n if (!incarnation) return;\n if (incarnation.stopPromise) {\n await incarnation.stopPromise;\n return;\n }\n this.clearIdleTimer(resource);\n incarnation.state = \"stopping\";\n resource.lastStopReason = reason;\n incarnation.abortController.abort();\n const runtimeId = incarnation.runtimeId;\n this.changed();\n incarnation.stopPromise = (async () => {\n await incarnation.handle?.stop(reason);\n })()\n .then(() => {\n if (resource.incarnation?.runtimeId !== runtimeId) return;\n resource.incarnation = null;\n if (reason !== \"start_failed\") resource.error = null;\n this.completeEviction(resource.id, runtimeId);\n if (resource.replacement) {\n this.startIncarnation(resource);\n return;\n }\n this.deleteEmptyResource(resource);\n this.changed();\n this.requestReconcile();\n })\n .catch((error) => {\n if (resource.incarnation?.runtimeId === runtimeId) {\n incarnation.state = \"stop_failed\";\n resource.error = toErrorMessage(error);\n this.completeEviction(resource.id, runtimeId);\n // Every replacement caller — the initiator and those that joined\n // it — gets the pool's typed stranded error, not the raw Adapter\n // rejection.\n this.rejectReplacement(resource, this.stopFailedError(resource));\n this.changed();\n this.requestReconcile();\n }\n throw error;\n })\n .finally(() => {\n if (resource.incarnation?.runtimeId === runtimeId) {\n incarnation.stopPromise = null;\n }\n });\n await incarnation.stopPromise;\n }\n\n private resolveClaims(resource: ResourceRecord, generation: number): void {\n for (const claim of resource.claims.values()) {\n if (claim.settled) continue;\n claim.settled = true;\n claim.queued = false;\n claim.detachAbort();\n claim.resolve({\n key: resource.key,\n leaseId: claim.leaseId,\n generation,\n release: claim.handle.release,\n });\n }\n }\n\n private markClaimsAdmitted(resource: ResourceRecord): boolean {\n let changed = false;\n for (const claim of resource.claims.values()) {\n if (claim.queued) changed = true;\n claim.queued = false;\n claim.detachAbort();\n }\n return changed;\n }\n\n private completeEviction(victimId: string, runtimeId: string): void {\n if (\n this.eviction?.victimId === victimId &&\n this.eviction.victimRuntimeId === runtimeId\n ) {\n this.eviction = null;\n }\n }\n\n private rejectReplacement(resource: ResourceRecord, error: unknown): void {\n const replacement = resource.replacement;\n if (!replacement) return;\n resource.replacement = null;\n replacement.reject(error);\n }\n\n private rejectNonWaitingClaims(resource: ResourceRecord): void {\n const error =\n resource.incarnation?.state === \"stop_failed\"\n ? this.stopFailedError(resource)\n : this.capacityError();\n let changed = false;\n for (const claim of [...resource.claims.values()]) {\n if (claim.mode === \"wait\" && !claim.fillOnly) {\n if (!claim.queued) changed = true;\n claim.queued = true;\n continue;\n }\n this.rejectClaim(resource, claim, error);\n changed = true;\n }\n this.deleteEmptyResource(resource);\n if (changed) this.changed();\n }\n\n private holdSuspendedClaims(resource: ResourceRecord): void {\n let changed = false;\n for (const claim of [...resource.claims.values()]) {\n // Suspension preserves ownership already granted to a visible surface.\n // Only new fail-fast demand is rejected while the fence is active.\n if (claim.settled) continue;\n if (claim.mode === \"wait\" && !claim.fillOnly) continue;\n this.rejectClaim(\n resource,\n claim,\n new DevServerCapacityError(\n \"cancelled\",\n resource.error\n ? `Devserver is suspended after failure: ${resource.error}`\n : \"Devserver remains stopped until an explicit activation or start\",\n ),\n );\n changed = true;\n }\n if (changed) this.changed();\n }\n\n private removeAllClaims(resource: ResourceRecord, error: unknown): void {\n if (resource.claims.size === 0) return;\n for (const claim of [...resource.claims.values()]) {\n this.rejectClaim(resource, claim, error);\n }\n resource.lastUsedAt = this.now();\n this.changed();\n }\n\n private expireSoftClaims(resource: ResourceRecord, notify = true): boolean {\n let changed = false;\n for (const claim of [...resource.claims.values()]) {\n if (claim.hard) continue;\n this.rejectClaim(\n resource,\n claim,\n new DevServerCapacityError(\n \"cancelled\",\n \"Soft devserver claim was evicted for capacity\",\n ),\n );\n changed = true;\n }\n if (changed) {\n resource.lastUsedAt = this.now();\n if (notify) this.changed();\n }\n return changed;\n }\n\n private rejectClaim(\n resource: ResourceRecord,\n claim: ClaimRecord,\n error: unknown,\n ): void {\n if (resource.claims.get(claim.leaseId) !== claim) return;\n resource.claims.delete(claim.leaseId);\n claim.detachAbort();\n claim.queued = false;\n if (!claim.settled) {\n claim.settled = true;\n claim.reject(error);\n }\n }\n\n private findEvictionCandidate(excludedId: string): ResourceRecord | null {\n return (\n [...this.resources.values()]\n .filter(\n (resource) =>\n resource.id !== excludedId &&\n resource.incarnation?.state === \"running\" &&\n ![...resource.claims.values()].some((claim) => claim.hard),\n )\n .sort((a, b) => a.lastUsedAt - b.lastUsedAt)[0] ?? null\n );\n }\n\n private hasEvictionInFlight(): boolean {\n return this.eviction !== null;\n }\n\n private scheduleIdleStop(resource: ResourceRecord): void {\n if (!resource.incarnation) return;\n if ([...resource.claims.values()].some((claim) => claim.hard)) return;\n this.clearIdleTimer(resource);\n resource.idleTimer = setTimeout(() => {\n resource.idleTimer = null;\n this.expireSoftClaims(resource);\n void this.stopIncarnation(resource, \"idle_timeout\").catch(() => {});\n }, this.idleTtlMs);\n }\n\n private clearIdleTimer(resource: ResourceRecord): void {\n if (!resource.idleTimer) return;\n clearTimeout(resource.idleTimer);\n resource.idleTimer = null;\n }\n\n private deleteEmptyResource(resource: ResourceRecord): void {\n if (\n resource.incarnation ||\n resource.claims.size > 0 ||\n resource.replacement\n ) {\n return;\n }\n // A crash-parking fence protected surface claims that are now gone.\n if (resource.suspendedBy === \"crash\") resource.suspendedBy = null;\n // The manual-stop fence outlives its claims: dropping the record here\n // would let the next speculative warm restart a runtime the user\n // explicitly stopped. Only resume() finishes the fence.\n if (resource.suspendedBy === \"manual_stop\") return;\n // Keep only the terminal outcome, not an incarnation or capacity claim.\n // A fresh launch or explicit stop clears it; workspace teardown deletes it.\n if (resource.lastStopReason === \"start_failed\" && resource.error) return;\n this.clearIdleTimer(resource);\n if (this.resources.get(resource.id) === resource) {\n this.resources.delete(resource.id);\n }\n }\n\n private findClaimOwner(leaseId: string): ResourceRecord | null {\n for (const resource of this.resources.values()) {\n if (resource.claims.has(leaseId)) return resource;\n }\n return null;\n }\n\n private firstClaimOrder(resource: ResourceRecord): number {\n let first = Number.POSITIVE_INFINITY;\n for (const claim of resource.claims.values()) {\n first = Math.min(first, claim.order);\n }\n return first;\n }\n\n private admissionBlocked(key: DevServerKey): boolean {\n return (\n this.closed ||\n this.stoppingKeys.has(serializeDevServerKey(key)) ||\n this.stoppingWorkspacePaths.has(key.workspacePath)\n );\n }\n\n private capacityUsed(): number {\n return [...this.resources.values()].filter(\n (resource) => resource.incarnation !== null,\n ).length;\n }\n\n private capacityError(): DevServerCapacityError {\n return new DevServerCapacityError(\n \"capacity_exceeded\",\n `All ${this.limit} managed devserver slots are pinned`,\n {\n limit: this.limit,\n activeCount: this.capacityUsed(),\n queuedCount: this.getSnapshot().queuedCount,\n },\n );\n }\n\n private stopFailedError(resource: ResourceRecord): DevServerCapacityError {\n const runtimeId = resource.incarnation?.runtimeId;\n const error = resource.error;\n return new DevServerCapacityError(\n \"runtime_stranded\",\n `The current ${resource.key.kind} devserver for this workspace could not be proven stopped (${runtimeId ?? \"unknown runtime\"}). ${error ?? \"Retry cleanup before starting a replacement.\"}`,\n {\n limit: this.limit,\n activeCount: this.capacityUsed(),\n queuedCount: this.getSnapshot().queuedCount,\n },\n );\n }\n\n private changed(): void {\n this.revision += 1;\n const snapshot = this.getSnapshot();\n for (const listener of this.listeners) {\n try {\n listener(snapshot);\n } catch {\n // Status/diagnostic consumers observe lifecycle state; they do not\n // own it and cannot be allowed to abort reconciliation.\n }\n }\n }\n}\n","import { realpathSync } from \"node:fs\";\nimport path from \"node:path\";\n\nexport type DevServerKind = \"canvas\" | \"app\";\n\nexport interface DevServerKey {\n workspacePath: string;\n kind: DevServerKind;\n}\n\n/** Stable map/disk identity for an already-canonical devserver key. */\nexport function serializeDevServerKey(key: DevServerKey): string {\n return `${key.kind}\\0${key.workspacePath}`;\n}\n\n/** Filesystem identity shared by the pool and every transport adapter. */\nexport function canonicalizeDevServerKey(key: DevServerKey): DevServerKey {\n let workspacePath: string;\n try {\n workspacePath = realpathSync.native(key.workspacePath);\n } catch {\n workspacePath = path.resolve(key.workspacePath);\n }\n if (process.platform === \"win32\") workspacePath = workspacePath.toLowerCase();\n return { workspacePath, kind: key.kind };\n}\n","import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { atomicWriteText } from \"./atomic-write\";\nimport { readPidStartIdentity } from \"./app-devserver\";\nimport {\n canonicalizeDevServerKey,\n serializeDevServerKey,\n type DevServerKey,\n} from \"./dev-server-key\";\n\nexport interface DevServerOwnershipRecord {\n key: DevServerKey;\n /** Pool incarnation identity; stale cleanup may remove only this runtime. */\n runtimeId: string;\n originalWorkspacePath: string;\n managerPid: number;\n managerStartedAt: string | null;\n recordedAt: number;\n}\n\ninterface RegistryFile {\n version: 2;\n records: Record<string, DevServerOwnershipRecord>;\n}\n\nexport interface DevServerOwnershipRegistryOptions {\n filePath: string;\n managerPid?: number;\n now?: () => number;\n isManagerAlive?: (record: DevServerOwnershipRecord) => Promise<boolean>;\n /** Milliseconds since OS boot; injectable for tests. */\n uptimeMs?: () => number;\n /** Injectable durable writer for failure-path tests. */\n writeText?: typeof atomicWriteText;\n}\n\nexport class DevServerOwnershipRegistryUnavailableError extends Error {\n readonly code = \"ownership_registry_unavailable\";\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"DevServerOwnershipRegistryUnavailableError\";\n }\n}\n\n/**\n * The reconciler's proof obligation: \"resolved\" means it positively verified\n * no recorded process survives (or safely reaped it) — only then is the\n * record forgotten. Everything else (\"unresolved\", or a throw) retains the\n * record so the possible survivor keeps occupying pool capacity.\n */\nexport type DevServerReconcileVerdict = \"resolved\" | \"unresolved\";\n\n/**\n * Guards the pre-boot staleness proof against wall-clock adjustments: a\n * record only counts as pre-boot when it predates the boot instant by more\n * than this slack.\n */\nconst PRE_BOOT_CLOCK_SLACK_MS = 5 * 60 * 1000;\n\nexport interface DevServerManagerIdentity {\n ownerId: string;\n managerPid: number;\n managerStartedAt: string | null;\n}\n\nasync function defaultIsManagerAlive(\n record: DevServerOwnershipRecord,\n): Promise<boolean> {\n try {\n process.kill(record.managerPid, 0);\n } catch {\n return false;\n }\n if (!record.managerStartedAt) return true;\n return (\n (await readPidStartIdentity(record.managerPid)) === record.managerStartedAt\n );\n}\n\n/**\n * Durable index of every managed local devserver owned by this Electron app\n * channel. It stores no kill authority itself; startup reconciliation delegates\n * to each engine's existing PID/birth/port-proven reaper.\n */\nexport class DevServerOwnershipRegistry {\n private readonly filePath: string;\n private readonly managerPid: number;\n private readonly ownerId: string;\n private readonly now: () => number;\n private readonly isManagerAlive: (\n record: DevServerOwnershipRecord,\n ) => Promise<boolean>;\n private readonly uptimeMs: () => number;\n private readonly writeText: typeof atomicWriteText;\n private readonly managerStartedAt: Promise<string | null>;\n private mutationTail = Promise.resolve();\n\n constructor(options: DevServerOwnershipRegistryOptions) {\n this.filePath = path.resolve(options.filePath);\n this.managerPid = options.managerPid ?? process.pid;\n this.ownerId = createHash(\"sha256\")\n .update(this.filePath)\n .digest(\"hex\")\n .slice(0, 16);\n this.now = options.now ?? Date.now;\n this.isManagerAlive = options.isManagerAlive ?? defaultIsManagerAlive;\n this.uptimeMs = options.uptimeMs ?? (() => os.uptime() * 1000);\n this.writeText = options.writeText ?? atomicWriteText;\n this.managerStartedAt = readPidStartIdentity(this.managerPid);\n }\n\n async getManagerIdentity(): Promise<DevServerManagerIdentity> {\n return {\n ownerId: this.ownerId,\n managerPid: this.managerPid,\n managerStartedAt: await this.managerStartedAt,\n };\n }\n\n async record(rawKey: DevServerKey, runtimeId: string): Promise<void> {\n if (!runtimeId) throw new Error(\"Devserver runtime id must not be empty\");\n const key = canonicalizeDevServerKey(rawKey);\n await this.mutate(async (file) => {\n file.records[serializeDevServerKey(key)] = {\n key,\n runtimeId,\n originalWorkspacePath: rawKey.workspacePath,\n managerPid: this.managerPid,\n managerStartedAt: await this.managerStartedAt,\n recordedAt: this.now(),\n };\n });\n }\n\n async remove(rawKey: DevServerKey, runtimeId: string): Promise<void> {\n const key = canonicalizeDevServerKey(rawKey);\n await this.mutate((file) => {\n const id = serializeDevServerKey(key);\n if (file.records[id]?.runtimeId === runtimeId) {\n delete file.records[id];\n }\n });\n }\n\n async reconcileStale(\n reconcile: (\n record: DevServerOwnershipRecord,\n ) => Promise<DevServerReconcileVerdict>,\n ): Promise<DevServerOwnershipRecord[]> {\n const snapshot = await this.read();\n const unresolved: DevServerOwnershipRecord[] = [];\n const bootAt = this.now() - this.uptimeMs();\n for (const record of Object.values(snapshot.records)) {\n if (await this.isManagerAlive(record)) continue;\n // No process survives a reboot, so a record from before the current\n // boot is proven stale without engine reconciliation. This is also the\n // escape valve for an unresolvable record (e.g. a deleted workspace\n // whose identity markers are gone).\n if (record.recordedAt < bootAt - PRE_BOOT_CLOCK_SLACK_MS) {\n await this.remove(record.key, record.runtimeId);\n continue;\n }\n try {\n if ((await reconcile(record)) === \"resolved\") {\n await this.remove(record.key, record.runtimeId);\n continue;\n }\n } catch {\n // A throw carries the same meaning as \"unresolved\" below.\n }\n // Retain confirmed cleanup failures for a later best-effort retry. This\n // persisted bookkeeping is deliberately separate from pool admission.\n unresolved.push(record);\n }\n return unresolved;\n }\n\n async snapshot(): Promise<DevServerOwnershipRecord[]> {\n return Object.values((await this.read()).records);\n }\n\n private async mutate(\n mutation: (file: RegistryFile) => void | Promise<void>,\n ): Promise<void> {\n const operation = this.mutationTail.then(async () => {\n const file = await this.read();\n await mutation(file);\n try {\n await this.writeText(\n this.filePath,\n `${JSON.stringify(file, null, 2)}\\n`,\n );\n } catch (error) {\n throw new DevServerOwnershipRegistryUnavailableError(\n `Could not durably update the devserver ownership registry at ${this.filePath}`,\n { cause: error },\n );\n }\n const persisted = await this.read();\n if (JSON.stringify(persisted) !== JSON.stringify(file)) {\n throw new DevServerOwnershipRegistryUnavailableError(\n `The devserver ownership registry update could not be verified at ${this.filePath}`,\n );\n }\n });\n this.mutationTail = operation.catch(() => {});\n await operation;\n }\n\n private async read(): Promise<RegistryFile> {\n let source: string;\n try {\n source = await readFile(this.filePath, \"utf-8\");\n } catch (error) {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n ) {\n return { version: 2, records: {} };\n }\n throw new DevServerOwnershipRegistryUnavailableError(\n `Could not read the devserver ownership registry at ${this.filePath}`,\n { cause: error },\n );\n }\n\n try {\n const parsed = JSON.parse(source) as\n | {\n version?: number;\n records?: Record<string, unknown>;\n }\n | undefined;\n if (\n (parsed?.version !== 1 && parsed?.version !== 2) ||\n typeof parsed.records !== \"object\" ||\n parsed.records === null ||\n Array.isArray(parsed.records)\n ) {\n throw new Error(\"Unsupported registry schema\");\n }\n const normalized: Record<string, DevServerOwnershipRecord> = {};\n for (const [id, value] of Object.entries(parsed.records)) {\n const record = value as Partial<DevServerOwnershipRecord> | null;\n if (\n record === null ||\n typeof record !== \"object\" ||\n (record.key?.kind !== \"canvas\" && record.key?.kind !== \"app\") ||\n typeof record.key.workspacePath !== \"string\" ||\n id !== serializeDevServerKey(record.key as DevServerKey) ||\n (parsed.version === 2 &&\n (typeof record.runtimeId !== \"string\" || !record.runtimeId)) ||\n typeof record.originalWorkspacePath !== \"string\" ||\n !Number.isInteger(record.managerPid) ||\n (record.managerStartedAt !== null &&\n typeof record.managerStartedAt !== \"string\") ||\n typeof record.recordedAt !== \"number\" ||\n !Number.isFinite(record.recordedAt)\n ) {\n throw new Error(`Invalid ownership record ${JSON.stringify(id)}`);\n }\n normalized[id] = {\n ...(record as DevServerOwnershipRecord),\n // V1 predates incarnation-safe removal. This deterministic identity\n // lets reconciliation retain/remove the legacy record safely, and\n // the next registry mutation persists it in the V2 shape.\n runtimeId:\n parsed.version === 2\n ? record.runtimeId!\n : `legacy:${record.managerPid}:${record.recordedAt}`,\n };\n }\n return { version: 2, records: normalized };\n } catch (error) {\n throw new DevServerOwnershipRegistryUnavailableError(\n `The devserver ownership registry is corrupt at ${this.filePath}. Move or repair that file before starting another managed devserver.`,\n { cause: error },\n );\n }\n }\n}\n","/** Only missing local tooling permits replacing a custom install for this run. */\nexport function missingInstallEnvironment(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return (\n /(?:^|[\\s/\"'])(?:docker|pnpm|yarn|bun)(?:[\"'])?: (?:command )?not found\\b/im.test(\n message,\n ) ||\n /['\"](?:docker|pnpm|yarn|bun)['\"] is not recognized as an internal or external command/i.test(\n message,\n ) ||\n /cannot connect to the docker daemon/i.test(message) ||\n /is the docker daemon running\\?/i.test(message)\n );\n}\n","import { missingInstallEnvironment } from \"./install-environment-fallback\";\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport {\n access,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { request as httpRequest } from \"node:http\";\nimport { createServer, connect as netConnect } from \"node:net\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { abortableWait } from \"@modules/abortable-wait\";\nimport type {\n FileEvent,\n WatchOptions,\n WatchSubscription,\n} from \"@modules/file-watcher\";\nimport { watch } from \"@modules/file-watcher\";\nimport { readPidStartIdentity, substituteAppPort } from \"./app-devserver\";\nimport { runAbortableShellCommand } from \"./abortable-shell-command\";\nimport {\n describeInstallConnectivity,\n INSTALL_IDLE_TIMEOUT_MS,\n} from \"./install-feedback\";\nimport { resolveHttpUrlSource } from \"./http-url-source\";\nimport { isTransientFsLockOutput } from \"./atomic-write\";\nimport {\n applyBundledNodeEnv,\n getBundledNodeBinDir,\n isBundledNodeActivated,\n prependDirToPathEnv,\n probeNodeVersion,\n removeDirFromPathEnv,\n setBundledNodeActivated,\n shouldUseBundledNode,\n} from \"./bundled-node\";\nimport { tempoSdkVersionSatisfiesCanvasHost } from \"./canvas-host-capability\";\nimport { CANVAS_HOST_RECOVERY_METRO_MODULE_SOURCE } from \"./canvas-host-recovery-metro-source\";\nimport { CANVAS_HOST_RECOVERY_MODULE_SOURCE } from \"./canvas-host-recovery-source\";\nimport {\n atomicWriteText,\n CanvasRegistry,\n REQUESTED_CANVASES_FILE_NAME,\n type ReactCanvasHostEntry,\n readTextFileOptional,\n scanCanvasFiles,\n toPosixPath,\n} from \"./canvas-registry\";\nimport { devLog, devTimer } from \"./debug-log\";\nimport { parseDevServerLockConflict } from \"./dev-lock-conflict\";\nimport {\n DEVSERVER_WATCHDOG_FILE_NAME,\n DEVSERVER_WATCHDOG_SOURCE,\n} from \"./devserver-watchdog-source\";\nimport {\n ConfigMissingError,\n CanvasHostIdentityError,\n DaemonizedStartRequiresStopError,\n DisposedError,\n InstallDependencyConflictError,\n InstallFailedError,\n InstallNetworkError,\n InstallTimeoutError,\n InstallFileLockedError,\n InstallInterruptedError,\n type InstallInterruptedEvidence,\n InstallPermissionDeniedError,\n InstallShellDialectMismatchError,\n MissingRootDepsError,\n NodeVersionUnsupportedError,\n ProcessCrashedError,\n StartFailedError,\n StartShellDialectMismatchError,\n ToolMissingError,\n ValidationError,\n} from \"./errors\";\nimport {\n buildHostUnresponsiveHint,\n classifyDevServerOutput,\n} from \"./hint-classifier\";\nimport { ProcessHintSlots } from \"./process-hint-slots\";\nimport {\n detectOwningPackageManager,\n type DetectedPackageManager,\n} from \"./package-manager\";\nimport {\n describePowerShellRejection,\n extractLeadingEnvAssignments,\n findPowerShellSyntax,\n isShellParseAbortOutput,\n} from \"./shell-dialect\";\nimport { findPortListenerPid } from \"./port-listener\";\nimport {\n buildUnsatisfiedNodeHint,\n discoverVersionManagerNodes,\n readProjectNodeRequirement,\n selectNodeOverride,\n versionSatisfiesRequirement,\n} from \"./project-node-version\";\nimport {\n resolveTempoSdkUpgradePlan,\n type TempoSdkUpgradePlan,\n} from \"./sdk-upgrade-plan\";\nimport { prepareTempoSdkDevSource } from \"./tempo-sdk-dev-source\";\nimport type {\n DevServerHint,\n DevServerHintClass,\n DevServerHintListener,\n DevServerInitResult,\n PersistedDevserverReconciliationVerdict,\n DevServerReadyResult,\n DevServerReconcileResult,\n HostExitEntry,\n HostStartAttemptEntry,\n ReconcileActivityEntry,\n RepairReport,\n TempoDevServerConfig,\n TempoDevServerState,\n TempoHostDevServerHandle,\n TempoHostExitListener,\n TempoHostExitReason,\n TempoHostLivenessSource,\n TempoProcessStatus,\n} from \"./types\";\nimport type { HttpUrlSource } from \"./types\";\n\nconst TEMPO_NEXT_STABLE_DIST_DIR = \".next/tempo\";\nconst TEMPO_NEXT_CACHE_MARKER = \".tempo-cache-v1\";\nconst TEMPO_NEXT_CLAIMS_DIR = \"next-dist-claims\";\nconst TEMPO_NEXT_POST_READY_CONFLICT_MAX_AGE_MS = 60_000;\nconst TEMPO_NEXT_UNCLAIMED_CACHE_LIMIT = 3;\nconst TEMPO_NEXT_LEGACY_CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;\n\nconst {\n error: launchAbortError,\n throwIfAborted: throwIfLaunchAborted,\n sleep: waitForLaunchDelay,\n race: raceLaunchAbort,\n} = abortableWait(\"Canvas devserver launch was cancelled\");\n\n/** Wait out a prior install's settling; its failure belongs to that install. */\nfunction waitForInstallBarrier(\n work: Promise<unknown>,\n signal?: AbortSignal,\n): Promise<void> {\n return raceLaunchAbort(\n work.then(\n () => undefined,\n () => undefined,\n ),\n signal,\n );\n}\n\nexport function createSuffixedTempoNextDistDir(): string {\n const nonce = `${Date.now().toString(36)}${Math.random()\n .toString(36)\n .slice(2, 6)}`;\n return `${TEMPO_NEXT_STABLE_DIST_DIR}-${nonce}`;\n}\n\nconst TEMPO_HOST_ROUTE = \"/tempo-host\";\nconst DEFAULT_CANVASES_DIR = \"designs\";\n/**\n * Name of the subdirectory under `<projectRoot>` that holds Tempo's\n * config + generated runtime artifacts. The canonical (and only supported)\n * physical layout puts `tempo.config.json` at `<projectRoot>/tempo/`.\n */\nconst TEMPO_DIR_NAME = \"tempo\";\nconst TEMPO_PROCESS_MARKER_FILE = \"devserver-process.json\";\n// Host exits are persisted here so they SURVIVE the renderer's `restartCanvas`\n// (which disposes the whole TempoDevServer instance + its in-memory exit log).\n// A fresh supervisor for the same project loads this on initialize, so a\n// bug-report bundle captured AFTER a crash→restart still shows the crashing\n// instance's exit code + output tail — the reason — not just the survivor.\nconst TEMPO_RECENT_EXITS_FILE = \"recent-exits.json\";\n/**\n * Generated runtime files Tempo used to emit under `<projectRoot>/tempo/` but\n * no longer manages. Their continued presence counts as drift, so they are\n * both DETECTED (`hasManagedFileDrift`) and REPAIRED (`reconcileGeneratedFiles`)\n * from this single source of truth — a divergence between the two lists would\n * flag drift that is never actually cleaned up.\n */\nconst STALE_MANAGED_CANVAS_FILES = [\n \"component-host.tsx\",\n \"component-registry.ts\",\n \"canvas-manifest.tsx\",\n] as const;\n/**\n * Generated module holding the CanvasHost Vite recovery adapter, imported by\n * the generated Vite `tempo-host.tsx`. Managed (written, drift-checked, and\n * deleted for non-Vite frameworks) by the same reconcile pass as the wrapper.\n */\nconst CANVAS_HOST_RECOVERY_FILE_NAME = \"canvas-host-recovery.ts\";\n/**\n * Global the generated host wrapper increments on every evaluation: 1 on a\n * cold document load, 2+ once the bundler's HMR has swapped the wrapper in\n * place. Read only by the Metro recovery adapter, which uses it to tell a\n * genuinely-broken canvas (cold — leave isolated) from one merely missing\n * from this client's lazily-built bundle (hot — reload once). See TEM-1410.\n * Mirrored by `WRAPPER_GENERATION_KEY` in the Metro adapter template and\n * pinned against it by canvas-host-recovery-metro.test.ts.\n */\nconst CANVAS_HOST_WRAPPER_GENERATION_KEY = \"__tempoCanvasHostWrapperGeneration\";\n\n/**\n * Which failed-module recovery adapter (if any) belongs in this framework's\n * generated host document. Both bundler hosts get one; they differ in the\n * hole they close, so the CONTENT is framework-specific while the emitted\n * file name is shared. Next.js compiles routes server-side and has neither\n * hole, so it gets none.\n */\nfunction canvasHostRecoverySourceFor(\n startFramework: DevServerStartFramework | undefined,\n): string | null {\n if (startFramework === \"vite\") return CANVAS_HOST_RECOVERY_MODULE_SOURCE;\n if (startFramework === \"expo\")\n return CANVAS_HOST_RECOVERY_METRO_MODULE_SOURCE;\n return null;\n}\n/**\n * Generated entry module that mounts every demand-registered canvas. Written\n * and drift-checked by the reconcile pass.\n */\nconst TEMPO_HOST_FILE_NAME = \"tempo-host.tsx\";\n/**\n * Every file under `<tempoRoot>/.tempo/` whose content the devserver owns —\n * the reconcile pass either writes it (host wrapper, recovery module), deletes\n * it (the stale list), or maintains it through the registry (the requested-\n * canvases manifest). Derived from the same constants the reconciler uses so\n * the watcher can never drift out of step with what actually gets repaired.\n *\n * `.tempo/` also holds generated artifacts the devserver does NOT manage (e.g.\n * `library-canvases/`, status/marker files); those are deliberately absent, so\n * writing them never wakes the repair path.\n */\nconst MANAGED_TEMPO_DIR_FILE_NAMES = [\n TEMPO_HOST_FILE_NAME,\n CANVAS_HOST_RECOVERY_FILE_NAME,\n REQUESTED_CANVASES_FILE_NAME,\n ...STALE_MANAGED_CANVAS_FILES,\n] as const;\n/**\n * The single watcher-backend selection policy for the whole fleet: every\n * Electron main-process file watcher delegates here via\n * `resolveWatcherBackend` (main/adapters/watcherBackend), so this is the one\n * place the policy lives.\n *\n * Precedence order:\n *\n * 1. `TEMPO_WATCHER_BACKEND=native|polling` — explicit override for\n * diagnosis, always wins.\n * 2. Windows defaults to POLLING: @parcel/watcher's Windows native backend\n * can dispatch a pending ReadDirectoryChangesW completion after its\n * subscription buffers have been destroyed, killing the whole host\n * process (ELECTRON-25). That mitigation stays the fleet default this\n * release.\n * 3. `TEMPO_WATCHER_ISOLATION=1` opts a Windows machine into \"native\" for\n * dogfooding process isolation — when the Electron host has installed its\n * watch provider, a native watch on win32 runs OUT OF PROCESS in the\n * supervised watcher utilityProcess rather than in this process. The\n * default flip is a deliberate later one-liner once rollout evidence\n * lands.\n * 4. Every other platform defaults to native.\n */\nexport function resolveManagedWatchBackend(\n platform: NodeJS.Platform = process.platform,\n override: string | undefined = process.env.TEMPO_WATCHER_BACKEND,\n isolationOverride: string | undefined = process.env.TEMPO_WATCHER_ISOLATION,\n): NonNullable<WatchOptions[\"backend\"]> {\n if (override === \"native\" || override === \"polling\") {\n return override;\n }\n if (platform === \"win32\") {\n return isolationOverride === \"1\" ? \"native\" : \"polling\";\n }\n return \"native\";\n}\n\nconst WATCH_BACKEND: WatchOptions[\"backend\"] = resolveManagedWatchBackend();\nconst MANAGED_WATCH_OPTIONS: WatchOptions = {\n ignore: [\"node_modules/**\", \".git/**\", \"dist/**\", \"build/**\", \".next/**\"],\n debounceMs: 50,\n coalesce: true,\n autoRecover: true,\n backend: WATCH_BACKEND,\n onError: (error) => {\n devLog(\"managed-file-watcher:error\", { message: error.message });\n },\n};\n\ninterface RuntimePaths {\n projectRoot: string;\n tempoRoot: string;\n pagesDir: string;\n}\n\nfunction resolveRuntimePaths(tempoConfigPath: string): RuntimePaths {\n const resolvedConfigPath = path.resolve(tempoConfigPath);\n const tempoRoot = path.dirname(resolvedConfigPath);\n const projectRoot =\n path.basename(tempoRoot) === TEMPO_DIR_NAME\n ? path.dirname(tempoRoot)\n : tempoRoot;\n let canvasesPath = DEFAULT_CANVASES_DIR;\n try {\n const raw = JSON.parse(fs.readFileSync(resolvedConfigPath, \"utf-8\")) as {\n paths?: { canvases?: unknown };\n };\n if (\n raw.paths &&\n typeof raw.paths.canvases === \"string\" &&\n raw.paths.canvases.trim().length > 0\n ) {\n canvasesPath = raw.paths.canvases.trim();\n }\n } catch {\n // TempoProject validates config before constructing the devserver. If this\n // fails here, fall back to the default so the eventual file access surfaces\n // the meaningful runtime error path.\n }\n return {\n projectRoot,\n tempoRoot,\n pagesDir: path.resolve(tempoRoot, canvasesPath),\n };\n}\n\n/**\n * Classify a watcher event path (given relative to the project root) as\n * touching a file the devserver owns — the gate in front of the debounced\n * repair pass.\n *\n * The managed set is the config file, the canvases directory (whose tree the\n * registry reconciles), and the ENUMERATED generated files under\n * `<tempoRoot>/.tempo/` — never the `.tempo/` prefix as a whole. That\n * directory is the project's generated-artifacts zone and also holds files the\n * reconciler does not manage (`library-canvases/`, `status.json`, the process\n * marker); writing those must not wake the repair path.\n *\n * Matching runs in both directions: an event on a descendant of a managed\n * directory matches (a canvas file created under the canvases dir), and so\n * does an event on an ancestor directory of a managed file (`tempo/.tempo`,\n * `tempo/`) — a directory-level event there can mean managed children appeared\n * or vanished, and the repair it schedules is a content-compared no-op when\n * they did not. That ancestor clause is also why `.tempo/library-canvases`\n * itself does not match: no managed file lives beneath it.\n */\nexport function isManagedDevServerPath(\n eventRelativePath: string,\n roots: {\n projectRoot: string;\n tempoRoot: string;\n tempoConfigPath: string;\n pagesDir: string;\n },\n): boolean {\n const relativePath = toPosixPath(eventRelativePath);\n const tempoDir = path.join(roots.tempoRoot, \".tempo\");\n const managedRelativePaths = [\n roots.tempoConfigPath,\n roots.pagesDir,\n ...MANAGED_TEMPO_DIR_FILE_NAMES.map((fileName) =>\n path.join(tempoDir, fileName),\n ),\n ]\n .map((managedPath) =>\n toPosixPath(path.relative(roots.projectRoot, managedPath)),\n )\n .filter((value) => value.length > 0 && !value.startsWith(\"../\"));\n return managedRelativePaths.some(\n (managedPath) =>\n relativePath === managedPath ||\n relativePath.startsWith(`${managedPath}/`) ||\n managedPath.startsWith(`${relativePath}/`),\n );\n}\n\n// 0xC000013A = STATUS_CONTROL_C_EXIT — the Windows exit status of a process\n// torn down via a console control event (Ctrl+C / console close / taskkill of\n// the console session). The canvas host exits with it when the app quits or\n// the supervisor's console goes away — a teardown, never a spontaneous server\n// crash. Classifying it as a crash surfaced every app-close as a phantom\n// \"Process exited (3221225786)\" error toolbar + an `outcome=errored`\n// CANVAS_LOAD_RESULT (TEM-873). Node reports the status as an unsigned DWORD;\n// accept the signed-int32 rendering too. POSIX exit codes are 8-bit, so this\n// can never match on macOS/Linux.\nconst WINDOWS_STATUS_CONTROL_C_EXIT = 0xc000013a; // 3221225786\nexport function isWindowsTeardownExitCode(code: number | null): boolean {\n return (\n code === WINDOWS_STATUS_CONTROL_C_EXIT ||\n code === (WINDOWS_STATUS_CONTROL_C_EXIT | 0)\n );\n}\n\ntype ExpectedTempoHostIdentity = {\n workspaceNonce: string;\n framework: \"vite\" | \"nextjs\";\n};\n\nclass TempoHostProcess implements TempoHostDevServerHandle {\n private readonly child: ChildProcess;\n\n private readonly commandCwd: string;\n\n private readonly commandEnv: NodeJS.ProcessEnv;\n\n private readonly stopCommand: string | null;\n\n private readonly onLauncherHandoff: (() => Promise<void>) | null;\n\n private terminationPromise: Promise<void> | null = null;\n\n private explicitTempoHostUrl: string | null;\n\n private readonly tempoHostUrlSource: HttpUrlSource | null;\n\n private readonly injectedPort: number;\n\n private readonly launchSignal?: AbortSignal;\n\n private readonly tempoHostUrlIsHardcoded: boolean;\n\n // The workspace root this host is expected to serve. Used to reject a\n // *different* workspace's tempo-host that collided onto the detected port\n // (see `fetchIsTempoHost` / `advertisedRootBelongsToWorkspace`).\n private readonly expectedProjectRoot: string | null;\n\n private readonly expectedIdentity: ExpectedTempoHostIdentity | null;\n\n private readonly identityFailureMessage: string | null;\n\n // A host-filesystem absence is only diagnostic context. It never blocks a\n // custom/containerized start; it enriches a later observed package failure.\n private readonly missingRootDepsProjectRoot: string | null;\n\n private detectedBaseUrl: string | null = null;\n\n private publicUrl: string | null = null;\n\n private stopped = false;\n private readonly hasParentDeathWatchdog: boolean;\n\n // True when the shell command exited 0 before HTTP readiness but a matching\n // scripts.stop exists. In this mode the child was only a launcher; HTTP is\n // the liveness authority until stop/restart runs the external teardown.\n private launcherFinished = false;\n\n private readinessConfirmed = false;\n\n private launcherStopRequired = false;\n\n private terminalExitNotified = false;\n\n // Set by stop()/forceKill() so the exit handler can distinguish an\n // intentional teardown from a crash.\n private intentionalStop = false;\n\n // Records whether the most recent exit was a \"clear crash\" (unexpected\n // non-zero / signalled exit, not an intentional stop). Read by\n // waitUntilReady — including its already-stopped fast path — so the\n // supervisor can auto-retry a crash exactly once even when the process died\n // before readiness was awaited.\n private lastExitCrashed = false;\n\n private readonly readySignal: Promise<void>;\n\n private resolveReady: (() => void) | null = null;\n\n private rejectReady: ((error: Error) => void) | null = null;\n\n private exitListeners: TempoHostExitListener[] = [];\n\n private outputListeners: Array<(data: string) => void> = [];\n\n // Collects recent process output so early-exit errors include diagnostic context\n private recentOutput: string[] = [];\n private static readonly MAX_RECENT_LINES = 30;\n\n // User-actionable hints about this process — classified from stdout/stderr\n // (see `hint-classifier.ts`) or observed by the supervisor from outside —\n // drive the renderer's toolbar messaging when the canvas is stuck or\n // errored. Two slots, because the two sources have different lifetimes.\n private readonly hints = new ProcessHintSlots();\n private hintListeners: DevServerHintListener[] = [];\n\n constructor(options: {\n cwd: string;\n command: string;\n explicitTempoHostUrl: string | null;\n tempoHostUrlSource: HttpUrlSource | null;\n injectedPort: number;\n launchSignal?: AbortSignal;\n tempoHostUrlIsHardcoded?: boolean;\n expectedProjectRoot: string | null;\n expectedIdentity?: ExpectedTempoHostIdentity | null;\n identityFailureMessage?: string | null;\n missingRootDepsProjectRoot?: string | null;\n stopCommand?: string | null;\n /** Persist teardown authority before probe-backed handoff can continue. */\n onLauncherHandoff?: () => Promise<void>;\n /** Per-launch additions layered over the managed env (e.g. the dev-lock\n * sidestep's TEMPO_NEXT_DIST_DIR). */\n extraEnv?: NodeJS.ProcessEnv;\n /** Path to the generated lifetime-coupling watchdog (win32; see\n * devserver-watchdog-source.ts), or null/absent for a direct spawn. */\n watchdogScriptPath?: string | null;\n /** Project Node override resolved by the supervisor (TEM-1536), so the\n * host process runs under the same Node the managed installs used. */\n projectNode?: {\n binDir: string;\n bundled: boolean;\n nodenvVersion?: string | null;\n } | null;\n }) {\n this.explicitTempoHostUrl = options.explicitTempoHostUrl;\n this.tempoHostUrlSource = options.tempoHostUrlSource;\n this.injectedPort = options.injectedPort;\n this.launchSignal = options.launchSignal;\n this.tempoHostUrlIsHardcoded = options.tempoHostUrlIsHardcoded ?? false;\n this.expectedProjectRoot = options.expectedProjectRoot;\n this.expectedIdentity = options.expectedIdentity ?? null;\n this.identityFailureMessage = options.identityFailureMessage ?? null;\n this.missingRootDepsProjectRoot =\n options.missingRootDepsProjectRoot ?? null;\n this.commandCwd = options.cwd;\n this.stopCommand = options.stopCommand ?? null;\n this.onLauncherHandoff = options.onLauncherHandoff ?? null;\n this.hasParentDeathWatchdog = options.watchdogScriptPath != null;\n // Lift leading `NAME=value` prefixes into the spawn env — sh applies\n // them itself, cmd.exe parses them as a program name and dies before\n // running anything (TEM-436). The lifted command is also what the Expo\n // detection below should see: `TEMPO=true npx expo start …` IS an Expo\n // start.\n const lifted = extractLeadingEnvAssignments(options.command);\n devLog(\"process:spawn\", {\n command: options.command,\n liftedEnvKeys: Object.keys(lifted.env),\n cwd: options.cwd,\n explicitTempoHostUrl: options.explicitTempoHostUrl,\n extraEnvKeys: options.extraEnv ? Object.keys(options.extraEnv) : [],\n watchdog: options.watchdogScriptPath != null,\n });\n // Expo's Metro disables fast-refresh under CI; keep HMR on for the canvas\n // reconcile loop (see buildManagedCommandEnv). Vite/Next ignore CI, so this\n // only affects the Expo start command. `isExpoStartCommand` is the single\n // source of truth for the shape, shared with the start-framework +\n // port-injection paths.\n const isExpoStart = isExpoStartCommand(lifted.command);\n this.commandEnv = {\n ...buildManagedCommandEnv({\n metroFastRefresh: isExpoStart,\n projectNode: options.projectNode ?? null,\n }),\n ...options.extraEnv,\n // Command-line assignments beat the managed env, matching sh.\n ...lifted.env,\n };\n this.child = spawnManagedLongRunningCommand({\n command: lifted.command,\n cwd: options.cwd,\n env: this.commandEnv,\n watchdogScriptPath: options.watchdogScriptPath ?? null,\n });\n devLog(\"process:spawned\", { pid: this.child.pid ?? null });\n\n this.readySignal = new Promise<void>((resolve, reject) => {\n this.resolveReady = resolve;\n this.rejectReady = reject;\n });\n // A child can exit between construction and the supervisor attaching its\n // waitUntilReady consumer. Observe the rejection immediately so that fast\n // crashes do not leak an unhandled rejection; awaiting the original\n // promise still receives the same error and preserves crash classification.\n void this.readySignal.catch(() => {});\n\n const onOutput = (chunk: Buffer | string) => {\n const text = String(chunk);\n this.recentOutput.push(text);\n if (this.recentOutput.length > TempoHostProcess.MAX_RECENT_LINES) {\n this.recentOutput.shift();\n }\n this.notifyOutputListeners(text);\n // Run the permanent classifier. If the line matches a known\n // user-actionable pattern, record it as the most recent hint and\n // notify subscribers (the Electron adapter forwards them to the\n // renderer as part of `tempobook:statusChanged`).\n const classifiedHint = classifyDevServerOutput(text);\n const hint =\n classifiedHint?.class === \"module-not-found\" &&\n this.missingRootDepsProjectRoot\n ? {\n ...classifiedHint,\n errorCode: \"root_deps_missing\" as const,\n hint: new MissingRootDepsError(this.missingRootDepsProjectRoot)\n .message,\n }\n : classifiedHint;\n if (hint) {\n this.recordHint(hint);\n }\n const matchedUrl = extractServerUrl(text);\n if (matchedUrl && !this.detectedBaseUrl) {\n this.detectedBaseUrl = matchedUrl;\n this.publicUrl = buildTempoHostUrl(matchedUrl);\n devLog(\"process:url_detected\", {\n pid: this.child.pid ?? null,\n baseUrl: matchedUrl,\n publicUrl: this.publicUrl,\n });\n }\n if ((this.publicUrl || this.detectedBaseUrl) && this.resolveReady) {\n this.resolveReady();\n this.resolveReady = null;\n this.rejectReady = null;\n }\n };\n\n this.child.stdout?.on(\"data\", onOutput);\n this.child.stderr?.on(\"data\", onOutput);\n\n this.child.on(\"error\", (error) => {\n devLog(\"process:spawn_error\", {\n pid: this.child.pid ?? null,\n message: error.message,\n recentOutputTail: this.recentOutput.slice(-10).join(\"\").slice(-2000),\n });\n if (this.rejectReady) {\n const output = this.recentOutput.join(\"\").trim();\n const outputSuffix = output ? `\\n\\nProcess output:\\n${output}` : \"\";\n this.rejectReady(\n new StartFailedError(\n `Failed to start Tempo host dev server: ${error.message}${outputSuffix}${this.hintSuffix()}`,\n ),\n );\n this.resolveReady = null;\n this.rejectReady = null;\n }\n });\n\n this.child.on(\"exit\", async (code, signal) => {\n this.stopped = true;\n const cleanLauncherExit =\n !this.intentionalStop &&\n !this.readinessConfirmed &&\n code === 0 &&\n signal == null;\n if (cleanLauncherExit && this.stopCommand) {\n await this.onLauncherHandoff?.();\n this.launcherFinished = true;\n this.lastExitCrashed = false;\n devLog(\"process:launcher_finished\", {\n pid: this.child.pid ?? null,\n explicitTempoHostUrl: this.explicitTempoHostUrl,\n });\n if (this.resolveReady) {\n this.resolveReady();\n this.resolveReady = null;\n this.rejectReady = null;\n }\n this.notifyExitListeners(code, signal, \"launcher-finished\", false);\n return;\n }\n // Before readiness a clean exit is a launcher shape (handed off above,\n // or rejected below as a daemonizing start without `scripts.stop`).\n // AFTER readiness there is no benign clean exit: the serving child is\n // gone whatever code it reported. The lifetime watchdog turns an\n // external SIGTERM into exit 0 (devserver-watchdog-source.ts) and Vite\n // itself exits 0 on SIGTERM, so keying on the code alone filed a killed\n // host as \"not a crash\" — the adapter kept it idle, the pool kept the\n // incarnation running, and every later start was a no-op (diagnostic\n // t177v8ds8x1yyzzqv0ffdmw2xx8dpfws).\n this.lastExitCrashed =\n !this.intentionalStop &&\n !isWindowsTeardownExitCode(code) &&\n (code !== 0 || signal != null || this.readinessConfirmed);\n if (cleanLauncherExit) {\n this.launcherStopRequired = true;\n }\n devLog(\"process:exit\", {\n pid: this.child.pid ?? null,\n code,\n signal,\n beforeReady: this.rejectReady != null,\n recentOutputTail: this.recentOutput.slice(-10).join(\"\").slice(-2000),\n });\n if (this.rejectReady) {\n if (cleanLauncherExit) {\n this.rejectReady(new DaemonizedStartRequiresStopError());\n this.resolveReady = null;\n this.rejectReady = null;\n this.notifyExitListeners(code, signal, \"process-exit\");\n return;\n }\n const detail = signal\n ? `signal ${signal}`\n : `code ${code ?? \"unknown\"}`;\n const output = this.recentOutput.join(\"\").trim();\n const outputSuffix = output ? `\\n\\nProcess output:\\n${output}` : \"\";\n this.rejectReady(\n new ProcessCrashedError(\n `Tempo host dev server exited before readiness (${detail})${outputSuffix}${this.hintSuffix()}`,\n ),\n );\n this.resolveReady = null;\n this.rejectReady = null;\n }\n this.notifyExitListeners(code ?? null, signal ?? null, \"process-exit\");\n });\n }\n\n onExit(listener: TempoHostExitListener): void {\n if (this.launcherFinished && !this.terminalExitNotified) {\n this.exitListeners.push(listener);\n listener(0, null, false, \"launcher-finished\", false);\n return;\n }\n if (this.stopped) {\n listener(\n null,\n null,\n this.lastExitCrashed,\n \"process-exit\",\n this.intentionalStop,\n );\n return;\n }\n\n this.exitListeners.push(listener);\n }\n\n onOutput(listener: (data: string) => void): void {\n this.outputListeners.push(listener);\n }\n\n onHint(listener: DevServerHintListener): void {\n this.hintListeners.push(listener);\n // Deliver the current hint immediately if one already exists — a late\n // subscriber (e.g. the renderer remounting after the user navigated\n // away) shouldn't miss the diagnostic the user needs.\n const current = this.hints.current();\n if (current) {\n try {\n listener(current);\n } catch {\n // best-effort\n }\n }\n }\n\n getLastHint(): DevServerHint | null {\n return this.hints.current();\n }\n\n /**\n * Record a hint classified from this process's stdout/stderr and notify\n * subscribers with whatever hint is now in effect (a supervisor hint, if one\n * is active, still takes precedence — see `ProcessHintSlots`).\n */\n recordHint(hint: DevServerHint): void {\n this.hints.recordOutput(hint);\n this.logHint(hint);\n this.notifyHintListeners(this.hints.current());\n }\n\n /**\n * Record a hint the supervisor observed about this process from outside its\n * output (a live child whose URL stopped answering — see\n * `runUrlHealthProbeTick`). It is shown in place of any output hint until\n * `clearSupervisorHint` withdraws it.\n */\n recordSupervisorHint(hint: DevServerHint): void {\n this.hints.recordSupervisor(hint);\n this.logHint(hint);\n this.notifyHintListeners(hint);\n }\n\n /**\n * Withdraw the supervisor hint of `hintClass` once the condition it\n * described has cleared (a URL probe succeeding again). Subscribers receive\n * the hint now in effect — the output hint that was showing before, or\n * `null` — so a consumer that mirrors \"the current hint\" (the Electron\n * adapter → renderer toolbar detail) stops reporting a recovered host as\n * stuck without losing a compile diagnostic that is still true.\n */\n clearSupervisorHint(hintClass: DevServerHintClass): void {\n if (!this.hints.clearSupervisor(hintClass)) return;\n devLog(\"process:hint_cleared\", {\n pid: this.child.pid ?? null,\n class: hintClass,\n });\n this.notifyHintListeners(this.hints.current());\n }\n\n private logHint(hint: DevServerHint): void {\n devLog(\"process:hint\", {\n pid: this.child.pid ?? null,\n class: hint.class,\n hint: hint.hint,\n line: hint.line,\n });\n }\n\n private notifyHintListeners(hint: DevServerHint | null): void {\n for (const listener of this.hintListeners) {\n try {\n listener(hint);\n } catch {\n // never let a hint subscriber crash output handling\n }\n }\n }\n\n getLivenessSource(): TempoHostLivenessSource {\n return this.launcherFinished ? \"probe\" : \"child\";\n }\n\n /**\n * The classifier's actionable fix, ready to append to a start-failure\n * message. The hint was already computed off the child's output, but it only\n * ever reached the renderer's status stream — the thrown error carried raw\n * stderr alone, so `start_canvas_devserver` handed the agent (and the user)\n * \"exited before readiness (code 1) / -H was unexpected at this time.\" with\n * no indication that the command never even parsed. See TEM-436.\n */\n private hintSuffix(): string {\n const current = this.hints.current();\n return current ? `\\n\\nHint: ${current.hint}` : \"\";\n }\n\n getPid(): number | null {\n return this.child.pid ?? null;\n }\n\n /** Tail of the child's stdout/stderr — the crash reason, for diagnostics. */\n getRecentOutputTail(): string {\n return this.recentOutput.join(\"\").slice(-4000);\n }\n\n /** Whether the host ever reported ready (URL detected) during its life. */\n hasBecomeReady(): boolean {\n return this.readinessConfirmed;\n }\n\n /**\n * Whether the child is alive right now. Combines the exit-event flag with a\n * signal-0 pid check so a lost exit event (missed SIGCHLD) can't leave a\n * dead child reading as alive forever. This handle is the AUTHORITY on\n * process liveness — supervisor decisions must consult it, never a stored\n * status mirror.\n */\n isAlive(): boolean {\n if (this.stopped) return false;\n const pid = this.child.pid;\n if (!pid || pid <= 1) return false;\n return isPidAlive(pid);\n }\n\n hasLivenessAuthority(): boolean {\n return this.launcherFinished || this.isAlive();\n }\n\n reportProbeBackedDeath(): void {\n if (!this.launcherFinished || this.terminalExitNotified) return;\n this.lastExitCrashed = true;\n this.notifyExitListeners(null, null, \"probe-death\");\n }\n\n /**\n * The supervisor's liveness check found this child gone without an `exit`\n * event having reached us (a lost SIGCHLD). Deliver the one terminal exit\n * its consumers are owed, so the adapter and pool release the host instead\n * of keeping a dead incarnation `running`; an `exit` event that arrives\n * late is then a no-op (`terminalExitNotified`).\n */\n reportObservedChildDeath(): void {\n if (this.stopped || this.terminalExitNotified) return;\n this.stopped = true;\n this.lastExitCrashed = !this.intentionalStop;\n this.notifyExitListeners(null, null, \"process-exit\");\n }\n\n /** Whether the most recent exit looked like an unexpected crash. */\n didLastExitCrash(): boolean {\n return this.lastExitCrashed;\n }\n\n /** The detected public host URL, when known (used to derive the port). */\n getPublicUrl(): string | null {\n return this.publicUrl;\n }\n\n private notifyOutputListeners(data: string): void {\n for (const listener of this.outputListeners) {\n listener(data);\n }\n }\n\n async waitUntilReady(timeoutMs = 12000): Promise<DevServerReadyResult> {\n if (this.launcherStopRequired) {\n const error = new DaemonizedStartRequiresStopError();\n return {\n ok: false,\n timedOut: false,\n crashed: false,\n errorCode: error.code,\n url: this.publicUrl ?? this.explicitTempoHostUrl,\n pid: this.child.pid ?? null,\n message: error.message,\n };\n }\n if (this.stopped && !this.launcherFinished) {\n return {\n ok: false,\n timedOut: false,\n // The process may have crashed before readiness was ever awaited (a\n // fast exit-1). Surface that so the supervisor still auto-retries once.\n crashed: this.lastExitCrashed,\n url: this.publicUrl,\n pid: this.child.pid ?? null,\n message: \"Dev server process has already stopped\",\n };\n }\n\n try {\n if (\n typeof this.tempoHostUrlSource === \"object\" &&\n this.tempoHostUrlSource !== null\n ) {\n if (this.stopCommand) {\n await this.waitForLauncherHandoff(timeoutMs);\n }\n this.explicitTempoHostUrl = await resolveHttpUrlSource({\n source: this.tempoHostUrlSource,\n label: \"urls.tempoHost\",\n cwd: this.commandCwd,\n env: this.commandEnv,\n port: this.injectedPort,\n timeoutMs: 30_000,\n signal: this.launchSignal,\n substitutePort: substituteAppPort,\n });\n } else if (!this.tempoHostUrlIsHardcoded) {\n await withTimeout(\n this.readySignal,\n timeoutMs,\n \"Timed out waiting for dev-server output\",\n );\n }\n } catch (error) {\n throwIfLaunchAborted(this.launchSignal);\n // Distinguish a genuine crash (the process exited before readiness — the\n // exit handler set `lastExitCrashed` and rejected `readySignal` with\n // `ProcessCrashedError`) from a plain timeout (the process is still alive\n // but never printed its ready output). Only a crash is auto-retried.\n const contractError = error instanceof DaemonizedStartRequiresStopError;\n const crashed = this.lastExitCrashed;\n return {\n ok: false,\n timedOut: !crashed && !contractError,\n crashed,\n errorCode: contractError ? error.code : undefined,\n url: this.publicUrl,\n pid: this.child.pid ?? null,\n message: error instanceof Error ? error.message : String(error),\n };\n }\n\n if (this.launcherStopRequired) {\n const error = new DaemonizedStartRequiresStopError();\n return {\n ok: false,\n timedOut: false,\n crashed: false,\n errorCode: error.code,\n url: this.publicUrl ?? this.explicitTempoHostUrl,\n pid: this.child.pid ?? null,\n message: error.message,\n };\n }\n\n const publicUrlCandidates = [\n this.detectedBaseUrl ? buildTempoHostUrl(this.detectedBaseUrl) : null,\n this.explicitTempoHostUrl,\n ].filter((value, index, all): value is string => {\n if (!value) {\n return false;\n }\n return all.indexOf(value) === index;\n });\n\n if (publicUrlCandidates.length === 0) {\n return {\n ok: false,\n timedOut: false,\n url: null,\n pid: this.child.pid ?? null,\n message: \"Tempo host did not report a usable server URL\",\n };\n }\n\n const probeResult = await waitForFirstHttpReachable(\n // Probe the advertised host form AND its 127.0.0.1↔localhost twin —\n // some loopback filters only intercept one form (TEM-873).\n expandLoopbackHostVariants(publicUrlCandidates),\n timeoutMs,\n this.expectedProjectRoot,\n this.expectedIdentity,\n () => !this.stopped || this.launcherFinished,\n );\n const publicUrl = probeResult.url;\n if (!publicUrl) {\n // The host printed a URL (so readiness resolved) but it never answered\n // HTTP. If the process ALSO exited non-zero, this is a CRASH, not a\n // timeout — Next.js 16's per-directory dev-lock abort takes exactly this\n // shape: it prints \"✓ Ready\" + a Local URL, THEN \"Another next dev\n // server is already running\" and exits 1. Without surfacing `crashed`\n // here, that abort was classified as a (non-retryable) timeout, so the\n // supervisor never auto-retried in an isolated directory — the\n // canvas-devserver restart loop in bundle\n // t17dsrf95f4r46daj9b74eczd988yy1w. Surfacing it routes the failure into\n // the crash-retry path, which selects a Tempo-owned suffix and re-spawns\n // without treating the banner PID as kill authority.\n const crashed = this.lastExitCrashed;\n // TEM-873: the child printed its URL and is STILL ALIVE, yet nothing\n // ever answered a probe on any transport or host form — not even a\n // wrong responder. That's loopback HTTP being blocked on this machine\n // (Windows firewall / antivirus filtering), not a server that failed to\n // come up. Classify distinctly so the UI can point at the firewall\n // instead of showing a generic timeout.\n const probeBlocked =\n !crashed && !this.stopped && !probeResult.sawResponder;\n // The inverse shape (TEM-1167): a responder answered on the host URL\n // for the whole wait but never identified as OUR tempo-host. On a\n // Next.js project this is the app answering `/tempo-host` with its own\n // 404 because the scaffolded host wiring is missing, stale, or\n // intercepted by the app's own middleware/proxy — a deterministic\n // failure a respawn can never fix, which previously read as a generic\n // \"did not become reachable\" timeout and produced an unbounded\n // kill/restart loop with `lastError: null` in bug-report bundles.\n // A daemon handoff stops only the launcher; URL probes still own the\n // host's liveness and its responses still carry actionable diagnoses.\n // Keep probeBlocked child-only: an unresponsive daemon may have died,\n // so a completed launcher is not evidence of loopback filtering.\n const canClassifyResponder = !crashed && this.hasLivenessAuthority();\n const wrongResponder =\n canClassifyResponder &&\n probeResult.sawResponder &&\n !probeResult.identityFailure;\n const identityFailure =\n canClassifyResponder && probeResult.identityFailure;\n let message = crashed\n ? \"Dev server exited before its URL became reachable\"\n : \"Tempo host URL did not become reachable in time\";\n if (wrongResponder) {\n const target = publicUrlCandidates[0] ?? \"\";\n message =\n `A server is answering at ${target}, but it isn't this workspace's ` +\n \"Tempo canvas host — the app responded without the Tempo host \" +\n \"document. The Tempo host wiring in this app is likely missing, \" +\n \"stale, or intercepted (for Next.js: the scaffolded /tempo-host \" +\n \"route, or a custom middleware/proxy handling the path). Re-running \" +\n \"Tempo's canvas setup regenerates the wiring.\";\n }\n if (identityFailure) {\n message =\n this.identityFailureMessage ??\n \"The Tempo host answered, but it did not prove the current served host contract. Ensure TEMPO=true is forwarded to the dev server, install tempo-sdk@latest, restart the configured host if it is externally managed, then choose Retry.\";\n }\n if (probeBlocked) {\n const target = publicUrlCandidates[0] ?? \"\";\n let targetHost = \"127.0.0.1\";\n try {\n targetHost = new URL(target).host;\n } catch {\n // keep the default\n }\n // A raw TCP connect refines the message: connect-succeeds means an\n // HTTP-layer filter; connect-fails means connections are blocked\n // outright. Either way it's the machine, not the dev server.\n const tcpConnected = await tcpConnectOnce(target, 800);\n message = tcpConnected\n ? `The dev server is running, but HTTP requests to it on ${targetHost} are being blocked — a firewall or antivirus may be filtering local (loopback) connections on this machine.`\n : `The dev server is running, but Tempo can't connect to it on ${targetHost} — a firewall or antivirus may be blocking local (loopback) connections on this machine.`;\n }\n return {\n ok: false,\n timedOut: !crashed,\n crashed,\n probeBlocked,\n wrongResponder,\n identityFailure,\n errorCode: identityFailure ? \"canvas_host_sdk_incompatible\" : undefined,\n url: publicUrlCandidates[0] ?? null,\n pid: this.child.pid ?? null,\n message,\n };\n }\n\n this.detectedBaseUrl = stripTempoHostRoute(publicUrl);\n this.publicUrl = publicUrl;\n this.readinessConfirmed = true;\n return {\n ok: true,\n timedOut: false,\n url: publicUrl,\n pid: this.child.pid ?? null,\n };\n }\n\n private async waitForLauncherHandoff(timeoutMs: number): Promise<void> {\n const deadline = Date.now() + Math.max(timeoutMs, 30_000);\n while (!this.launcherFinished) {\n throwIfLaunchAborted(this.launchSignal);\n if (this.stopped) {\n await this.readySignal;\n if (this.launcherFinished) return;\n throw new Error(\n \"The canvas start command stopped before urls.tempoHost.command could run.\",\n );\n }\n if (Date.now() >= deadline) {\n throw new Error(\n \"Timed out waiting for the canvas start command to hand off before running urls.tempoHost.command\",\n );\n }\n await waitForLaunchDelay(120, this.launchSignal);\n }\n }\n\n async terminate(): Promise<void> {\n this.intentionalStop = true;\n if (this.terminationPromise) return await this.terminationPromise;\n\n const termination = this.performTermination();\n this.terminationPromise = termination;\n try {\n await termination;\n } finally {\n if (this.terminationPromise === termination) {\n this.terminationPromise = null;\n }\n }\n }\n\n async stop(): Promise<void> {\n await this.terminate();\n }\n\n private async performTermination(): Promise<void> {\n if (this.stopCommand) {\n const stopResult = await runBoundedShellCommand({\n command: this.stopCommand,\n cwd: this.commandCwd,\n env: this.commandEnv,\n timeoutMs: 10_000,\n });\n devLog(\"process:stop_command\", {\n command: this.stopCommand,\n ...stopResult,\n });\n }\n\n if (this.stopped) {\n if (this.launcherFinished) {\n this.lastExitCrashed = false;\n this.notifyExitListeners(0, null, \"process-exit\");\n }\n } else {\n const pid = this.child.pid;\n if (!pid) {\n this.stopped = true;\n this.notifyExitListeners(null, null, \"process-exit\");\n } else {\n const processTreeGone = await terminateManagedLongRunningProcess(\n this.child,\n {\n gracefulShutdownMs: 3_000,\n forcedShutdownMs: 2_000,\n },\n );\n if (!processTreeGone) {\n throw new Error(\n `Canvas devserver process tree rooted at pid ${pid} survived termination`,\n );\n }\n this.stopped = true;\n }\n }\n\n const url = this.publicUrl;\n if (url && !(await waitForHttpUnreachable(url, 5000))) {\n throw new Error(\n `Canvas devserver is still reachable at ${url} after stop`,\n );\n }\n }\n\n forceKill(): void {\n this.intentionalStop = true;\n forceKillManagedLongRunningProcess(this.child, this.hasParentDeathWatchdog);\n }\n\n private notifyExitListeners(\n code: number | null,\n signal: NodeJS.Signals | null,\n reason: TempoHostExitReason,\n terminal = true,\n ): void {\n if (this.exitListeners.length === 0) {\n return;\n }\n\n const listeners = [...this.exitListeners];\n if (terminal) {\n this.exitListeners = [];\n this.terminalExitNotified = true;\n }\n\n for (const listener of listeners) {\n try {\n listener(\n code,\n signal,\n this.lastExitCrashed,\n reason,\n this.intentionalStop,\n );\n } catch {\n // Ignore listener failures.\n }\n }\n }\n}\n\n// Identity of the APP INSTANCE hosting this supervisor process — the Electron\n// main process configures its userData dir, the canvas demo its per-worker\n// workspace dir. Persisted into the devserver ownership marker so a LATER\n// process of the SAME instance can prove \"we started this server\" and destroy\n// it once its manager process is gone. Distinct build flavors (the dev and\n// prod Tempo apps share workspace dirs on disk) get distinct userData paths,\n// so neither ever purges the other's server.\n//\n// First value wins — same contract as `configureDebugLogPath`: every\n// supervisor in the process must agree on one identity, so call this once at\n// app startup before any TempoDevServer is constructed. Left null in bare CLI\n// hosts and tests; a null id never matches an instance, so unidentified\n// processes never purge by ownership and their own records are never\n// ownership-purged by others — they degrade to the non-destructive probe\n// ladder.\nlet supervisorAppInstanceId: string | null = null;\n\nexport function configureSupervisorIdentity(options: {\n appInstanceId: string;\n}): void {\n if (supervisorAppInstanceId !== null) return;\n supervisorAppInstanceId = options.appInstanceId;\n}\n\n// The ownership marker persisted beside `devserverUrl` in `.tempo/status.json`.\n// `managerPid` is the supervisor HOST process (the Electron main / MCP host),\n// not the dev-server child: its death is what proves the server is unmanaged.\ninterface PersistedDevserverOwner {\n appInstanceId: string | null;\n managerPid: number;\n startedAt: number | null;\n portWasTempoAllocated: boolean;\n launchPid: number | null;\n launchProcessStartedAt: string | null;\n launchPort: number | null;\n stopCommand: string | null;\n}\n\n// A marker is only trusted when its managerPid is a real, signalable pid\n// (> 1 — `process.kill(0, …)`/pid 1 have special semantics). Anything else\n// (hand-edited file, partial write) downgrades to \"no marker\", which routes\n// reconcile to the non-destructive probe ladder.\nfunction parsePersistedDevserverOwner(\n value: unknown,\n): PersistedDevserverOwner | null {\n if (typeof value !== \"object\" || value === null) return null;\n const record = value as {\n appInstanceId?: unknown;\n managerPid?: unknown;\n startedAt?: unknown;\n portWasTempoAllocated?: unknown;\n launchPid?: unknown;\n launchProcessStartedAt?: unknown;\n launchPort?: unknown;\n stopCommand?: unknown;\n };\n const managerPid = record.managerPid;\n if (\n typeof managerPid !== \"number\" ||\n !Number.isInteger(managerPid) ||\n managerPid <= 1\n ) {\n return null;\n }\n return {\n appInstanceId:\n typeof record.appInstanceId === \"string\" ? record.appInstanceId : null,\n managerPid,\n startedAt: typeof record.startedAt === \"number\" ? record.startedAt : null,\n portWasTempoAllocated: record.portWasTempoAllocated === true,\n launchPid:\n typeof record.launchPid === \"number\" &&\n Number.isInteger(record.launchPid) &&\n record.launchPid > 1\n ? record.launchPid\n : null,\n launchProcessStartedAt:\n typeof record.launchProcessStartedAt === \"string\"\n ? record.launchProcessStartedAt\n : null,\n launchPort:\n typeof record.launchPort === \"number\" &&\n Number.isInteger(record.launchPort) &&\n record.launchPort > 0 &&\n record.launchPort < 65_536\n ? record.launchPort\n : null,\n stopCommand:\n typeof record.stopCommand === \"string\" && record.stopCommand.length > 0\n ? record.stopCommand\n : null,\n };\n}\n\nexport class TempoDevServer {\n private readonly config: TempoDevServerConfig;\n\n private readonly projectRoot: string;\n\n private readonly tempoRoot: string;\n\n private readonly pagesDir: string;\n\n private readonly commandCwd: string;\n\n private readonly processMarkerPath: string;\n\n private activeHostIdentity: ExpectedTempoHostIdentity | null = null;\n\n // Everything getState() surfaces EXCEPT processStatus, which is deliberately\n // not storable: liveness is derived from the owned child handle on every\n // read (see currentProcessStatus), so a stale mirror recording a live host\n // as dead is unrepresentable.\n private readonly state: Omit<\n TempoDevServerState,\n \"processStatus\" | \"livenessSource\"\n >;\n\n // The process-status verdict that applies only while NO live child is held:\n // \"stopped\" (never started / intentional stop), \"starting\" (start intent\n // recorded, preflight/install running before the spawn), \"error\" (start\n // failed), \"exited\" (the child died). While a live child exists this field\n // is ignored — the handle decides. Rationale: a failed 1.5s health probe\n // once wrote \"exited\" for a live, serving host, and every warm maintenance\n // pass then force-reconciled against it, invalidating live storyboards\n // every ~4s (bundle t17dg5gcp9prjjfzvbbqhjkhp98c0bp7).\n private idleProcessStatus: TempoProcessStatus = \"stopped\";\n\n private readonly registry: CanvasRegistry;\n\n private initializePromise: Promise<DevServerInitResult> | null = null;\n\n // The Node the project's declared requirement resolves to for this tree,\n // when it differs from the managed default (TEM-1536). Resolved by\n // selectNodeRuntimeForStart and applied to the host-process spawn env;\n // install/upgrade shell commands re-resolve per-cwd through the same memo.\n private projectNodeOverride: ProjectNodeOverride | null = null;\n\n private reconcileQueue: Array<{\n reason: string;\n force: boolean;\n resolve: (value: DevServerReconcileResult) => void;\n reject: (error: unknown) => void;\n }> = [];\n\n private reconcileWorkerRunning = false;\n\n private reconcileRetryCount = 0;\n\n private reconcileRetryTimer: ReturnType<typeof setTimeout> | null = null;\n\n private disposed = false;\n\n // Periodic poll that re-asserts I-4 (URL means alive). When the bound\n // dev-server URL becomes HTTP-unreachable, `state.devserverUrl` is cleared\n // so the renderer stops sending traffic into a black hole. See §11.6.\n private urlHealthProbeTimer: ReturnType<typeof setInterval> | null = null;\n private urlHealthProbeUrl: string | null = null;\n\n // When THIS session's server reached ready — the ownership marker's\n // `startedAt`. Kept out of `state` (not renderer-facing) and set at the\n // single site that assigns `state.devserverUrl`, so re-persists don't\n // drift the recorded start time.\n private devserverReadyAt: number | null = null;\n private devserverLaunchPid: number | null = null;\n private devserverLaunchProcessStartedAt: string | null = null;\n private devserverLaunchPort: number | null = null;\n private devserverStopCommand: string | null = null;\n private devserverPortWasTempoAllocated = false;\n /** Stop authority persisted after handoff but before URL readiness. */\n private devserverExternalRuntimePending = false;\n private static readonly URL_HEALTH_PROBE_INTERVAL_MS = 5_000;\n\n private static readonly URL_HEALTH_FAILURES_TO_DEMOTE = 3;\n\n // File watcher on managed files (Section 11.6). On any relevant change/delete\n // a debounced repair() call fires. `@modules/file-watcher` wraps\n // @parcel/watcher with recursive watching, batching, coalescing, and\n // auto-recovery, so this path does not need a periodic drift-polling backstop.\n private managedFileWatchers: WatchSubscription[] = [];\n private managedFileWatcherStopPromise: Promise<void> | null = null;\n private repairDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n private static readonly REPAIR_DEBOUNCE_MS = 100;\n\n private static readonly RECONCILE_RETRY_DELAYS = [500, 1000, 2000, 5000];\n\n // Backoff for self-enqueued drift-correction passes that keep finding the\n // same drift without fixing it (e.g. writes suppressed by the thrash\n // breaker, or any future \"permanent drift\" state the deferred-generation\n // bail doesn't cover). First correction stays immediate; a persistent\n // no-op cycle settles at the 30s cap instead of re-enqueuing itself at\n // hundreds of reconciles per second (TEM-1490 shredded the rotating devlog\n // at ~520 cycles/sec this way).\n private convergenceDriftStreak = 0;\n private convergenceRetryTimer: ReturnType<typeof setTimeout> | null = null;\n private static readonly CONVERGENCE_BACKOFF_DELAYS = [\n 0, 250, 1000, 5000, 30_000,\n ];\n\n // Thrash circuit breaker for managed-file rewrites (regression:\n // \"storyboards are blank and flash in/out\", bug report 2026-06-10).\n // When an external writer keeps re-drifting a managed file right after every\n // restore, restoring on every reconcile is an unbounded HMR-full-reload storm\n // that remounts every storyboard projection.\n //\n // The breaker keys on (file, content): thrash is *restoring the same content\n // over and over* because a competing writer keeps reverting it. Content\n // EVOLUTION — the desired content differing from what we last wrote — is a\n // legitimate state change and ALWAYS writes through, even mid-backoff. This\n // distinction is load-bearing: demand registration appends a new, different\n // entry to tempo-host.tsx every time a canvas is opened, so a user walking\n // canvas-by-canvas rewrites the host with evolving content in quick\n // succession. A content-blind breaker armed on the 5th such rewrite and then\n // silently dropped every later canvas's registration — the canvas never\n // landed in the host and never rendered, with no error anywhere (bug 2026-07).\n //\n // Policy (same-content only): after MANAGED_REWRITE_THRASH_THRESHOLD\n // consecutive rewrites of the same file to the SAME content (each within\n // MANAGED_REWRITE_THRASH_WINDOW_MS of the previous), rewrites of that file are\n // suppressed for MANAGED_REWRITE_BACKOFF_MS, then ONE probe rewrite is\n // allowed; if the drift persists the backoff re-arms immediately, so\n // steady-state thrash is damped to ~1 rewrite per backoff period. A rewrite\n // whose desired content differs from the last write resets the count and\n // writes through immediately. Forced reconciles (initialize) bypass and reset\n // the breaker; a pass that finds the file already converged clears it.\n //\n // lastContent is the exact desired content of the previous write (null for a\n // managed-file deletion, e.g. removing a stale artifact) — stored verbatim\n // rather than hashed since managed files are few and small (KBs).\n private managedRewriteThrash = new Map<\n string,\n {\n consecutiveRewrites: number;\n lastRewriteAt: number;\n backoffUntil: number;\n lastContent: string | null;\n }\n >();\n\n private static readonly MANAGED_REWRITE_THRASH_THRESHOLD = 5;\n\n private static readonly MANAGED_REWRITE_THRASH_WINDOW_MS = 10_000;\n\n private static readonly MANAGED_REWRITE_BACKOFF_MS = 30_000;\n\n // How many recent reconcile passes to retain for diagnostics. Sized to cover\n // a few seconds of a worst-case loop (watcher debounce + repair debounce)\n // so a snapshot still shows the churn pattern, not just its tail.\n private static readonly RECONCILE_ACTIVITY_LIMIT = 50;\n\n // A start episode is at most ~4 attempts (initial + up to 3 retries); keep a\n // few episodes' worth so a snapshot shows the recent recovery history.\n private static readonly START_ATTEMPT_LIMIT = 30;\n\n // Post-ready crashes drive the auto-retry loop; keep enough to show several\n // crash→restart cycles in one snapshot.\n private static readonly RECENT_EXITS_LIMIT = 30;\n\n // The currently-running host process, when one is live. Held so `getState()`\n // can surface its stdout/stderr tail (diagnostics) without threading the\n // local `process` var out of `startDevServer`.\n private activeHostProcess: TempoHostProcess | null = null;\n\n private urlHealthProbeConsecutiveFailures = 0;\n // Bumped whenever the probe loop is (re)started or stopped, so a probe\n // that was already in flight for the PREVIOUS loop cannot report into this\n // one. URL equality is not enough of a fence: a replacement process on the\n // same URL would inherit the old process's last failure.\n private urlHealthProbeGeneration = 0;\n\n // Tempo-managed Next hosts never share the user's ordinary `.next` cache.\n // This starts at the stable Tempo-owned directory and only receives a suffix\n // after Next reports an observed lock conflict for that directory.\n private nextDistDir: string | null = null;\n\n private nextDistDirClaim: {\n distDir: string;\n claimPath: string;\n token: string;\n } | null = null;\n\n private static readonly installBarriers = new Map<string, Promise<void>>();\n\n constructor(config: TempoDevServerConfig) {\n const paths = resolveRuntimePaths(config.tempoConfigPath);\n this.config = {\n ...config,\n scripts: {\n ...config.scripts,\n },\n };\n this.projectRoot = paths.projectRoot;\n this.tempoRoot = paths.tempoRoot;\n this.pagesDir = paths.pagesDir;\n this.commandCwd = paths.tempoRoot;\n this.processMarkerPath = path.join(\n paths.tempoRoot,\n \".tempo\",\n TEMPO_PROCESS_MARKER_FILE,\n );\n this.state = {\n setupStatus: \"uninitialized\",\n reconcileStatus: \"idle\",\n initialized: false,\n generation: 0,\n lastError: null,\n dependenciesInstalled: false,\n devserverUrl: null,\n urlUnreachableSince: null,\n recentReconciles: [],\n recentStartAttempts: [],\n recentExits: [],\n };\n this.registry = new CanvasRegistry(\n this.projectRoot,\n this.tempoRoot,\n this.pagesDir,\n );\n }\n\n getConfig(): TempoDevServerConfig {\n return {\n ...this.config,\n scripts: {\n ...this.config.scripts,\n },\n };\n }\n\n // The single source of host liveness. A held, signalable child is authority\n // for foreground starts; after a clean daemonizing launcher exits, the\n // retained host handle switches authority to the HTTP probe. Terminal\n // verdicts still come only from an owned exit or confirmed probe death.\n private activeHostHasLivenessAuthority(): boolean {\n const active = this.activeHostProcess;\n if (!active) return false;\n return typeof active.hasLivenessAuthority === \"function\"\n ? active.hasLivenessAuthority()\n : active.isAlive();\n }\n\n private activeHostLivenessSource(): TempoHostLivenessSource | null {\n const active = this.activeHostProcess;\n if (!active) return null;\n return typeof active.getLivenessSource === \"function\"\n ? active.getLivenessSource()\n : \"child\";\n }\n\n private currentProcessStatus(): TempoProcessStatus {\n if (this.activeHostHasLivenessAuthority()) {\n return this.state.devserverUrl ? \"running\" : \"starting\";\n }\n return this.idleProcessStatus;\n }\n\n getState(): TempoDevServerState {\n return {\n ...this.state,\n processStatus: this.currentProcessStatus(),\n livenessSource: this.activeHostLivenessSource(),\n // Copy the buffer so callers (e.g. the diagnostic snapshot) can't mutate\n // — or observe later mutations of — our internal window.\n recentReconciles: this.state.recentReconciles.map((entry) => ({\n ...entry,\n })),\n recentStartAttempts: this.state.recentStartAttempts.map((entry) => ({\n ...entry,\n })),\n recentExits: this.state.recentExits.map((entry) => ({ ...entry })),\n recentOutputTail: this.activeHostProcess?.getRecentOutputTail() ?? null,\n };\n }\n\n // Append a host-process exit to the rolling diagnostics window. Captures the\n // post-ready crash that `recentStartAttempts` (which only records up to\n // readiness) misses entirely. Also mirrored to disk so it survives the\n // renderer's `restartCanvas` (which disposes this whole supervisor instance).\n private recordHostExit(entry: HostExitEntry): void {\n this.state.recentExits.push(entry);\n const overflow =\n this.state.recentExits.length - TempoDevServer.RECENT_EXITS_LIMIT;\n if (overflow > 0) {\n this.state.recentExits.splice(0, overflow);\n }\n void this.persistRecentExits();\n }\n\n private recentExitsFilePath(): string {\n return path.join(this.tempoRoot, \".tempo\", TEMPO_RECENT_EXITS_FILE);\n }\n\n // Fire-and-forget mirror of the exit log to disk. Best-effort: a failed write\n // just means a fresh supervisor won't see this crash, which is no worse than\n // before persistence existed.\n private async persistRecentExits(): Promise<void> {\n try {\n await mkdir(path.dirname(this.recentExitsFilePath()), {\n recursive: true,\n });\n await atomicWriteText(\n this.recentExitsFilePath(),\n JSON.stringify(this.state.recentExits),\n );\n } catch {}\n }\n\n // Seed the in-memory exit log from disk so a supervisor created AFTER a\n // crash→restart still reports the crashing instance's exit (+ output tail).\n private async loadRecentExitsFromDisk(): Promise<void> {\n const loaded = await readJsonFile<HostExitEntry[]>(\n this.recentExitsFilePath(),\n );\n if (!Array.isArray(loaded)) {\n return;\n }\n const valid = loaded.filter(\n (entry): entry is HostExitEntry =>\n !!entry &&\n typeof entry === \"object\" &&\n typeof (entry as HostExitEntry).at === \"number\",\n );\n // On-disk history is older than anything recorded this run, so it goes\n // first; keep only the most recent within the cap.\n this.state.recentExits = [...valid, ...this.state.recentExits].slice(\n -TempoDevServer.RECENT_EXITS_LIMIT,\n );\n }\n\n // Append a completed reconcile to the rolling diagnostics window, evicting\n // the oldest once the cap is reached.\n private recordReconcileActivity(entry: ReconcileActivityEntry): void {\n this.state.recentReconciles.push(entry);\n const overflow =\n this.state.recentReconciles.length -\n TempoDevServer.RECONCILE_ACTIVITY_LIMIT;\n if (overflow > 0) {\n this.state.recentReconciles.splice(0, overflow);\n }\n }\n\n // Append a host-launch attempt to the rolling diagnostics window. Mirrors\n // `recordReconcileActivity`. Surfaced via `getState()` so a bundle shows the\n // auto-retry lifecycle directly.\n private recordStartAttempt(entry: HostStartAttemptEntry): void {\n this.state.recentStartAttempts.push(entry);\n const overflow =\n this.state.recentStartAttempts.length -\n TempoDevServer.START_ATTEMPT_LIMIT;\n if (overflow > 0) {\n this.state.recentStartAttempts.splice(0, overflow);\n }\n }\n\n async initialize(): Promise<DevServerInitResult> {\n if (this.initializePromise) {\n return await this.initializePromise;\n }\n\n this.initializePromise = this.performInitialize();\n try {\n return await this.initializePromise;\n } finally {\n this.initializePromise = null;\n }\n }\n\n async reconcile(\n reason: string,\n force = false,\n ): Promise<DevServerReconcileResult> {\n return await this.enqueueReconcile(reason, force);\n }\n\n async startDevServer(options?: {\n /** Package-manager stdout/stderr, available before the host is spawned. */\n onInstallOutput?: (data: string) => void;\n forceInstall?: boolean;\n signal?: AbortSignal;\n /**\n * Invoked synchronously each time a host child is spawned — the initial\n * launch and every supervisor-driven retry — before any readiness wait.\n * Lets the caller stamp what state that exact child was born into.\n */\n onProcessSpawned?: () => void;\n }): Promise<TempoHostDevServerHandle> {\n const t = devTimer(\"supervisor:start_dev_server\", {\n projectRoot: this.projectRoot,\n tempoRoot: this.tempoRoot,\n commandCwd: this.commandCwd,\n installScript: this.config.scripts.install,\n updateSdkScript: this.config.scripts.updateSdk,\n startScript: this.config.scripts.start,\n forceInstall: options?.forceInstall ?? false,\n dependenciesInstalled: this.state.dependenciesInstalled,\n });\n if (!this.state.initialized) {\n t.done({ ok: false, reason: \"not_initialized\" });\n throw new ValidationError(\n \"TempoDevServer.startDevServer() requires initialize() first\",\n );\n }\n throwIfLaunchAborted(options?.signal);\n\n // Refuse a PowerShell-shaped start command before installing or spawning\n // anything: cmd.exe would abort while PARSING it (\"X was unexpected at\n // this time.\"), and no shell Tempo spawns with can run it. Checking the\n // configured string here — rather than waiting for the child's stderr —\n // makes the failure deterministic on every platform and skips the doomed\n // install/spawn/crash-retry cycle entirely. See TEM-436.\n const startDialectIssue = findPowerShellSyntax(this.config.scripts.start);\n if (startDialectIssue) {\n t.done({ ok: false, reason: \"start_shell_dialect_mismatch\" });\n throw new StartShellDialectMismatchError(\n this.config.scripts.start,\n describePowerShellRejection(startDialectIssue),\n );\n }\n const tempoHostUrlCommand =\n this.config.tempoHostUrl !== null &&\n typeof this.config.tempoHostUrl === \"object\"\n ? this.config.tempoHostUrl.command\n : null;\n const tempoHostUrlDialectIssue = tempoHostUrlCommand\n ? findPowerShellSyntax(tempoHostUrlCommand)\n : null;\n if (tempoHostUrlCommand && tempoHostUrlDialectIssue) {\n t.done({ ok: false, reason: \"tempo_host_url_shell_dialect_mismatch\" });\n throw new StartShellDialectMismatchError(\n tempoHostUrlCommand,\n `urls.tempoHost.command contains PowerShell-only syntax (${tempoHostUrlDialectIssue}); use one plain cross-platform command`,\n );\n }\n\n // Record start intent so status reads \"starting\" through the whole\n // preflight/install phase, which runs before any child exists.\n this.idleProcessStatus = \"starting\";\n let process: TempoHostProcess;\n let missingRootDepsProjectRoot: string | null = null;\n let sdkUpgradeResult: TempoSdkUpgradeResult | null = null;\n const outputListeners: Array<(data: string) => void> = [];\n const hintListeners: DevServerHintListener[] = [];\n const exitListeners: TempoHostExitListener[] = [];\n\n const attachProcessListeners = (activeProcess: TempoHostProcess): void => {\n activeProcess.onExit((code, signal, crashed, reason, intentional) => {\n if (reason === \"launcher-finished\") {\n devLog(\"supervisor:launcher_finished\", {\n pid: activeProcess.getPid(),\n });\n for (const listener of exitListeners) {\n listener(code, signal, crashed, reason, intentional);\n }\n return;\n }\n // Record EVERY exit of the active host — including the post-ready crash\n // that `recentStartAttempts` can't see — with the child's output tail so\n // the crash reason is in the bundle. (#3)\n if (process === activeProcess) {\n this.recordHostExit({\n at: Date.now(),\n code,\n signal,\n crashed: activeProcess.didLastExitCrash(),\n livenessSource: activeProcess.getLivenessSource(),\n afterReady: activeProcess.hasBecomeReady(),\n port: portFromReadyUrl(activeProcess.getPublicUrl()),\n outputTail: activeProcess.getRecentOutputTail(),\n });\n void this.releaseNextDistDirClaim();\n }\n if (this.activeHostProcess === activeProcess) {\n this.activeHostProcess = null;\n }\n if (process !== activeProcess) {\n return;\n }\n this.devserverExternalRuntimePending = false;\n this.state.devserverUrl = null;\n this.state.urlUnreachableSince = null;\n this.idleProcessStatus = \"exited\";\n this.stopUrlHealthProbe();\n this.removeProcessMarkerForPid(activeProcess.getPid()).catch(() => {});\n this.persistStatus().catch(() => {});\n for (const listener of exitListeners) {\n listener(code, signal, crashed, reason, intentional);\n }\n });\n for (const listener of outputListeners) {\n activeProcess.onOutput(listener);\n }\n for (const listener of hintListeners) {\n activeProcess.onHint(listener);\n }\n };\n\n const spawnProcess = async (attempt: number): Promise<TempoHostProcess> => {\n throwIfLaunchAborted(options?.signal);\n const startPlan = await this.buildStartCommandForAttempt(attempt);\n throwIfLaunchAborted(options?.signal);\n const expectedIdentity = await this.readExpectedHostIdentity();\n const stopCommand = this.config.scripts.stop\n ? substituteAppPort(this.config.scripts.stop, startPlan.injectedPort)\n : null;\n this.devserverStopCommand = stopCommand;\n this.devserverLaunchPort = startPlan.injectedPort;\n this.devserverPortWasTempoAllocated = startPlan.portWasTempoAllocated;\n this.devserverExternalRuntimePending = false;\n let activeProcess!: TempoHostProcess;\n activeProcess = new TempoHostProcess({\n cwd: this.commandCwd,\n command: startPlan.command,\n explicitTempoHostUrl: startPlan.tempoHostUrl,\n tempoHostUrlSource: this.config.tempoHostUrl,\n injectedPort: startPlan.injectedPort,\n launchSignal: options?.signal,\n tempoHostUrlIsHardcoded:\n typeof this.config.tempoHostUrl === \"string\" &&\n !this.config.tempoHostUrl.includes(\"${PORT}\"),\n expectedProjectRoot: this.projectRoot,\n expectedIdentity,\n identityFailureMessage: sdkUpgradeResult\n ? new CanvasHostIdentityError({\n managedUpgradeAttempted: sdkUpgradeResult.attempted,\n remediationCommand: sdkUpgradeResult.plan.remediationCommand,\n remediationDirectory: sdkUpgradeResult.plan.cwd,\n upgradeError: sdkUpgradeResult.error ?? null,\n projectNodeHint: getProjectNodeUnsatisfiedHint(this.commandCwd),\n }).message\n : null,\n missingRootDepsProjectRoot,\n stopCommand,\n onLauncherHandoff: async () => {\n if (this.activeHostProcess !== activeProcess) return;\n this.devserverExternalRuntimePending = true;\n await this.persistStatus();\n },\n // The tempo-sdk Next plugin applies this documented Next config field.\n // It keeps Tempo's host cache isolated from the user's own `next dev`.\n extraEnv: {\n ...startPlan.env,\n ...(expectedIdentity\n ? { TEMPO_WORKSPACE_NONCE: expectedIdentity.workspaceNonce }\n : {}),\n ...(this.nextDistDir\n ? { TEMPO_NEXT_DIST_DIR: this.nextDistDir }\n : {}),\n },\n watchdogScriptPath: await this.ensureDevserverWatchdogScript(),\n projectNode: toProjectNodeEnvOption(this.projectNodeOverride),\n });\n process = activeProcess;\n this.activeHostIdentity = expectedIdentity;\n this.activeHostProcess = activeProcess;\n this.devserverLaunchPid = activeProcess.getPid();\n this.devserverLaunchProcessStartedAt = this.devserverLaunchPid\n ? await readProcessStartedAt(this.devserverLaunchPid)\n : null;\n attachProcessListeners(activeProcess);\n // Supervision is established; now the caller stamps what this exact\n // child was born into (see startDevServer options). A throwing hook is\n // a caller bug, never a reason to leak the child it was told about.\n try {\n options?.onProcessSpawned?.();\n } catch (error) {\n devLog(\"supervisor:on_process_spawned_threw\", {\n pid: activeProcess.getPid(),\n error: error instanceof Error ? error.message : String(error),\n });\n }\n await this.writeProcessMarker(activeProcess.getPid()).catch(() => {});\n if (options?.signal?.aborted) {\n await activeProcess.terminate();\n throw launchAbortError();\n }\n return activeProcess;\n };\n\n try {\n // Persisted status can outlive an interrupted install, changed inputs,\n // or deleted packages. Recheck both receipts and declared dependencies.\n if (this.state.dependenciesInstalled) {\n const [tempoInstallReady, rootInstallReady] = await Promise.all([\n this.tempoInstallReady(),\n this.projectRootInstallReady(),\n ]);\n if (!tempoInstallReady || !rootInstallReady) {\n devLog(\"supervisor:dependencies_stale\", {\n projectRoot: this.projectRoot,\n tempoRoot: this.tempoRoot,\n rootInstallReady,\n tempoInstallReady,\n });\n this.state.dependenciesInstalled = false;\n await this.persistStatus().catch(() => {});\n }\n }\n await this.selectNodeRuntimeForStart();\n throwIfLaunchAborted(options?.signal);\n if (options?.forceInstall || !this.state.dependenciesInstalled) {\n await this.installDependenciesForStart(\n options?.forceInstall === true,\n options?.signal,\n options?.onInstallOutput,\n );\n }\n throwIfLaunchAborted(options?.signal);\n // A successful custom install may legitimately leave the host-side root\n // empty (Docker/devcontainer installs live across the process boundary).\n // Record the fact only as context: it cannot block the start. If the\n // spawned server later emits a package-resolution failure, its observed\n // hint is enriched with the existing typed `root_deps_missing` remedy.\n missingRootDepsProjectRoot =\n (await this.projectRootDepsPresentOrNotRequired(false))\n ? null\n : this.projectRoot;\n await this.selectNextDistDir();\n const installRoots = [...new Set([this.projectRoot, this.tempoRoot])];\n let sdkInstallReceipts: Map<string, boolean> | undefined;\n let sdkCommandCompleted = false;\n sdkUpgradeResult = await ensureTempoSdkLatestSpec(\n this.commandCwd,\n options?.signal,\n {\n updateCommand: this.config.scripts.updateSdk,\n runCommand: async (command, cwd, signal) => {\n // An SDK update can relink the tree without changing its inputs.\n // Keep no completion proof across a failed or cancelled command.\n const affectedRoots =\n this.config.scripts.updateSdk?.trim() ||\n (await resolveTempoSdkUpgradePlan(cwd))\n .governedByAncestorWorkspace\n ? installRoots\n : [this.tempoRoot];\n sdkInstallReceipts = new Map();\n this.state.dependenciesInstalled = false;\n await this.persistStatus();\n for (const root of affectedRoots) {\n // Preflight established this proof before SDK maintenance\n // changed the declared spec, so it may no longer match its key.\n if ((await readInstallCompletionMarker(root)) !== null) {\n sdkInstallReceipts.set(\n root,\n await pathExists(\n path.join(\n root,\n \"node_modules\",\n EXTERNAL_LAYOUT_INSTALL_MARKER,\n ),\n ),\n );\n }\n await invalidateInstallCompletionMarkers(root);\n }\n await runShellCommand(\n command,\n cwd,\n signal,\n options?.onInstallOutput,\n );\n sdkCommandCompleted = true;\n },\n },\n );\n throwIfLaunchAborted(options?.signal);\n if (\n sdkInstallReceipts &&\n (sdkCommandCompleted || !sdkUpgradeResult.error)\n ) {\n // A clean install remains complete even if its SDK is still stale.\n // Restore only prior proof; the host identity checks SDK compatibility.\n for (const [root, externalLayout] of sdkInstallReceipts) {\n await writeInstallCompletionMarker(root);\n if (externalLayout) await writeExternalLayoutInstallMarker(root);\n }\n this.state.dependenciesInstalled = true;\n await this.persistStatus();\n } else if (!sdkInstallReceipts) {\n // ensureTempoSdkLatestSpec may intentionally update package.json and\n // a local SDK link without running an install command. Refresh only\n // existing proof so that change does not cause a redundant install.\n await Promise.all(installRoots.map(refreshInstallCompletionMarkers));\n }\n\n process = await spawnProcess(0);\n } catch (error) {\n this.idleProcessStatus = \"error\";\n this.state.lastError =\n error instanceof Error ? error.message : String(error);\n t.done({\n ok: false,\n reason: \"preflight_or_spawn_failed\",\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n\n // Wrap the handle to persist the URL when the devserver becomes ready and\n // kick off the periodic reachability probe (Section 11.6 / I-4 enforcement).\n const self = this;\n const stopActiveProcess = async (): Promise<void> => {\n self.stopUrlHealthProbe();\n await process.terminate();\n await self.releaseNextDistDirClaim();\n await self.removeProcessMarkerForPid(process.getPid()).catch(() => {});\n self.idleProcessStatus = \"stopped\";\n };\n return {\n onExit(listener) {\n exitListeners.push(listener);\n if (process.getLivenessSource() === \"probe\") {\n listener(0, null, false, \"launcher-finished\", false);\n }\n },\n onOutput(listener) {\n outputListeners.push(listener);\n process.onOutput(listener);\n },\n onHint(listener) {\n hintListeners.push(listener);\n process.onHint(listener);\n },\n getLastHint() {\n return process.getLastHint();\n },\n getLivenessSource() {\n return process.getLivenessSource();\n },\n async stop() {\n await stopActiveProcess();\n },\n async terminate() {\n await stopActiveProcess();\n },\n forceKill() {\n self.stopUrlHealthProbe();\n process.forceKill();\n void self.releaseNextDistDirClaim();\n self.removeProcessMarkerForPid(process.getPid()).catch(() => {});\n self.idleProcessStatus = \"stopped\";\n },\n async waitUntilReady(timeoutMs?: number) {\n let result = await process.waitUntilReady(timeoutMs);\n self.recordStartAttempt({\n at: Date.now(),\n kind: \"initial\",\n attempt: 0,\n port: portFromReadyUrl(result.url),\n livenessSource: process.getLivenessSource(),\n nextDistDir: self.nextDistDir ?? undefined,\n ...mapResultToAttemptOutcome(result),\n });\n let crashRetriesUsed = 0;\n let sidestepRetriesUsed = 0;\n let nodeToolchainRetriesUsed = 0;\n for (let attempt = 1; attempt <= 3; attempt += 1) {\n const portCollision = shouldRetryPortCollision(result, process);\n const crashed =\n !portCollision && !result.ok && result.crashed === true;\n // Capture the crashed host's output before teardown so recovery can\n // classify the failure that was actually observed.\n const crashOutput = process.getRecentOutputTail();\n // A verified conflict in the stable Tempo-owned directory gets one\n // retry in a fresh sibling. No PID inference or killing is involved.\n const lockConflictSidestep =\n crashed &&\n sidestepRetriesUsed === 0 &&\n self.nextDistDir === TEMPO_NEXT_STABLE_DIST_DIR &&\n parseDevServerLockConflict(crashOutput) !== null;\n const observedNodeFailure =\n crashed &&\n process.getLastHint()?.class === \"node-version-unsupported\";\n const nodeToolchainRetry =\n observedNodeFailure &&\n nodeToolchainRetriesUsed === 0 &&\n shouldRetryWithBundledNode({\n command: self.config.scripts.start,\n hintClass: process.getLastHint()?.class ?? null,\n bundledNodeAvailable: getBundledNodeBinDir() !== null,\n bundledNodeAlreadyActive: isBundledNodeActivated(),\n });\n // A \"clear crash\" (the host process exited before it became ready)\n // auto-retries exactly ONCE. A second crash is surfaced (the manual\n // \"failed to start\" toast + Retry), never looped — looping a\n // genuinely broken config is the failure mode the forceKill comment\n // below was written to avoid. A plain timeout is NOT a clear crash\n // and is not auto-retried. Port collisions and the dev-lock sidestep\n // keep their own dedicated retry budgets and don't consume the one\n // crash retry.\n const clearCrash =\n crashed &&\n !observedNodeFailure &&\n !lockConflictSidestep &&\n crashRetriesUsed === 0;\n if (\n !portCollision &&\n !lockConflictSidestep &&\n !nodeToolchainRetry &&\n !clearCrash\n ) {\n break;\n }\n if (lockConflictSidestep) {\n sidestepRetriesUsed += 1;\n } else if (nodeToolchainRetry) {\n nodeToolchainRetriesUsed += 1;\n setBundledNodeActivated(true);\n self.projectNodeOverride = await resolveProjectNodeOverrideForDir(\n self.tempoRoot,\n );\n devLog(\"supervisor:node_toolchain_retry\", {\n command: self.config.scripts.start,\n bundledNodeBinDir: getBundledNodeBinDir(),\n });\n } else if (clearCrash) {\n crashRetriesUsed += 1;\n }\n self.stopUrlHealthProbe();\n await process.terminate();\n await self\n .removeProcessMarkerForPid(process.getPid())\n .catch(() => {});\n if (lockConflictSidestep) {\n self.nextDistDir = await self.selectSuffixedNextDistDir();\n devLog(\"supervisor:next_dist_dir_suffixed\", {\n distDir: self.nextDistDir,\n reason: \"observed-live-lock\",\n tempoRoot: self.tempoRoot,\n });\n }\n process = await spawnProcess(attempt);\n result = await process.waitUntilReady(timeoutMs);\n self.recordStartAttempt({\n at: Date.now(),\n kind: portCollision\n ? \"port-retry\"\n : lockConflictSidestep\n ? \"lock-sidestep-retry\"\n : nodeToolchainRetry\n ? \"node-toolchain-retry\"\n : \"crash-retry\",\n attempt,\n port: portFromReadyUrl(result.url),\n livenessSource: process.getLivenessSource(),\n nextDistDir: self.nextDistDir ?? undefined,\n ...mapResultToAttemptOutcome(result),\n });\n }\n if (result.ok && result.url) {\n self.state.devserverUrl = result.url;\n self.state.urlUnreachableSince = null;\n self.devserverReadyAt = Date.now();\n self.devserverExternalRuntimePending = false;\n // Persist descendants as ownership evidence for T-03's teardown\n // ladder. A port listener is useful evidence only when it is\n // already a member of that spawned tree. In particular, lsof names\n // com.docker.backend for a Docker-published port on macOS; promoting\n // that unrelated listener into the launch identity would make a\n // later pid/start-time check prove ownership of the wrong process.\n const shellPid = process.getPid();\n if (shellPid) {\n const workerPids = new Set(\n await resolveDescendantPids(shellPid).catch(() => []),\n );\n const readyPort = portFromReadyUrl(result.url);\n if (readyPort != null) {\n const listenerPid = await findPortListenerPid(readyPort).catch(\n () => null,\n );\n if (listenerPid != null && workerPids.has(listenerPid)) {\n self.devserverLaunchPid = listenerPid;\n self.devserverLaunchProcessStartedAt =\n await readProcessStartedAt(listenerPid);\n }\n }\n await self\n .writeProcessMarker(shellPid, [...workerPids])\n .catch(() => {});\n }\n await self.persistStatus();\n self.startUrlHealthProbe(result.url);\n t.done({ ok: true, url: result.url, pid: result.pid });\n } else {\n // Startup failed (output timeout, or the host URL never became\n // reachable in time). Tear down the process we spawned BEFORE\n // marking error — otherwise the detached child group survives.\n //\n // The command is launched as a detached, shell-wrapped group\n // (`sh -c \"… next dev …\"`), so on Next.js the live tree is\n // sh → next-cli → next-server. If we only mark `error` and walk\n // away (the old behaviour), `next-server` keeps running orphaned\n // and keeps holding the Turbopack lock under the Tempo-owned\n // distDir (`.next/tempo*/`). Every\n // subsequent start then collides with that lock and exits 1 — the\n // canvas-devserver \"Process exited (1)\" restart loop that no\n // amount of Retry/relaunch can clear. The unified termination ladder\n // runs the configured external stop and then tears down our process\n // group, which releases the lock before another start.\n self.stopUrlHealthProbe();\n await process.terminate();\n await self\n .removeProcessMarkerForPid(process.getPid())\n .catch(() => {});\n self.idleProcessStatus = \"error\";\n // A conflict after the isolated-directory retry means the host did\n // not honor the override or another supervisor claimed that exact\n // directory. Surface the observed holder without acting on its PID.\n const finalConflict = parseDevServerLockConflict(\n process.getRecentOutputTail(),\n );\n if (finalConflict) {\n const enriched =\n `Another dev server (PID ${finalConflict.pid}) is holding the ` +\n `selected Tempo canvas build directory. Close the other Tempo ` +\n `session for this worktree, then retry.`;\n result = { ...result, message: enriched };\n }\n const finalHint = process.getLastHint();\n if (finalHint?.errorCode) {\n const typedMessage =\n finalHint.class === \"node-version-unsupported\"\n ? new NodeVersionUnsupportedError(\n extractObservedNodeVersion(process.getRecentOutputTail()) ??\n \"unknown or missing\",\n \"Node.js 20.19+ or 22.12+ for Vite 7+\",\n ).message\n : finalHint.hint;\n result = {\n ...result,\n errorCode: finalHint.errorCode,\n message: typedMessage,\n };\n }\n // Record WHY in durable state. Bug-report bundles from the\n // wrong-responder restart loop (TEM-1167) showed dozens of exits\n // with `lastError: null` — the failure classification lived only in\n // this result and was lost with the supervisor instance.\n self.state.lastError =\n result.message ?? \"Canvas dev server failed to become ready\";\n await self.persistStatus().catch(() => {});\n t.done({\n ok: false,\n reason: \"not_ready\",\n timedOut: result.timedOut,\n url: result.url,\n pid: result.pid,\n message: result.message,\n });\n }\n return result;\n },\n };\n }\n\n private async installDependenciesForStart(\n forceInstall: boolean,\n signal?: AbortSignal,\n onOutput?: (data: string) => void,\n ): Promise<void> {\n const installTimer = devTimer(\"supervisor:install\", {\n tempoRoot: this.tempoRoot,\n commandCwd: this.commandCwd,\n command: this.config.scripts.install,\n });\n\n try {\n const installWork = async (): Promise<void> => {\n throwIfLaunchAborted(signal);\n const steps = await this.buildInstallSteps(forceInstall);\n if (steps.length === 0) {\n return;\n }\n this.state.dependenciesInstalled = false;\n await this.persistStatus();\n try {\n for (const step of steps) {\n throwIfLaunchAborted(signal);\n await runInstallStep(step, signal, onOutput);\n }\n } catch (error) {\n throwIfLaunchAborted(signal);\n if (\n !steps.some((step) => step.label === \"custom tempo install\") ||\n !missingInstallEnvironment(error)\n )\n throw error;\n // Shared projects often carry a Docker-based installer from the\n // author's machine. When that tool is unavailable, install the\n // declared JS dependencies with each root's own package manager.\n // Keep the config intact; registry, permission and build failures\n // are not eligible. Runtime errors still surface in the canvas.\n const localSteps = await this.buildInstallSteps(false, false);\n if (!localSteps.length) throw error;\n console.warn(\n \"[tempo-devserver] The configured installer needs unavailable local tooling; trying a local dependency install for this launch.\",\n );\n for (const step of localSteps) {\n throwIfLaunchAborted(signal);\n await runInstallStep(step, signal, onOutput);\n }\n }\n };\n\n // A cancelled install retains this key until its process tree settles.\n // A later supervisor waits, then re-evaluates the completion markers\n // under its own abort signal instead of inheriting old cancellation.\n while (TempoDevServer.installBarriers.has(this.projectRoot)) {\n await waitForInstallBarrier(\n TempoDevServer.installBarriers.get(this.projectRoot)!,\n signal,\n );\n }\n const work = installWork();\n TempoDevServer.installBarriers.set(this.projectRoot, work);\n try {\n await work;\n } finally {\n if (TempoDevServer.installBarriers.get(this.projectRoot) === work) {\n TempoDevServer.installBarriers.delete(this.projectRoot);\n }\n }\n throwIfLaunchAborted(signal);\n this.state.dependenciesInstalled = true;\n await this.persistStatus();\n installTimer.done({ ok: true });\n } catch (error) {\n installTimer.done({\n ok: false,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n private async buildInstallSteps(\n forceInstall: boolean,\n useConfiguredScript = true,\n ): Promise<InstallStep[]> {\n const configuredInstall = this.config.scripts.install.trim();\n if (\n useConfiguredScript &&\n !looksLikeGeneratedInstallScript(configuredInstall)\n ) {\n // Trust pre-materialized trees: when BOTH roots carry the\n // install-completion sentinel AND their declared deps are physically\n // present, skip the custom script entirely. This is what lets the FTUE\n // demo pipeline pre-install offline from the bundled pnpm store and\n // have the devserver's FIRST start trust it, instead of re-running a\n // (networked, possibly npm-shaped) custom install over a\n // pnpm-materialized tree. The sentinel requirement is load-bearing:\n // merely-populated trees without a completed install (e.g. a root with\n // zero declared tempo deps) must still run the custom script, which may\n // do extra work beyond installing. `forceInstall` bypasses the\n // trust — a crash-retry must reinstall a suspect tree.\n if (\n !forceInstall &&\n (await this.projectRootInstallReady()) &&\n (await installCompletedForCurrentDeps(this.tempoRoot)) &&\n (await this.tempoInstallReady())\n ) {\n devLog(\"supervisor:custom_install_skipped_premat\", {\n projectRoot: this.projectRoot,\n tempoRoot: this.tempoRoot,\n });\n return [];\n }\n // The custom script is about to be spawned through the platform shell.\n // A PowerShell-shaped command cannot run under cmd.exe (or sh) — the\n // shell aborts while PARSING it — so refuse it here, before any spawn,\n // with the same typed error the runtime classifier would produce from\n // cmd's stderr. Checked only on this path: the generated commands below\n // are plain package-manager invocations by construction, and the\n // premat trust-skip above must keep working regardless of the\n // configured script. See TEM-436.\n const dialectIssue = findPowerShellSyntax(configuredInstall);\n if (dialectIssue) {\n throw new InstallShellDialectMismatchError(\n configuredInstall,\n describePowerShellRejection(dialectIssue),\n );\n }\n // MUST pass through the protected-root guard like the generated path\n // below. A custom install script can be anything — e.g. the Tempo\n // monorepo's own `tempo/tempo.config.json` declares\n // `cd .. && pnpm install --frozen-lockfile && ...`, which re-links the\n // entire workspace's node_modules. When CI resolves a devserver/canvas\n // spec to the runner's own checkout, running that here transiently\n // deletes the `playwright-core` symlink and crashes every Playwright\n // worker (`MODULE_NOT_FOUND: playwright-core/lib/utils`). See\n // protectedRootFor. No-op in production (env unset).\n return filterProtectedInstallSteps([\n {\n cwd: this.commandCwd,\n command: configuredInstall,\n label: \"custom tempo install\",\n // Record completion so `tempoInstallReady()` can trust the tree on\n // the next start instead of reinstalling every launch. No\n // `cleanBeforeInstall`: a custom script may re-link a shared\n // workspace (`cd .. && pnpm install`) that must not be wiped.\n markerRoots: [this.projectRoot, this.tempoRoot],\n externalLayoutMarkerRoots: [this.projectRoot, this.tempoRoot],\n },\n ]);\n }\n\n const separateRoots = this.projectRoot !== this.tempoRoot;\n const [rootManager, tempoManager] = await Promise.all([\n detectOwningPackageManager(this.projectRoot),\n separateRoots ? detectOwningPackageManager(this.tempoRoot) : null,\n ]);\n\n const steps: InstallStep[] = [];\n // Repair the app incrementally when incomplete. A routine canvas reload\n // should reuse a healthy app; a shared app/host root needs just one step.\n if (\n rootManager &&\n ((!separateRoots && forceInstall) ||\n !(await this.projectRootInstallReady()))\n ) {\n steps.push({\n cwd: this.projectRoot,\n command: await buildSafeInstallCommand(rootManager, this.projectRoot),\n label: \"project root install\",\n markerRoots: [this.projectRoot],\n });\n }\n\n if (tempoManager && (forceInstall || !(await this.tempoInstallReady()))) {\n steps.push({\n cwd: this.tempoRoot,\n command: await buildSafeInstallCommand(tempoManager, this.tempoRoot),\n label: \"tempo sidecar install\",\n // On a forced retry (e.g. a crash-retry after the devserver died) the\n // existing tree is suspect, so wipe it for a clean, complete install\n // rather than an incremental top-up over a possibly-partial tree. A\n // routine \"not ready\" (marker absent/stale, no force) does an ordinary\n // incremental install — cheap when already satisfied, and npm restores\n // any missing optional-native binary. See TEM-820.\n cleanBeforeInstall: forceInstall,\n // Record completion only after `npm install` exits 0 so an interrupted\n // install is never mistaken for a finished one on the next start.\n markerRoots: [this.tempoRoot],\n });\n }\n\n if (useConfiguredScript && !rootManager && !tempoManager) {\n steps.push({\n cwd: this.commandCwd,\n command: configuredInstall,\n label: \"fallback tempo install\",\n markerRoots: [this.projectRoot, this.tempoRoot],\n externalLayoutMarkerRoots: [this.projectRoot, this.tempoRoot],\n });\n }\n\n return filterProtectedInstallSteps(steps);\n }\n\n private async selectNodeRuntimeForStart(): Promise<void> {\n const packageJson = await readJsonFile<{\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n }>(path.join(this.tempoRoot, \"package.json\"));\n const viteSpec =\n packageJson?.dependencies?.vite ?? packageJson?.devDependencies?.vite;\n const viteMajor =\n typeof viteSpec === \"string\" ? parseFirstSemverMajor(viteSpec) : null;\n // Vite 7+ hosts have a hard Node floor (20.19+ / 22.12+). Other surfaces\n // only need *some* Node present (they fail later at install if absent).\n const requireFloor = viteMajor != null && viteMajor >= 7;\n\n // Probe the user's SYSTEM Node (no bundled shims on PATH) to decide whether\n // to fall back to the host's bundled toolchain. FALLBACK-ONLY: a Node that\n // works for this surface is never shadowed.\n const systemNodeVersion = await resolveManagedNodeVersion({\n skipBundledNode: true,\n });\n const floorOk =\n systemNodeVersion != null &&\n isNodeVersionCompatibleWithVite7(systemNodeVersion);\n\n const binDir = getBundledNodeBinDir();\n if (\n binDir &&\n shouldUseBundledNode({ systemNodeVersion, requireFloor, floorOk })\n ) {\n setBundledNodeActivated(true);\n devLog(\"supervisor:bundled_node_activated\", {\n tempoRoot: this.tempoRoot,\n systemNodeVersion,\n requireFloor,\n binDir,\n });\n }\n\n // After the machine-level fallback question is settled, reconcile the\n // PROJECT's declared Node (`engines.node` / `.nvmrc`) with what the\n // managed env resolves. A repo pinning a Node the default doesn't satisfy\n // makes every managed package-manager command fail (TEM-1536); the\n // override — a satisfying nvm/nodenv install, or the bundled Node —\n // applies to the host spawn here and to install/upgrade spawns via\n // runShellCommand's per-cwd resolution of the same memo.\n this.projectNodeOverride = await resolveProjectNodeOverrideForDir(\n this.tempoRoot,\n );\n\n // Selection ends here. Do not turn the prediction into a pre-spawn block:\n // a custom launcher may run in Docker or another environment whose Node is\n // unrelated to the host PATH. Managed commands classify observed Node\n // failures after spawn and get one bundled-toolchain retry instead.\n if (!requireFloor) {\n return;\n }\n\n // Selection only ever returns floor-compatible overrides, so this stays\n // an assert on the no-override path in practice.\n const effectiveNodeVersion =\n this.projectNodeOverride?.version ??\n (isBundledNodeActivated()\n ? await resolveManagedNodeVersion()\n : systemNodeVersion);\n if (\n !effectiveNodeVersion ||\n !isNodeVersionCompatibleWithVite7(effectiveNodeVersion)\n ) {\n devLog(\"supervisor:node_runtime_prediction_unmet\", {\n tempoRoot: this.tempoRoot,\n effectiveNodeVersion,\n requirement: \"Node.js 20.19+ or 22.12+\",\n });\n }\n }\n\n private async buildStartCommandForAttempt(\n attempt: number,\n ): Promise<ManagedStartPlan> {\n const configuredPort = portFromConfiguredTempoHostUrl(\n this.config.tempoHostUrl,\n );\n const needsLegacyPortInjection =\n !this.config.scripts.start.includes(\"${PORT}\") &&\n canInjectDevServerPort(this.config.scripts.start);\n const framework = needsLegacyPortInjection\n ? await this.resolveStartFramework()\n : \"vite\";\n // A literal configured URL pins every retry to its declared port. Every\n // other shape gets a fresh verified-free port per attempt, including\n // custom launchers: TEMPO_PORT is the universal contract that makes those\n // launchers portable without Tempo guessing their CLI dialect.\n const port = configuredPort ?? (await getAvailableLocalPort());\n const plan = buildManagedStartPlan({\n command: this.config.scripts.start,\n tempoHostUrl: this.config.tempoHostUrl,\n port,\n framework,\n });\n devLog(\"supervisor:start_port_selected\", {\n attempt,\n port,\n framework,\n pinnedByTempoHostUrl: configuredPort !== null,\n exportsPort: plan.env.PORT !== undefined,\n });\n return plan;\n }\n\n // Which dev-server flag dialect `scripts.start` should be rewritten with.\n // Next takes precedence (matches the scaffold's \"prefer Next when both\n // signals exist\" rule); Expo is detected next; everything else — Vite and\n // generic PM-wrapped dev scripts — uses the Vite dialect.\n private async resolveStartFramework(): Promise<DevServerStartFramework> {\n if (await this.isNextTempoHost()) {\n return \"next\";\n }\n if (await this.isExpoTempoHost()) {\n return \"expo\";\n }\n return \"vite\";\n }\n\n private async readExpectedHostIdentity(): Promise<ExpectedTempoHostIdentity | null> {\n const framework = await this.resolveStartFramework();\n if (framework === \"expo\") return null;\n const noncePath = path.join(this.tempoRoot, \".tempo\", \"workspace-nonce\");\n const workspaceNonce = (await readTextFileOptional(noncePath))?.trim();\n if (!workspaceNonce) {\n // Old projects can predate the nonce scaffold. Keep the pre-change\n // marker + project-root gate in that explicit compatibility lane, and\n // make the downgrade visible in diagnostics. tempo-project owns\n // generating/repairing this file; the runtime supervisor must not mint a\n // second identity behind its back.\n devLog(\"supervisor:workspace_nonce_unavailable\", {\n noncePath,\n tempoRoot: this.tempoRoot,\n });\n return null;\n }\n return {\n workspaceNonce,\n framework: framework === \"next\" ? \"nextjs\" : \"vite\",\n };\n }\n\n private async isExpoTempoHost(): Promise<boolean> {\n if (isExpoStartCommand(this.config.scripts.start)) {\n return true;\n }\n\n const packageJson = await readJsonFile<{\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n }>(path.join(this.tempoRoot, \"package.json\"));\n return Boolean(\n packageJson?.dependencies?.expo ?? packageJson?.devDependencies?.expo,\n );\n }\n\n /**\n * Validate that every dependency declared in the Tempo-owned\n * `tempo/package.json` is actually present in `tempo/node_modules/`. The\n * persisted `dependenciesInstalled: true` flag survives across app\n * launches and can outlast the actual install — e.g. when the user\n * deletes `tempo/node_modules/`, or when Tempo bumps a required dep\n * (PR adding `vite-tsconfig-paths`, `@tailwindcss/vite`, etc.) and the\n * project's previous install predates the bump.\n *\n * Returns `true` when the persisted flag can be trusted; `false` means\n * the supervisor should re-run `installDependenciesForStart` even if\n * `state.dependenciesInstalled === true`. Treats a missing/unparseable\n * `package.json` as \"nothing to verify\" → returns `true` so we don't\n * fight the user when they've intentionally non-standard a setup.\n *\n * TWO checks, both must pass:\n * 1. every declared top-level dep has a `node_modules/<name>` dir (cheap;\n * catches a manually-deleted tree or a not-yet-installed new dep), and\n * 2. the install-completion marker matches the current dependency inputs —\n * i.e. a prior `npm install` actually RAN TO COMPLETION for this\n * `package.json`/lockfile. This is what closes TEM-820: a slow install\n * interrupted after the declared deps landed but before an optional\n * native binary (`@rollup/rollup-<platform>`) was fetched leaves check 1\n * passing but check 2 failing, so the partial tree is reinstalled instead\n * of trusted and later crashing Vite at startup.\n */\n private async tempoInstallReady(): Promise<boolean> {\n const packageJsonPath = path.join(this.tempoRoot, \"package.json\");\n let raw: string;\n try {\n raw = await readFile(packageJsonPath, \"utf-8\");\n } catch {\n // No `tempo/package.json` at all — nothing for the supervisor to\n // verify. The caller's `dependenciesInstalled` decision stands.\n return true;\n }\n let parsed: { dependencies?: unknown; devDependencies?: unknown };\n try {\n parsed = JSON.parse(raw) as {\n dependencies?: unknown;\n devDependencies?: unknown;\n };\n } catch {\n return true;\n }\n const declared = new Set<string>();\n for (const block of [parsed.dependencies, parsed.devDependencies]) {\n if (!block || typeof block !== \"object\" || Array.isArray(block)) continue;\n for (const name of Object.keys(block as Record<string, unknown>)) {\n if (name.length > 0) declared.add(name);\n }\n }\n if (declared.size === 0) return true;\n // Only a successful custom install can vouch for dependencies behind a\n // container/devcontainer boundary. A normal completion receipt with no\n // remaining package directories is a damaged local tree, not that proof.\n if (await externalLayoutInstallCompleted(this.tempoRoot)) {\n return true;\n }\n if (await hasPnpLoader(this.tempoRoot)) {\n return await installCompletedForCurrentDeps(this.tempoRoot);\n }\n for (const name of declared) {\n const depPath = path.join(this.tempoRoot, \"node_modules\", name);\n try {\n await access(depPath);\n } catch {\n return false;\n }\n }\n // Declared deps are all present — but that can't see a missing transitive\n // optional-native binary. Require a matching completion marker too.\n return await installCompletedForCurrentDeps(this.tempoRoot);\n }\n\n private async projectRootInstallReady(): Promise<boolean> {\n // A partial install can contain every direct dependency while peers or\n // transitives remain empty stubs. Check the durable receipt first; only\n // completed installs need the shallow physical-dependency check.\n return (\n (await installCompletedForCurrentDeps(this.projectRoot)) &&\n (await this.projectRootDepsPresentOrNotRequired())\n );\n }\n\n private async projectRootDepsPresentOrNotRequired(\n acceptExternalLayoutProof = true,\n ): Promise<boolean> {\n const rootPackageJson = path.join(this.projectRoot, \"package.json\");\n const packageJson = await readJsonFile<{\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n }>(rootPackageJson);\n if (!packageJson) {\n // No root package.json — nothing for the user-side to install. The\n // canvas-local components will resolve out of the Tempo-side\n // node_modules. Not a misconfiguration.\n return true;\n }\n const declared = new Set([\n ...Object.keys(packageJson.dependencies ?? {}),\n ...Object.keys(packageJson.devDependencies ?? {}),\n ]);\n if (declared.size === 0) return true;\n // Yarn Plug'n'Play intentionally has no node_modules tree. A completed\n // custom install may likewise materialize dependencies only inside a\n // container or devcontainer. In both cases the successful completion\n // marker is the host-side proof; repeatedly requiring physical package\n // directories would rerun the install on every canvas start forever.\n if (\n (await hasPnpLoader(this.projectRoot)) ||\n (acceptExternalLayoutProof &&\n (await externalLayoutInstallCompleted(this.projectRoot)))\n ) {\n return true;\n }\n for (const name of declared) {\n try {\n await access(path.join(this.projectRoot, \"node_modules\", name));\n } catch {\n return false;\n }\n }\n return true;\n }\n\n private async selectNextDistDir(): Promise<void> {\n await this.releaseNextDistDirClaim();\n this.nextDistDir = null;\n if (!(await this.isNextTempoHost())) return;\n\n this.nextDistDir = TEMPO_NEXT_STABLE_DIST_DIR;\n const recentPostReadyConflict = [...this.state.recentExits]\n .reverse()\n .find(\n (entry) =>\n entry.afterReady === true &&\n Date.now() - entry.at <= TEMPO_NEXT_POST_READY_CONFLICT_MAX_AGE_MS &&\n parseDevServerLockConflict(entry.outputTail) !== null,\n );\n if (recentPostReadyConflict) {\n this.nextDistDir = await this.selectSuffixedNextDistDir();\n devLog(\"supervisor:next_dist_dir_suffixed\", {\n distDir: this.nextDistDir,\n reason: \"fresh-post-ready-lock-exit\",\n exitAt: recentPostReadyConflict.at,\n tempoRoot: this.tempoRoot,\n });\n }\n }\n\n private nextDistClaimsDir(): string {\n return path.join(this.tempoRoot, \".tempo\", TEMPO_NEXT_CLAIMS_DIR);\n }\n\n private nextDistCachePath(distDir: string): string {\n return path.resolve(this.commandCwd, distDir);\n }\n\n private nextDistClaimPath(distDir: string): string {\n return path.join(\n this.nextDistClaimsDir(),\n `${path.basename(distDir)}.json`,\n );\n }\n\n private async tryClaimNextDistDir(distDir: string): Promise<boolean> {\n const cachePath = this.nextDistCachePath(distDir);\n const claimPath = this.nextDistClaimPath(distDir);\n await mkdir(cachePath, { recursive: true });\n await writeFile(path.join(cachePath, TEMPO_NEXT_CACHE_MARKER), \"1\\n\", {\n flag: \"a\",\n });\n await mkdir(path.dirname(claimPath), { recursive: true });\n const token = randomUUID();\n const processStartedAt = await readProcessStartedAt(process.pid);\n const claimText = `${JSON.stringify({\n token,\n managerPid: process.pid,\n processStartedAt,\n claimedAt: Date.now(),\n distDir,\n })}\\n`;\n\n for (let attempt = 0; attempt < 2; attempt += 1) {\n try {\n await writeFile(claimPath, claimText, {\n encoding: \"utf-8\",\n flag: \"wx\",\n });\n this.nextDistDirClaim = { distDir, claimPath, token };\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const existing = await readJsonFile<{\n managerPid?: unknown;\n processStartedAt?: unknown;\n }>(claimPath);\n const managerPid =\n typeof existing?.managerPid === \"number\" ? existing.managerPid : null;\n const expectedStartedAt =\n typeof existing?.processStartedAt === \"string\"\n ? existing.processStartedAt\n : null;\n const managerAlive = managerPid != null && isPidAlive(managerPid);\n const liveStartedAt = managerAlive\n ? await readProcessStartedAt(managerPid)\n : null;\n const claimIsLive =\n managerAlive &&\n (expectedStartedAt == null || liveStartedAt === expectedStartedAt);\n if (claimIsLive) return false;\n await rm(claimPath, { force: true });\n }\n }\n return false;\n }\n\n private async selectSuffixedNextDistDir(): Promise<string> {\n await this.releaseNextDistDirClaim();\n const nextRoot = path.join(this.commandCwd, \".next\");\n const existing = await readdir(nextRoot, { withFileTypes: true }).catch(\n () => [],\n );\n const reusable: Array<{ distDir: string; mtimeMs: number }> = [];\n for (const entry of existing) {\n if (!entry.isDirectory() || !/^tempo-[a-z0-9]+$/i.test(entry.name)) {\n continue;\n }\n const cachePath = path.join(nextRoot, entry.name);\n try {\n await access(path.join(cachePath, TEMPO_NEXT_CACHE_MARKER));\n const cacheStat = await stat(cachePath);\n reusable.push({\n distDir: `.next/${entry.name}`,\n mtimeMs: cacheStat.mtimeMs,\n });\n } catch {}\n }\n reusable.sort((a, b) => b.mtimeMs - a.mtimeMs);\n for (const candidate of reusable) {\n if (await this.tryClaimNextDistDir(candidate.distDir)) {\n await this.sweepNextDistDirs();\n return candidate.distDir;\n }\n }\n while (true) {\n const candidate = createSuffixedTempoNextDistDir();\n if (await this.tryClaimNextDistDir(candidate)) {\n await this.sweepNextDistDirs();\n return candidate;\n }\n }\n }\n\n private async releaseNextDistDirClaim(): Promise<void> {\n const claim = this.nextDistDirClaim;\n this.nextDistDirClaim = null;\n if (!claim) return;\n const persisted = await readJsonFile<{ token?: unknown }>(claim.claimPath);\n if (persisted?.token === claim.token) {\n await rm(claim.claimPath, { force: true }).catch(() => {});\n }\n await this.sweepNextDistDirs();\n }\n\n private async sweepNextDistDirs(): Promise<void> {\n const nextRoot = path.join(this.commandCwd, \".next\");\n const entries = await readdir(nextRoot, { withFileTypes: true }).catch(\n () => [],\n );\n const now = Date.now();\n const unclaimedManaged: Array<{ path: string; mtimeMs: number }> = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const cachePath = path.join(nextRoot, entry.name);\n if (/^tempo-sidestep-/i.test(entry.name)) {\n const cacheStat = await stat(cachePath).catch(() => null);\n if (\n cacheStat &&\n now - cacheStat.mtimeMs > TEMPO_NEXT_LEGACY_CACHE_MAX_AGE_MS\n ) {\n await rm(cachePath, { recursive: true, force: true }).catch(() => {});\n }\n continue;\n }\n if (!/^tempo-[a-z0-9]+$/i.test(entry.name)) continue;\n try {\n await access(path.join(cachePath, TEMPO_NEXT_CACHE_MARKER));\n } catch {\n // Builds created by the pre-claim implementation have no marker. They\n // are never reused, but can still accumulate without bound, so remove\n // only clearly stale instances from that legacy lane.\n const cacheStat = await stat(cachePath).catch(() => null);\n if (\n cacheStat &&\n now - cacheStat.mtimeMs > TEMPO_NEXT_LEGACY_CACHE_MAX_AGE_MS\n ) {\n await rm(cachePath, { recursive: true, force: true }).catch(() => {});\n }\n continue;\n }\n try {\n await access(this.nextDistClaimPath(`.next/${entry.name}`));\n continue;\n } catch {}\n const cacheStat = await stat(cachePath).catch(() => null);\n if (cacheStat) {\n unclaimedManaged.push({ path: cachePath, mtimeMs: cacheStat.mtimeMs });\n }\n }\n unclaimedManaged.sort((a, b) => b.mtimeMs - a.mtimeMs);\n await Promise.all(\n unclaimedManaged\n .slice(TEMPO_NEXT_UNCLAIMED_CACHE_LIMIT)\n .map((entry) =>\n rm(entry.path, { recursive: true, force: true }).catch(() => {}),\n ),\n );\n }\n\n private async ensureDevserverWatchdogScript(): Promise<string | null> {\n // Enabled on every supported local platform. Detached POSIX process groups\n // are just as capable of surviving a hard Electron death as Windows child\n // trees; the pipe EOF is the only cleanup signal a SIGKILL cannot skip.\n // Tests or constrained hosts may explicitly opt out with `=0`.\n const enabled = process.env.TEMPO_DEVSERVER_WATCHDOG !== \"0\";\n if (!enabled) return null;\n const watchdogPath = path.join(\n this.tempoRoot,\n \".tempo\",\n DEVSERVER_WATCHDOG_FILE_NAME,\n );\n try {\n await mkdir(path.dirname(watchdogPath), { recursive: true });\n await atomicWriteText(watchdogPath, DEVSERVER_WATCHDOG_SOURCE);\n return watchdogPath;\n } catch {\n devLog(\"supervisor:watchdog_write_failed\", { watchdogPath });\n return null;\n }\n }\n\n private async isNextTempoHost(): Promise<boolean> {\n if (/\\bnext(?:\\s+dev|\\s|$)/.test(this.config.scripts.start)) {\n return true;\n }\n\n const nextConfigPaths = [\n \"next.config.js\",\n \"next.config.mjs\",\n \"next.config.cjs\",\n \"next.config.ts\",\n ];\n for (const configFile of nextConfigPaths) {\n try {\n await access(path.join(this.tempoRoot, configFile));\n return true;\n } catch {}\n }\n\n const packageJson = await readJsonFile<{\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n }>(path.join(this.tempoRoot, \"package.json\"));\n return Boolean(\n packageJson?.dependencies?.next ?? packageJson?.devDependencies?.next,\n );\n }\n\n private async writeProcessMarker(\n pid: number | null,\n workerPids: number[] = [],\n ): Promise<void> {\n if (!pid || !(await this.isNextTempoHost())) {\n return;\n }\n\n await mkdir(path.dirname(this.processMarkerPath), { recursive: true });\n await writeFile(\n this.processMarkerPath,\n `${JSON.stringify(\n {\n framework: \"nextjs\",\n owner: \"tempo\",\n pid,\n // The real `next dev` worker PIDs (descendants of the detached shell\n // `pid`). Persisted so a LATER session can reap them directly even\n // after the shell died and the worker reparented to init — the orphan\n // that survives an app close/rebuild and collides with the next start.\n workerPids,\n cwd: this.commandCwd,\n command: this.config.scripts.start,\n startedAt: new Date().toISOString(),\n },\n null,\n 2,\n )}\\n`,\n \"utf-8\",\n );\n }\n\n private async removeProcessMarkerForPid(pid: number | null): Promise<void> {\n if (!pid) {\n return;\n }\n\n const marker = await readTempoProcessMarker(this.processMarkerPath);\n if (!marker || marker.pid === pid) {\n await rm(this.processMarkerPath, { force: true });\n }\n }\n\n // Demand-driven host registration: a canvas is added to tempo-host.tsx only\n // after the renderer/main process explicitly asks to load that canvas. This\n // keeps opening one canvas from importing every other canvas in the project.\n async ensureCanvasRegistered(canvasFile: string): Promise<void> {\n const result = await this.registry.ensureReactCanvasHostEntry(canvasFile);\n if (!result) return;\n await this.enqueueReconcile(\"ensure-canvas-registered\", false);\n }\n\n async listRegisteredCanvases(): Promise<string[]> {\n const files = await scanCanvasFiles(this.pagesDir);\n return files.map((absolutePath) =>\n toPosixPath(path.relative(this.projectRoot, absolutePath)),\n );\n }\n\n /**\n * The demand-registered canvas set backing the generated tempo-host.tsx.\n * Diagnostics-only: lets a bug-report bundle show whether the active canvas\n * is actually registered in the host (TEM-919 was invisible without this).\n */\n async getRequestedCanvasPaths(): Promise<string[]> {\n return await this.registry.getRequestedCanvasPaths();\n }\n\n async dispose(): Promise<void> {\n this.disposed = true;\n this.registry.dispose();\n this.stopUrlHealthProbe();\n await this.stopManagedFileWatchers();\n await this.releaseNextDistDirClaim();\n if (this.repairDebounceTimer !== null) {\n clearTimeout(this.repairDebounceTimer);\n this.repairDebounceTimer = null;\n }\n if (this.reconcileRetryTimer !== null) {\n clearTimeout(this.reconcileRetryTimer);\n this.reconcileRetryTimer = null;\n }\n if (this.convergenceRetryTimer !== null) {\n clearTimeout(this.convergenceRetryTimer);\n this.convergenceRetryTimer = null;\n }\n // Reject any pending reconcile queue items so they don't run after dispose.\n for (const item of this.reconcileQueue.splice(0)) {\n item.reject(new DisposedError());\n }\n }\n\n // Periodic HTTP probe that re-asserts the dev server URL is reachable.\n // Foreground starts clear the URL through their child exit; daemonized\n // starts have no live child after launch, so three consecutive failures are\n // their terminal signal. A failed probe against a child we still hold alive\n // never demotes — it only records urlUnreachableSince.\n private startUrlHealthProbe(url: string): void {\n this.stopUrlHealthProbe();\n if (this.disposed) return;\n this.urlHealthProbeUrl = url;\n this.urlHealthProbeConsecutiveFailures = 0;\n this.urlHealthProbeGeneration += 1;\n this.urlHealthProbeTimer = setInterval(() => {\n void this.runUrlHealthProbeTick();\n }, TempoDevServer.URL_HEALTH_PROBE_INTERVAL_MS);\n if (typeof this.urlHealthProbeTimer.unref === \"function\") {\n this.urlHealthProbeTimer.unref();\n }\n }\n\n private stopUrlHealthProbe(): void {\n if (this.urlHealthProbeTimer !== null) {\n clearInterval(this.urlHealthProbeTimer);\n this.urlHealthProbeTimer = null;\n }\n this.urlHealthProbeUrl = null;\n this.urlHealthProbeConsecutiveFailures = 0;\n this.urlHealthProbeGeneration += 1;\n }\n\n private async runUrlHealthProbeTick(): Promise<void> {\n const probedUrl = this.urlHealthProbeUrl;\n const generation = this.urlHealthProbeGeneration;\n if (!probedUrl || this.disposed) return;\n const reachable = await probeUrlOnce(\n probedUrl,\n 1_500,\n this.projectRoot,\n this.activeHostIdentity,\n );\n // Bail if the probe loop was stopped or restarted while this probe was in\n // flight (the server restarted under us — possibly on the SAME URL): the\n // new loop starts from a clean streak and this result belongs to the old\n // process.\n if (this.urlHealthProbeGeneration !== generation) return;\n if (reachable) {\n this.state.urlUnreachableSince = null;\n this.urlHealthProbeConsecutiveFailures = 0;\n // A host that answered again is no longer unresponsive; withdraw the\n // hint the failed streak recorded so the renderer stops blaming a\n // healthy host for whatever it is still waiting on.\n this.activeHostProcess?.clearSupervisorHint(\"host-unresponsive\");\n return;\n }\n // A failed probe alone never demotes a host we still hold alive: one\n // missed 1.5s window (event-loop starvation under load) is weak evidence,\n // and the child's exit event — not HTTP reachability — is the authority\n // on death. The probe's job is only the deaths that leave no live handle\n // (crashed previous app instance, lost exit event → isAlive() false).\n // Degraded-but-alive stays visible via urlUnreachableSince — and, once\n // the streak is long enough to rule out a load blip, as a hint on the\n // process so the renderer can say WHY the canvas is stuck instead of\n // showing \"Starting render host window\" until its 3-minute watchdog\n // (diagnostic t175n51yhjbemx3dxvf2a7n4j18dq9dy: Vite alive, `/` and\n // `/tempo-host` timing out for minutes, no message anywhere).\n if (this.activeHostProcess?.isAlive()) {\n this.state.urlUnreachableSince ??= Date.now();\n this.urlHealthProbeConsecutiveFailures += 1;\n devLog(\"supervisor:url_probe_failed_host_alive\", {\n url: probedUrl,\n consecutiveFailures: this.urlHealthProbeConsecutiveFailures,\n unreachableSince: this.state.urlUnreachableSince,\n });\n if (\n this.urlHealthProbeConsecutiveFailures ===\n TempoDevServer.URL_HEALTH_FAILURES_TO_DEMOTE\n ) {\n this.activeHostProcess.recordSupervisorHint(\n buildHostUnresponsiveHint(probedUrl, this.state.urlUnreachableSince),\n );\n }\n return;\n }\n const probeBacked = this.activeHostLivenessSource() === \"probe\";\n if (probeBacked) {\n this.urlHealthProbeConsecutiveFailures += 1;\n this.state.urlUnreachableSince ??= Date.now();\n devLog(\"supervisor:url_probe_failed_probe_backed\", {\n url: probedUrl,\n consecutiveFailures: this.urlHealthProbeConsecutiveFailures,\n requiredFailures: TempoDevServer.URL_HEALTH_FAILURES_TO_DEMOTE,\n unreachableSince: this.state.urlUnreachableSince,\n });\n if (\n this.urlHealthProbeConsecutiveFailures <\n TempoDevServer.URL_HEALTH_FAILURES_TO_DEMOTE\n ) {\n return;\n }\n const activeProcess = this.activeHostProcess;\n activeProcess?.reportProbeBackedDeath();\n // The process exit listener normally clears URL/state synchronously and\n // notifies the adapter. Keep the state transition authoritative even if\n // no consumer subscribed to the handle.\n if (this.state.devserverUrl === probedUrl) {\n this.state.devserverUrl = null;\n this.state.urlUnreachableSince = null;\n this.idleProcessStatus = \"exited\";\n if (this.activeHostProcess === activeProcess) {\n this.activeHostProcess = null;\n }\n await this.persistStatus().catch(() => {});\n }\n this.stopUrlHealthProbe();\n return;\n }\n // A child-backed host whose process is gone but whose `exit` event never\n // arrived (lost SIGCHLD): deliver the terminal exit ourselves so the\n // adapter and pool release the host. The exit listener normally clears\n // this state; keep the transition authoritative even if none subscribed.\n this.activeHostProcess?.reportObservedChildDeath();\n this.state.devserverUrl = null;\n this.state.urlUnreachableSince = null;\n this.idleProcessStatus = \"exited\";\n this.stopUrlHealthProbe();\n await this.persistStatus().catch(() => {});\n }\n\n private async performInitialize(): Promise<DevServerInitResult> {\n this.state.setupStatus = \"initializing\";\n const t = devTimer(\"supervisor:initialize\", {\n projectRoot: this.projectRoot,\n tempoRoot: this.tempoRoot,\n pagesDir: this.pagesDir,\n });\n\n try {\n await mkdir(this.tempoRoot, { recursive: true });\n const { staleDevserverUrl, staleDevserverOwner } =\n await this.hydrateStatusFromDisk();\n // Reconcile the persisted devserver URL with reality (purge an\n // unmanaged server this app instance started; scrub dead or foreign\n // entries from disk) — never resurrect it into live state (restarts\n // are fresh starts).\n const persistedDevserverReconciliation =\n await this.reconcilePersistedDevserverUrl(\n staleDevserverUrl,\n staleDevserverOwner,\n );\n // Load prior host exits (incl. one from a since-disposed supervisor that\n // crashed and was restarted) so diagnostics survive `restartCanvas`.\n await this.loadRecentExitsFromDisk();\n\n // A force=true reconcile can re-touch managed host files. If the dev\n // server is ALREADY running and serving, those atomic writes fire the\n // `.tempo/` watcher → an HMR/Turbopack recompile mid-serve, which on Next\n // 16 Turbopack can crash the server (exit 1). A re-initialize of a LIVE\n // host must therefore use force=false: drifted files are still repaired\n // (content compare), but unchanged ones are left untouched so the watcher\n // stays quiet. First bringup (no live process) keeps force=true so\n // missing/half-written files are guaranteed to exist before the host\n // spawns. Liveness is read from the owned child handle — a booting\n // (\"starting\") host counts as live too, since forced writes mid-boot\n // carry the same HMR-crash risk as mid-serve.\n const firstInitialize = !this.state.initialized;\n const hostAlive = this.activeHostHasLivenessAuthority();\n if (firstInitialize && !hostAlive) {\n await this.registry.resetStaleRequestedCanvases();\n }\n const reconcileResult = await this.enqueueReconcile(\n \"initialize\",\n !hostAlive,\n );\n this.state.initialized = true;\n this.state.setupStatus = \"ready\";\n await this.startManagedFileWatchers();\n t.done({\n ok: true,\n changedFilesCount: reconcileResult.changedFiles.length,\n generation: reconcileResult.generation,\n });\n return {\n changedFiles: reconcileResult.changedFiles,\n generation: reconcileResult.generation,\n initialized: true,\n persistedDevserverReconciliation,\n };\n } catch (error) {\n this.state.setupStatus = \"error\";\n this.state.lastError =\n error instanceof Error ? error.message : String(error);\n t.done({\n ok: false,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n // ── repair() — the closure operator over the state space (§11.4) ──────────\n // Restores every hard invariant the module owns. Idempotent: calling it\n // twice in a row reaches the same state after one walk and is a no-op on\n // the second. Called from: file watchers (debounced), the convergence\n // check after each reconcile drain, and external repair triggers.\n async repair(): Promise<RepairReport> {\n if (this.disposed) {\n throw new DisposedError();\n }\n\n const report: RepairReport = {\n managedRepaired: [],\n urlInvalidated: false,\n generation: this.state.generation,\n };\n\n // I-1: config file still present. Re-resolving the parsed config lives\n // upstream in tempo-project today (it owns disk → ResolvedTempoConfig\n // translation). Here we just guard against deletion at the path that\n // was used to construct us.\n await this.assertConfigFilePresent();\n\n // I-2 + I-3: every generated runtime file (.tempo/*) is checked and any\n // drifted file is rewritten in one pass. force=false matters here: repair() is the\n // closure operator invoked from the .tempo/ watcher, and our own\n // atomicWriteText calls trigger that watcher. Writing with force=true\n // would re-touch every managed file on every repair tick — Vite's HMR\n // sees touched generated files and refreshes the iframe — creating a\n // self-perpetuating loop where every watcher fire causes more watcher\n // fires. force=false skips writes when current === next, so the loop\n // terminates after one pass.\n const reconcileResult = await this.enqueueReconcile(\"repair\", false);\n report.managedRepaired.push(...reconcileResult.changedFiles);\n report.generation = reconcileResult.generation;\n\n // I-4: URL still reachable. Same corroboration rule as the periodic\n // probe: a failed probe against a child we still hold alive is weak\n // evidence and never demotes — only a probe failure with no live handle\n // invalidates the URL.\n if (this.state.devserverUrl) {\n const reachable = await probeUrlOnce(\n this.state.devserverUrl,\n 1_500,\n this.projectRoot,\n this.activeHostIdentity,\n );\n const probeBacked = this.activeHostLivenessSource() === \"probe\";\n if (!reachable && !this.activeHostProcess?.isAlive() && !probeBacked) {\n this.state.devserverUrl = null;\n this.state.urlUnreachableSince = null;\n this.idleProcessStatus = \"exited\";\n this.stopUrlHealthProbe();\n report.urlInvalidated = true;\n await this.persistStatus().catch(() => {});\n }\n }\n\n return report;\n }\n\n // Verify tempo.config.json exists at its canonical path\n // (`<projectRoot>/tempo/tempo.config.json`). Mirrors the discovery logic\n // in `tempo-project/config.ts`; deliberately duplicated here to avoid an\n // upward dependency (tempo-project → tempo-devserver remains the only\n // direction). If config resolution moves into tempo-devserver in a\n // later increment, this duplication collapses.\n private async assertConfigFilePresent(): Promise<void> {\n try {\n await access(this.config.tempoConfigPath);\n return;\n } catch {\n throw new ConfigMissingError(this.projectRoot);\n }\n }\n\n // ── Managed file watchers (§11.6) ─────────────────────────────────────────\n // Watch projectRoot recursively and filter to the paths the devserver owns.\n // This catches .tempo, tempo.config.json, and newly-created canvas files even\n // when their parent directories did not exist during initialize.\n //\n // Relevant events route to the same debounced repair path. The registry\n // reconcile inside repair handles both generated-file drift and canvas-file\n // add/delete drift.\n private async startManagedFileWatchers(): Promise<void> {\n if (this.disposed) return;\n await this.stopManagedFileWatchers();\n if (this.disposed) return;\n\n try {\n const subscription = await watch(\n this.projectRoot,\n (events) => {\n if (this.disposed) return;\n if (!events.some((event) => this.isManagedWatcherEvent(event))) {\n return;\n }\n this.scheduleManagedRepair();\n },\n {\n ...MANAGED_WATCH_OPTIONS,\n // Transport interruption: the coverage gap may have missed a\n // managed-file change. repair() re-derives everything from disk,\n // so one scheduled repair reconciles any drift fully.\n onInterruption: () => this.scheduleManagedRepair(),\n },\n );\n this.managedFileWatchers.push(subscription);\n } catch (error) {\n devLog(\"managed-file-watcher:start_failed\", {\n root: this.projectRoot,\n message: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n private async stopManagedFileWatchers(): Promise<void> {\n if (this.managedFileWatcherStopPromise) {\n await this.managedFileWatcherStopPromise;\n return;\n }\n const watchers = this.managedFileWatchers.splice(0);\n this.managedFileWatcherStopPromise = Promise.all(\n watchers.map((watcher) =>\n watcher.unsubscribe().catch((error) => {\n devLog(\"managed-file-watcher:unsubscribe_failed\", {\n message: error instanceof Error ? error.message : String(error),\n });\n }),\n ),\n ).then(() => undefined);\n try {\n await this.managedFileWatcherStopPromise;\n } finally {\n this.managedFileWatcherStopPromise = null;\n }\n }\n\n private isManagedWatcherEvent(event: FileEvent): boolean {\n return isManagedDevServerPath(event.relativePath, {\n projectRoot: this.projectRoot,\n tempoRoot: this.tempoRoot,\n tempoConfigPath: this.config.tempoConfigPath,\n pagesDir: this.pagesDir,\n });\n }\n\n private scheduleManagedRepair(): void {\n if (this.disposed) return;\n if (this.repairDebounceTimer !== null) {\n clearTimeout(this.repairDebounceTimer);\n }\n this.repairDebounceTimer = setTimeout(() => {\n this.repairDebounceTimer = null;\n void this.repair().catch((error) => {\n this.state.lastError =\n error instanceof Error ? error.message : String(error);\n });\n }, TempoDevServer.REPAIR_DEBOUNCE_MS);\n if (typeof this.repairDebounceTimer.unref === \"function\") {\n this.repairDebounceTimer.unref();\n }\n }\n\n private enqueueReconcile(\n reason: string,\n force: boolean,\n ): Promise<DevServerReconcileResult> {\n if (this.disposed) {\n return Promise.reject(new DisposedError());\n }\n devLog(\"reconcile:enqueued\", {\n reason,\n force,\n queueDepthBefore: this.reconcileQueue.length,\n workerRunning: this.reconcileWorkerRunning,\n generation: this.state.generation,\n });\n return new Promise<DevServerReconcileResult>((resolve, reject) => {\n this.reconcileQueue.push({\n reason,\n force,\n resolve,\n reject,\n });\n void this.processReconcileQueue();\n });\n }\n\n private async processReconcileQueue(): Promise<void> {\n if (this.reconcileWorkerRunning || this.disposed) {\n return;\n }\n\n this.reconcileWorkerRunning = true;\n\n while (this.reconcileQueue.length > 0 && !this.disposed) {\n // Drain all pending items so one performReconcile handles the current\n // explicitly requested canvas set.\n const batch = this.reconcileQueue.splice(0);\n const force = batch.some((item) => item.force);\n\n try {\n const result = await this.performReconcile(batch[0].reason, force);\n this.reconcileRetryCount = 0;\n for (const item of batch) {\n item.resolve(result);\n }\n } catch (error) {\n this.state.reconcileStatus = \"error\";\n this.state.lastError =\n error instanceof Error ? error.message : String(error);\n for (const item of batch) {\n item.reject(error);\n }\n this.scheduleRecoveryReconcile();\n }\n }\n\n if (this.state.initialized) {\n this.scheduleConvergenceCheck();\n }\n this.reconcileWorkerRunning = false;\n }\n\n // When a reconcile fails, schedule a retry with exponential backoff.\n // The retry enqueues a new reconcile which re-scans the on-disk state,\n // converging the generated files with the source of truth even after\n // transient I/O failures.\n private scheduleRecoveryReconcile(): void {\n if (this.disposed || this.reconcileRetryTimer !== null) {\n return;\n }\n\n if (\n this.reconcileRetryCount >= TempoDevServer.RECONCILE_RETRY_DELAYS.length\n ) {\n console.error(\n `[TempoDevServer] Reconcile failed after ${this.reconcileRetryCount} retries`,\n );\n return;\n }\n\n const delay =\n TempoDevServer.RECONCILE_RETRY_DELAYS[this.reconcileRetryCount];\n this.reconcileRetryCount += 1;\n this.reconcileRetryTimer = setTimeout(() => {\n this.reconcileRetryTimer = null;\n // The retry's reject reaches scheduleRecoveryReconcile via the\n // worker loop's catch — there's no awaiter here to handle it, so\n // attaching a no-op handler keeps the rejection from leaking as\n // an unhandled-promise warning during test teardown.\n void this.enqueueReconcile(\"recovery-retry\", false).catch(() => {});\n }, delay);\n }\n\n // After the queue drains, verify every managed file matches its expected\n // function-of-state. Catches drift from claims added during the final\n // reconcile, external file modifications, or previous write failures.\n // Section 11.6: extended from \"registry only\" to \"all managed files.\"\n private scheduleConvergenceCheck(): void {\n if (this.disposed) return;\n // A backoff delay is already armed — the pending pass will re-check.\n if (this.convergenceRetryTimer !== null) return;\n void (async () => {\n try {\n if (this.disposed) return;\n if (await this.hasManagedFileDrift()) {\n if (this.disposed) return;\n const delay =\n TempoDevServer.CONVERGENCE_BACKOFF_DELAYS[\n Math.min(\n this.convergenceDriftStreak,\n TempoDevServer.CONVERGENCE_BACKOFF_DELAYS.length - 1,\n )\n ];\n this.convergenceDriftStreak += 1;\n // The drift-correction retry will hit the same I/O if the\n // workspace is being torn down (test cleanup, branch switch\n // mid-reconcile, etc.) — swallow the rejection so it doesn't\n // surface as an unhandled-promise warning. Failures are still\n // recorded in state.lastError by the worker's catch path.\n // force=false on purpose: drift is already confirmed, and a forced\n // pass would re-touch every managed file and bypass + reset the\n // thrash breaker.\n const enqueueCorrection = () => {\n this.convergenceRetryTimer = null;\n if (this.disposed) return;\n void this.enqueueReconcile(\"drift-correction\", false)\n .then((result) => {\n // The pass actually restored something — genuine one-shot\n // drift, not a stuck state. Future corrections are prompt\n // again.\n if (result.changedFiles.length > 0) {\n this.convergenceDriftStreak = 0;\n }\n })\n .catch(() => {});\n };\n if (delay === 0) {\n enqueueCorrection();\n } else {\n this.convergenceRetryTimer = setTimeout(enqueueCorrection, delay);\n }\n } else {\n this.convergenceDriftStreak = 0;\n }\n } catch {\n // Non-critical — next claim change will trigger a reconcile.\n }\n })();\n }\n\n // Inspect every managed file the module owns. Returns true if any one is\n // missing or has content other than what we'd write right now. Read-only\n // — pairs with the drift-correction reconcile (a force=false\n // compare-and-write pass) which actually restores drift.\n private async hasManagedFileDrift(): Promise<boolean> {\n const tempoDir = path.join(this.tempoRoot, \".tempo\");\n const reactCanvasEntries =\n await this.registry.collectReactCanvasHostEntries();\n const startFramework = await this.resolveStartFramework();\n const wrapperPath = path.join(tempoDir, TEMPO_HOST_FILE_NAME);\n const expectedWrapper = buildTempoHostWrapper(reactCanvasEntries, {\n startFramework,\n });\n if ((await readTextFileOptional(wrapperPath)) !== expectedWrapper) {\n return true;\n }\n\n const recoveryPath = path.join(tempoDir, CANVAS_HOST_RECOVERY_FILE_NAME);\n const expectedRecovery = canvasHostRecoverySourceFor(startFramework);\n if ((await readTextFileOptional(recoveryPath)) !== expectedRecovery) {\n return true;\n }\n\n for (const staleFileName of STALE_MANAGED_CANVAS_FILES) {\n if (\n (await readTextFileOptional(path.join(tempoDir, staleFileName))) !==\n null\n ) {\n return true;\n }\n }\n\n return false;\n }\n\n private async performReconcile(\n reason: string,\n force: boolean,\n ): Promise<DevServerReconcileResult> {\n this.state.reconcileStatus = \"reconciling\";\n const timer = devTimer(\"reconcile\", {\n reason,\n force,\n generationBefore: this.state.generation,\n });\n\n const changedFiles: string[] = [];\n try {\n changedFiles.push(...(await this.reconcileGeneratedFiles(force)));\n\n // Delta-driven on purpose: `force` alone must never invalidate a\n // converged system. Writes are already content-gated even under force,\n // so a forced pass that changed nothing is a true no-op — bumping the\n // generation for it would signal a regeneration that never happened.\n const generationBumped = changedFiles.length > 0;\n if (generationBumped) {\n this.state.generation += 1;\n }\n\n this.state.reconcileStatus = \"idle\";\n // Basenames only — full absolute paths are noise in the bug-report\n // bundle and would leak the user's home-dir layout.\n const changedBasenames = changedFiles.map((p) => path.basename(p));\n this.recordReconcileActivity({\n at: Date.now(),\n reason,\n force,\n generationBumped,\n changedFiles: changedBasenames,\n generation: this.state.generation,\n });\n timer.done({\n outcome: \"ok\",\n generation: this.state.generation,\n generationBumped,\n changedFileCount: changedFiles.length,\n changedFiles: changedBasenames,\n });\n return {\n changedFiles,\n generation: this.state.generation,\n reason,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.recordReconcileActivity({\n at: Date.now(),\n reason,\n force,\n generationBumped: false,\n changedFiles: [],\n generation: this.state.generation,\n error: message,\n });\n timer.done({\n outcome: \"error\",\n generation: this.state.generation,\n error: message,\n });\n throw error;\n }\n }\n\n /**\n * Thrash-breaker gate for a managed-file restore. Returns false while\n * the file is in backoff (the caller must skip the rewrite — leaving\n * the drifted content in place is strictly better than HMR-reload\n * storming every storyboard). See `managedRewriteThrash`.\n *\n * The breaker keys on (file, content): a rewrite whose desired\n * `nextContent` differs from what we last wrote is content EVOLUTION,\n * not a same-content restore loop, so it ALWAYS writes through — even\n * while a backoff armed by a prior same-content storm is still active.\n * (`nextContent` is `null` for a managed-file deletion.)\n */\n private shouldRewriteManagedFile(\n filePath: string,\n force: boolean,\n nextContent: string | null,\n ): boolean {\n if (force) {\n // Forced full regenerations (initialize) are explicit, not drift\n // signals — reset the breaker so the fresh state starts clean.\n this.managedRewriteThrash.delete(filePath);\n return true;\n }\n const entry = this.managedRewriteThrash.get(filePath);\n if (!entry) return true;\n // Evolving desired content is a real state change — never suppress it.\n if (nextContent !== entry.lastContent) return true;\n return Date.now() >= entry.backoffUntil;\n }\n\n /** Record a drift-triggered rewrite; arms the backoff on sustained thrash. */\n private noteManagedFileRewrite(\n filePath: string,\n force: boolean,\n nextContent: string | null,\n ): void {\n if (force) return;\n const now = Date.now();\n const prev = this.managedRewriteThrash.get(filePath);\n // Consecutive only when we restored the SAME content as last time (a real\n // ping-pong loop) AND either that rewrite was within the thrash window, OR\n // this is the probe shortly after a backoff expired and the drift persisted\n // — re-arm immediately rather than requiring another full threshold run. A\n // rewrite whose content differs (content evolution, e.g. registering a new\n // canvas) or one long after the backoff window is a fresh, legitimate write\n // and starts the count over.\n const isConsecutive =\n prev !== undefined &&\n nextContent === prev.lastContent &&\n ((prev.backoffUntil > 0 &&\n now <=\n prev.backoffUntil +\n TempoDevServer.MANAGED_REWRITE_THRASH_WINDOW_MS) ||\n now - prev.lastRewriteAt <=\n TempoDevServer.MANAGED_REWRITE_THRASH_WINDOW_MS);\n const consecutiveRewrites = isConsecutive\n ? prev.consecutiveRewrites + 1\n : 1;\n const arming =\n consecutiveRewrites >= TempoDevServer.MANAGED_REWRITE_THRASH_THRESHOLD;\n this.managedRewriteThrash.set(filePath, {\n consecutiveRewrites,\n lastRewriteAt: now,\n backoffUntil: arming\n ? now + TempoDevServer.MANAGED_REWRITE_BACKOFF_MS\n : 0,\n lastContent: nextContent,\n });\n if (arming) {\n // The storm used to be silent — make the breaker visible in the\n // devserver debug log (ships in the bug-report bundle).\n devLog(\"reconcile:managed_rewrite_backoff\", {\n file: path.basename(filePath),\n consecutiveRewrites,\n backoffMs: TempoDevServer.MANAGED_REWRITE_BACKOFF_MS,\n });\n }\n }\n\n /**\n * A non-forced pass found the file already converged. Only clear the\n * breaker once convergence has HELD beyond the thrash window — a pass\n * that runs right after our own restore (e.g. the watcher repair our own\n * write triggers) trivially sees \"converged\" while the competing\n * writer is still mid-ping-pong, and clearing there would reset the\n * consecutive count every cycle and defeat the breaker.\n */\n private noteManagedFileConverged(filePath: string): void {\n const entry = this.managedRewriteThrash.get(filePath);\n if (!entry) return;\n const now = Date.now();\n if (\n now >= entry.backoffUntil &&\n now - entry.lastRewriteAt >\n TempoDevServer.MANAGED_REWRITE_THRASH_WINDOW_MS\n ) {\n this.managedRewriteThrash.delete(filePath);\n }\n }\n\n private async reconcileGeneratedFiles(force: boolean): Promise<string[]> {\n const changedFiles: string[] = [];\n const tempoDir = path.join(this.tempoRoot, \".tempo\");\n const wrapperPath = path.join(tempoDir, TEMPO_HOST_FILE_NAME);\n const reactCanvasEntries =\n await this.registry.collectReactCanvasHostEntries();\n const startFramework = await this.resolveStartFramework();\n\n // Content-addressed gate: write only when the desired content actually\n // differs from disk — never re-touch a converged file, even under force.\n // force=true (first bringup) still writes a missing/half-written file\n // because currentWrapper (null/stale) !== nextWrapper; it only loses the\n // ability to re-rename an ALREADY-correct file. That re-rename is exactly\n // what the renderer's restartCanvas loop fired on every retry — a fresh\n // supervisor's performInitialize runs reconcile with force=true against an\n // already-correct `.tempo/`, and on Windows the rename throws EPERM under a\n // watcher / AV / OneDrive lock, spinning the host-startup failure loop\n // (TEM-432). `force` still governs the thrash breaker\n // (shouldRewriteManagedFile) for genuine drift.\n const currentWrapper = await readTextFileOptional(wrapperPath);\n const nextWrapper = buildTempoHostWrapper(reactCanvasEntries, {\n startFramework,\n });\n if (currentWrapper !== nextWrapper) {\n if (this.shouldRewriteManagedFile(wrapperPath, force, nextWrapper)) {\n await atomicWriteText(wrapperPath, nextWrapper);\n changedFiles.push(wrapperPath);\n this.noteManagedFileRewrite(wrapperPath, force, nextWrapper);\n }\n } else {\n this.noteManagedFileConverged(wrapperPath);\n }\n\n // The Vite and Expo host wrappers each import `./canvas-host-recovery`\n // (their generated failed-module recovery adapter), so that file is\n // managed with the same content-addressed gate. Its CONTENT is\n // framework-specific; Next has no adapter, and a leftover module from a\n // framework switch is deleted like any other stale managed file.\n const recoveryPath = path.join(tempoDir, CANVAS_HOST_RECOVERY_FILE_NAME);\n const currentRecovery = await readTextFileOptional(recoveryPath);\n const nextRecovery = canvasHostRecoverySourceFor(startFramework);\n if (currentRecovery !== nextRecovery) {\n if (this.shouldRewriteManagedFile(recoveryPath, force, nextRecovery)) {\n if (nextRecovery === null) {\n await rm(recoveryPath, { force: true });\n } else {\n await atomicWriteText(recoveryPath, nextRecovery);\n }\n changedFiles.push(recoveryPath);\n this.noteManagedFileRewrite(recoveryPath, force, nextRecovery);\n }\n } else {\n this.noteManagedFileConverged(recoveryPath);\n }\n\n for (const staleFileName of STALE_MANAGED_CANVAS_FILES) {\n const stalePath = path.join(tempoDir, staleFileName);\n if ((await readTextFileOptional(stalePath)) !== null) {\n // Deletions carry no content; `null` is the sentinel so repeated deletes\n // of the same stale path count as same-\"content\" and stay breaker-eligible.\n if (this.shouldRewriteManagedFile(stalePath, force, null)) {\n await rm(stalePath, { force: true });\n changedFiles.push(stalePath);\n this.noteManagedFileRewrite(stalePath, force, null);\n }\n } else {\n this.noteManagedFileConverged(stalePath);\n }\n }\n\n return changedFiles;\n }\n\n private getStatusFilePath(): string {\n return path.join(this.tempoRoot, \".tempo\", \"status.json\");\n }\n\n // Restore the previous session's dependencies-install cache (distrust-\n // verified again before every start — see tempoInstallReady). The persisted\n // devserverUrl is deliberately NOT restored into live state:\n // `state.devserverUrl` only ever names THIS session's live, health-probed\n // server. The URL health probe attaches on a real start, so a resurrected\n // URL would sit unprobed — a dead (or since-recycled) port leaking to\n // `getState()` readers. The old URL is returned solely as input to\n // `reconcilePersistedDevserverUrl`.\n private async hydrateStatusFromDisk(): Promise<{\n staleDevserverUrl: string | null;\n staleDevserverOwner: PersistedDevserverOwner | null;\n }> {\n const raw = await readTextFileOptional(this.getStatusFilePath());\n if (!raw) {\n this.state.dependenciesInstalled = false;\n return { staleDevserverUrl: null, staleDevserverOwner: null };\n }\n\n try {\n const parsed = JSON.parse(raw) as {\n dependenciesInstalled?: unknown;\n devserverUrl?: unknown;\n devserverOwner?: unknown;\n };\n this.state.dependenciesInstalled = parsed.dependenciesInstalled === true;\n return {\n staleDevserverUrl:\n typeof parsed.devserverUrl === \"string\" ? parsed.devserverUrl : null,\n staleDevserverOwner: parsePersistedDevserverOwner(\n parsed.devserverOwner,\n ),\n };\n } catch {\n this.state.dependenciesInstalled = false;\n return { staleDevserverUrl: null, staleDevserverOwner: null };\n }\n }\n\n // Reconcile the devserverUrl a previous writer persisted with reality.\n // A dead manager makes an owned record eligible for cleanup, but signalling\n // still requires one of the three proofs: its persisted stop command, or a\n // listener whose pid + process-birth identity match the launch record. Every\n // other shape is scrub-or-leave, never kill: multiple supervisor PROCESSES serve the same\n // projectRoot concurrently (the app's main process plus each\n // workspace-chat MCP host runs its own TempoDevServer), so \"reachable and\n // identity-ok for this root\" is exactly what a LIVE SIBLING supervisor's\n // healthy server looks like — it does not prove orphanhood. In every\n // branch `state.devserverUrl` in THIS instance is left untouched: a\n // persisted URL is never resurrected into live state.\n private async reconcilePersistedDevserverUrl(\n staleUrl: string | null,\n staleOwner: PersistedDevserverOwner | null,\n ): Promise<PersistedDevserverReconciliationVerdict> {\n if (!staleUrl) {\n if (!staleOwner) return \"nothing_to_reconcile\";\n if (isPidAlive(staleOwner.managerPid)) return \"live_sibling\";\n const markedOurs =\n supervisorAppInstanceId !== null &&\n staleOwner.appInstanceId === supervisorAppInstanceId;\n return markedOurs\n ? await this.purgeOwnedOrphanDevserver(null, staleOwner)\n : \"foreign_process\";\n }\n // A re-initialize of a live host reads back the URL THIS session\n // persisted on ready — that server is live and health-probed, not stale.\n if (staleUrl === this.state.devserverUrl) return \"current_runtime\";\n\n // Ownership rung — decided before any probe. A record marked with OUR app\n // instance id and a dead manager is an orphan candidate; the cleanup\n // method below separately proves whether a command or process may be acted\n // on. A marker from a DIFFERENT instance (the dev and prod Tempo builds\n // share workspace dirs) proves nothing and falls through to the probe\n // ladder below.\n const markedOurs =\n staleOwner !== null &&\n supervisorAppInstanceId !== null &&\n staleOwner.appInstanceId === supervisorAppInstanceId;\n if (markedOurs) {\n if (isPidAlive(staleOwner.managerPid)) {\n // A live supervisor of our own instance (app main process +\n // workspace-chat MCP host case). Its server and its on-disk record\n // are both load-bearing — leave everything alone.\n devLog(\"supervisor:owned_live_sibling\", {\n url: staleUrl,\n markerPid: staleOwner.managerPid,\n });\n return \"live_sibling\";\n }\n return await this.purgeOwnedOrphanDevserver(staleUrl, staleOwner);\n }\n\n const outcome = await probeTempoHost(\n staleUrl,\n 1_500,\n this.projectRoot,\n await this.readExpectedHostIdentity(),\n );\n if (outcome === \"ok\") {\n // A live tempo-host for this root, owned by some sibling session. Leave\n // the process alone AND leave the file alone: the on-disk URL is\n // load-bearing for cross-process discovery (stateless readers of\n // read-status.ts), and scrubbing it would flip-flop against the owning\n // supervisor's re-persists.\n devLog(\"supervisor:stale_url_live_sibling\", { url: staleUrl });\n return \"live_sibling\";\n }\n if (outcome === \"unreachable\") {\n // Nothing answered — a dead entry; scrub it from disk.\n devLog(\"supervisor:stale_url_cleared\", {\n url: staleUrl,\n reason: \"unreachable\",\n });\n await this.persistStatus();\n return \"orphan_absent\";\n } else {\n // Something answered but did not prove it is our tempo-host for this\n // workspace (port recycled by an unrelated app, or a sibling\n // workspace's host). The process is untouched; our record of the port\n // is meaningless, so scrub it from disk.\n devLog(\"supervisor:stale_url_foreign_left_alone\", { url: staleUrl });\n await this.persistStatus();\n return \"foreign_process\";\n }\n }\n\n // Clean up a server OUR app instance started whose managing supervisor is\n // gone. The persisted stop command is user-authorized cleanup. Otherwise a\n // listener is signalled only when its pid and birth identity match the\n // launch record; a fresh port lookup alone is diagnostic, never proof.\n private async purgeOwnedOrphanDevserver(\n staleUrl: string | null,\n owner: PersistedDevserverOwner,\n ): Promise<PersistedDevserverReconciliationVerdict> {\n const port =\n owner.launchPort ?? (staleUrl ? portFromReadyUrl(staleUrl) : null);\n let stopCommandRan = false;\n let stopCommandSucceeded = false;\n if (owner.stopCommand) {\n const stopResult = await runBoundedShellCommand({\n command: owner.stopCommand,\n cwd: this.commandCwd,\n env: {\n ...buildManagedCommandEnv(),\n ...(port !== null\n ? { TEMPO_PORT: String(port), PORT: String(port) }\n : {}),\n },\n timeoutMs: 10_000,\n });\n stopCommandRan = true;\n stopCommandSucceeded = stopResult.ok;\n devLog(\"supervisor:owned_orphan_stop_command\", {\n url: staleUrl,\n command: owner.stopCommand,\n ...stopResult,\n });\n }\n const listenerPid = port !== null ? await findPortListenerPid(port) : null;\n let listenerIdentityMatches = false;\n if (\n owner.portWasTempoAllocated &&\n listenerPid !== null &&\n listenerPid === owner.launchPid &&\n owner.launchProcessStartedAt !== null\n ) {\n const liveStartedAt = await readProcessStartedAt(listenerPid);\n listenerIdentityMatches = liveStartedAt === owner.launchProcessStartedAt;\n }\n // Every platform requires the recorded pid + birth identity. A recycled\n // port (including macOS com.docker.backend) is never a kill license.\n const maySignalListener =\n listenerPid !== null &&\n listenerPid > 1 &&\n listenerPid !== process.pid &&\n listenerIdentityMatches;\n if (maySignalListener && listenerPid !== null) {\n await terminateProcessTreeByPid(listenerPid, 3_000, 2_000);\n }\n const finalOutcome = staleUrl\n ? await probeTempoHost(\n staleUrl,\n 1_500,\n this.projectRoot,\n await this.readExpectedHostIdentity(),\n )\n : null;\n const launchIdentityStillAlive =\n owner.launchPid !== null &&\n owner.launchProcessStartedAt !== null &&\n (await readProcessStartedAt(owner.launchPid)) ===\n owner.launchProcessStartedAt;\n const reconciled =\n !launchIdentityStillAlive &&\n (staleUrl ? finalOutcome !== \"ok\" : stopCommandSucceeded);\n devLog(\"supervisor:owned_orphan_purge_result\", {\n url: staleUrl,\n markerPid: owner.managerPid,\n listenerPid,\n stopCommandRan,\n listenerIdentityMatches,\n listenerSignalled: maySignalListener,\n finalOutcome,\n launchIdentityStillAlive,\n reconciled,\n });\n if (reconciled) {\n // Persist writes only live state — this scrubs both the URL and the\n // ownership marker from status.json after cleanup has been verified.\n await this.persistStatus();\n }\n return reconciled ? \"orphan_reaped\" : \"orphan_cleanup_failed\";\n }\n\n private async persistStatus(): Promise<void> {\n const nextStatus: Record<string, unknown> = {\n dependenciesInstalled: this.state.dependenciesInstalled,\n };\n if (this.state.devserverUrl) {\n nextStatus.devserverUrl = this.state.devserverUrl;\n }\n if (this.state.devserverUrl || this.devserverExternalRuntimePending) {\n // Ownership marker: lets a later process identify an orphan candidate.\n // It is deliberately insufficient as kill authority by itself; cleanup\n // still requires the persisted stop command or matching pid + birth\n // identity. Written even when identity is unconfigured (appInstanceId\n // null): null never matches an instance, so such records stay on the\n // non-destructive probe ladder.\n nextStatus.devserverOwner = {\n appInstanceId: supervisorAppInstanceId,\n managerPid: process.pid,\n startedAt: this.devserverReadyAt ?? Date.now(),\n portWasTempoAllocated: this.devserverPortWasTempoAllocated,\n launchPid: this.devserverLaunchPid,\n launchProcessStartedAt: this.devserverLaunchProcessStartedAt,\n launchPort: this.devserverLaunchPort,\n stopCommand: this.devserverStopCommand,\n } satisfies PersistedDevserverOwner;\n }\n const statusPath = this.getStatusFilePath();\n const text = `${JSON.stringify(nextStatus, null, 2)}\\n`;\n try {\n await writeStatusTextBestEffort(statusPath, text);\n } catch (error) {\n devLog(\"status:persist_failed\", {\n statusPath,\n message: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n\n// Emits the generated `.tempo/tempo-host.tsx` that mounts every\n// demand-registered React canvas under a single CanvasHost.\n//\n// Isolation invariant: a canvas module that fails to load (throwing import,\n// missing dependency, syntax error) must blank ONLY its own canvas — never the\n// sibling canvases sharing the host. This is why each entry is a `lazy(() =>\n// import(...))` registration passed to `CanvasHost`, which owns the per-canvas\n// `CanvasImportBoundary`: a static `import Canvas0 from \"...\"` at the top\n// of the module\n// couples every canvas into one ESM graph, so a single broken import throws\n// during host evaluation and takes down the whole file — which is exactly the\n// incident this shape prevents (one broken canvas import blanked every open\n// canvas). Keep the lazy + boundary wrapping; do not collapse back to static\n// imports. See specs/canvas-resilience-spec.md.\nfunction buildTempoHostWrapper(\n reactCanvasEntries: readonly ReactCanvasHostEntry[] = [],\n options: { startFramework?: DevServerStartFramework } = {},\n): string {\n const hasReactCanvasEntries = reactCanvasEntries.length > 0;\n const canvasModuleEntries = reactCanvasEntries\n .map(\n ({ importPath, importSpecifier }) =>\n ` {\\n canvasPath: ${JSON.stringify(importPath)},\\n load: () => import(${JSON.stringify(importSpecifier)}),\\n },`,\n )\n .join(\"\\n\");\n const canvasModulesDeclaration = hasReactCanvasEntries\n ? `const canvasModules = [\\n${canvasModuleEntries}\\n];\\n`\n : \"\";\n const directCanvasSelection = hasReactCanvasEntries\n ? ` const directUrlParams = new URLSearchParams(directUrlSearch);\n const requestedCanvasPath =\n directUrlParams.get(\"canvasPath\")?.trim() ??\n directUrlParams.get(\"pageFile\")?.trim();\n const activeCanvasModules = directUrlParams.has(\"tempoCaptureIdle\")\n ? []\n : requestedCanvasPath\n ? canvasModules.filter(\n ({ canvasPath }) => canvasPath === requestedCanvasPath,\n )\n : canvasModules;\n`\n : \"\";\n const canvasModulesProp = hasReactCanvasEntries\n ? \" canvasModules={activeCanvasModules}\"\n : \"\";\n // Both bundler hosts get a failed-module recovery adapter, with different\n // content (see `canvasHostRecoverySourceFor`): Vite's closes the\n // failed-module-evaluation HMR hole (section 4.8 of\n // specs/canvas-vite-native-simplification-nlspec.md); Metro's closes the\n // lazy-bundling gap where a canvas demand-registered onto a live document\n // is hot-swapped in as a module id this client never received (TEM-1410).\n // Each self-installs on evaluation, so this side-effect import stays\n // exactly-once per document even when the wrapper re-evaluates under HMR.\n const recoveryImport = canvasHostRecoverySourceFor(options.startFramework)\n ? `import \"./canvas-host-recovery\";\\n`\n : \"\";\n // This stamp does two jobs, and NEITHER is optional — Metro does not\n // commit to which one converges a given registration:\n //\n // 1. It is the Metro adapter's one input: 1 on a cold document load, 2+\n // once HMR has swapped this wrapper in place. That bit separates \"this\n // canvas is genuinely broken, leave it isolated\" from \"this canvas is\n // merely missing from this client's lazily-built bundle, reload once\".\n //\n // 2. It biases Metro toward answering a canvas-set change with a full\n // document reload instead of a Fast Refresh hot swap. That reload\n // rebuilds the bundle WITH the newly registered canvas — the receipt\n // Vite gets from its `/__tempo/canvas-host-registration`\n // invalidate-then-transform barrier and Metro has no equivalent for.\n //\n // Measured on a real `expo start --web` host, repeatedly: an unstamped\n // wrapper hot-swaps every time and every `import()` through the swapped\n // code — the new canvas AND canvases already rendering — throws\n // `Requiring unknown module \"N\"` forever. A stamped wrapper full-reloads on\n // MOST registrations but not all: Metro's boundary walk keys off module\n // exports (metro-runtime `isReactRefreshBoundary`), which the stamp does\n // not change, and some registrations still land as a hot swap. On those,\n // the adapter reads generation 2+ off this stamp and converges the\n // document with one reload ~100ms later (one transient unknown-module\n // error instead of a permanent one). Treat (1) and (2) as co-equal: do not\n // remove the adapter because reloads \"seem\" to always happen, and do not\n // remove the stamp because the adapter \"seems\" to cover everything — the\n // adapter is blind without it.\n //\n // Stamped for every framework so the value is never framework-conditional\n // at read time; only the Metro adapter reads it. The Vite wrapper stays a\n // Fast Refresh boundary with the stamp in place (plugin-react keys off\n // exports too) — pinned by the storyboard-readiness e2e suite.\n const generationStamp = `const tempoHostGlobals = globalThis as any;\\ntempoHostGlobals.${CANVAS_HOST_WRAPPER_GENERATION_KEY} =\\n (tempoHostGlobals.${CANVAS_HOST_WRAPPER_GENERATION_KEY} ?? 0) + 1;\\n`;\n\n return `/* THIS FILE IS AUTO-GENERATED. DO NOT EDIT. */\nimport { useSyncExternalStore } from \"react\";\nimport { CanvasHost } from \"tempo-sdk/canvas\";\n${recoveryImport}\n${generationStamp}${canvasModulesDeclaration}\nfunction subscribeToDirectUrl(onChange: () => void) {\n window.addEventListener(\"popstate\", onChange);\n return () => window.removeEventListener(\"popstate\", onChange);\n}\n\nfunction readDirectUrlSearch() {\n return window.location.search;\n}\n\nexport default function TempoHost() {\n // History mutations do not reload the document. Subscribing here makes\n // direct-render targets navigable even with older tempo-sdk packages whose\n // CanvasHost only samples location.search when its parent renders.\n const directUrlSearch = useSyncExternalStore(\n subscribeToDirectUrl,\n readDirectUrlSearch,\n () => \"\",\n );\n void directUrlSearch;\n${directCanvasSelection}\n return (\n <CanvasHost${canvasModulesProp}>\n </CanvasHost>\n );\n}\n`;\n}\n\ntype PackageManagerName = DetectedPackageManager[\"pm\"];\n\ntype InstallStep = {\n cwd: string;\n command: string;\n label: string;\n /**\n * Wipe `<cwd>/node_modules` before running the install command so npm does a\n * clean, complete resolution instead of an incremental top-up over a\n * possibly-partial tree. Used on forced retries (e.g. a crash-retry after a\n * devserver died) where the existing tree is suspect. See TEM-820.\n */\n cleanBeforeInstall?: boolean;\n /**\n * Write install-completion sentinels for these package roots after the\n * command exits 0, so the next start can tell a finished install from an\n * interrupted one and trust container/PnP layouts without reinstalling.\n * See TEM-820.\n */\n markerRoots?: string[];\n /** Package roots whose successful custom install is allowed to materialize\n * dependencies outside the host filesystem (Docker/devcontainers). */\n externalLayoutMarkerRoots?: string[];\n};\n\n/**\n * Path of the install-completion sentinel. Lives INSIDE `node_modules` so that\n * deleting `node_modules` also removes the marker — a manually-cleaned tree is\n * then correctly seen as \"not installed\". See TEM-820.\n */\nfunction installCompletionMarkerPath(packageRoot: string): string {\n return path.join(packageRoot, \"node_modules\", \".tempo-install-complete\");\n}\n\n// v2 could be stamped on a broken app root by SDK maintenance even though\n// its install never completed. Re-establish that proof once after upgrade.\nconst INSTALL_COMPLETION_MARKER_VERSION = \"tempo-install-complete-v3\";\nconst EXTERNAL_LAYOUT_INSTALL_MARKER = \".tempo-external-install-complete\";\n\n/**\n * The completion key fingerprints the dependency declaration plus the nearest\n * lockfile. There is intentionally no time-based throttle: unchanged inputs\n * are durable proof, while any dependency/lock resolution change invalidates\n * immediately. SDK commands invalidate before mutation and restore after a\n * completed install; spec-only maintenance refreshes existing proof.\n */\nexport async function computeInstallCompletionKey(\n packageRoot: string,\n): Promise<string | null> {\n const packageJsonPath = path.join(packageRoot, \"package.json\");\n const raw = await readTextFileOptional(packageJsonPath);\n if (raw == null) return null;\n\n let dependencyInput: unknown = raw;\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n dependencyInput = {\n packageManager: parsed.packageManager ?? null,\n dependencies: parsed.dependencies ?? null,\n devDependencies: parsed.devDependencies ?? null,\n optionalDependencies: parsed.optionalDependencies ?? null,\n peerDependencies: parsed.peerDependencies ?? null,\n overrides: parsed.overrides ?? null,\n resolutions: parsed.resolutions ?? null,\n pnpm: parsed.pnpm ?? null,\n };\n } catch {\n // Invalid JSON will fail elsewhere; hashing the raw bytes still prevents a\n // stale completion marker from being trusted.\n }\n\n const lockfileNames = [\n \"pnpm-lock.yaml\",\n \"package-lock.json\",\n \"npm-shrinkwrap.json\",\n \"yarn.lock\",\n \"bun.lock\",\n \"bun.lockb\",\n ];\n let current = path.resolve(packageRoot);\n let lockfile: { path: string; content: Buffer } | null = null;\n while (true) {\n for (const name of lockfileNames) {\n try {\n lockfile = {\n path: path.relative(packageRoot, path.join(current, name)),\n content: await readFile(path.join(current, name)),\n };\n break;\n } catch {}\n }\n if (lockfile) break;\n const parent = path.dirname(current);\n if (parent === current) break;\n current = parent;\n }\n\n const hash = createHash(\"sha256\");\n hash.update(JSON.stringify(dependencyInput));\n hash.update(\"\\0\");\n hash.update(lockfile?.path ?? \"no-lockfile\");\n hash.update(\"\\0\");\n if (lockfile) hash.update(lockfile.content);\n hash.update(\n `\\0${process.platform}\\0${process.arch}\\0${process.versions.modules}`,\n );\n return `${INSTALL_COMPLETION_MARKER_VERSION}:${hash.digest(\"hex\")}`;\n}\n\n/** Read the stored completion key, or `null` if the marker is absent. */\nasync function readInstallCompletionMarker(\n packageRoot: string,\n): Promise<string | null> {\n return await readTextFileOptional(installCompletionMarkerPath(packageRoot));\n}\n\n/**\n * True when a prior `npm install` completed successfully for the CURRENT\n * dependency inputs — i.e. the on-disk marker matches the freshly-computed key.\n * Returns `false` (⇒ reinstall) when the marker is absent (fresh machine,\n * manually-cleaned tree, or an interrupted install that never got to write it)\n * or stale (written by an older marker-format version).\n */\nasync function installCompletedForCurrentDeps(\n packageRoot: string,\n): Promise<boolean> {\n const marker = await readInstallCompletionMarker(packageRoot);\n if (marker === null) {\n // No receipt means no completed install. Avoid hashing a potentially\n // large lockfile on the common fresh/cancelled-install path.\n return !(await pathExists(path.join(packageRoot, \"package.json\")));\n }\n const key = await computeInstallCompletionKey(packageRoot);\n if (key == null) {\n // No package.json ⇒ nothing to install ⇒ vacuously complete.\n return true;\n }\n return marker === key;\n}\n\n/**\n * Record a successful install against its final dependency inputs. All\n * managed installers share this protocol: invalidate before mutation, then\n * record completion only after the install finishes successfully.\n */\nexport async function writeInstallCompletionMarker(\n packageRoot: string,\n): Promise<void> {\n const key = await computeInstallCompletionKey(packageRoot);\n if (key == null) return;\n try {\n // Ensure `node_modules/` exists: a custom install script may install to a\n // different root (e.g. `cd .. && pnpm install`) and never create this one.\n // An empty `node_modules` here is harmless — the declared-deps check still\n // forces a reinstall if the actual packages aren't present.\n await mkdir(path.join(packageRoot, \"node_modules\"), { recursive: true });\n await writeFile(installCompletionMarkerPath(packageRoot), key, \"utf-8\");\n } catch (error) {\n // Best-effort: a missing marker only costs one extra (idempotent, fast when\n // already satisfied) reinstall on the next start — never a failure.\n console.warn(\n `[tempo-devserver] failed to write install-completion marker at ${packageRoot}:`,\n error,\n );\n }\n}\n\nasync function externalLayoutInstallCompleted(\n packageRoot: string,\n): Promise<boolean> {\n const marker = await readTextFileOptional(\n path.join(packageRoot, \"node_modules\", EXTERNAL_LAYOUT_INSTALL_MARKER),\n );\n return (\n marker !== null &&\n marker === (await computeInstallCompletionKey(packageRoot))\n );\n}\n\n/** Remove prior proof before any managed installer mutates this root. */\nexport async function invalidateInstallCompletionMarkers(\n packageRoot: string,\n): Promise<void> {\n await Promise.all([\n rm(installCompletionMarkerPath(packageRoot), { force: true }),\n rm(path.join(packageRoot, \"node_modules\", EXTERNAL_LAYOUT_INSTALL_MARKER), {\n force: true,\n }),\n ]);\n}\n\nasync function writeExternalLayoutInstallMarker(\n packageRoot: string,\n): Promise<void> {\n const key = await computeInstallCompletionKey(packageRoot);\n if (key == null) return;\n await mkdir(path.join(packageRoot, \"node_modules\"), { recursive: true });\n await writeFile(\n path.join(packageRoot, \"node_modules\", EXTERNAL_LAYOUT_INSTALL_MARKER),\n key,\n \"utf-8\",\n );\n}\n\nasync function refreshInstallCompletionMarkers(\n packageRoot: string,\n): Promise<void> {\n const marker = await readInstallCompletionMarker(packageRoot);\n if (\n marker === null ||\n marker === (await computeInstallCompletionKey(packageRoot))\n )\n return;\n await writeInstallCompletionMarker(packageRoot);\n if (\n await pathExists(\n path.join(packageRoot, \"node_modules\", EXTERNAL_LAYOUT_INSTALL_MARKER),\n )\n ) {\n await writeExternalLayoutInstallMarker(packageRoot);\n }\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function hasPnpLoader(packageRoot: string): Promise<boolean> {\n return (\n (await pathExists(path.join(packageRoot, \".pnp.cjs\"))) ||\n (await pathExists(path.join(packageRoot, \".pnp.js\")))\n );\n}\n\nasync function nodeModulesHasMeaningfulEntries(\n packageRoot: string,\n): Promise<boolean> {\n try {\n const entries = await readdir(path.join(packageRoot, \"node_modules\"));\n return entries.some((entry) => !entry.startsWith(\".\"));\n } catch {\n return false;\n }\n}\n\n/**\n * Recognize every `scripts.install` shape Tempo itself has ever scaffolded —\n * the bare `<pm> install` forms (pre-#881 workspaces) and the PM-aware\n * chained `cd .. && <pm> install && cd tempo && <pm> install` forms. For any\n * of these, `buildInstallSteps` IGNORES the config string and plans its own\n * structured steps (project-root install when its completion proof is missing,\n * then the tempo sidecar install). Anything else is a user-authored script\n * and runs verbatim.\n *\n * This recognition is load-bearing for config ownership (TEM-1324): because\n * a stale bare value already gets the correct install BEHAVIOR here, nothing\n * ever needs to rewrite the user's `tempo.config.json` in place. Narrowing\n * this set would silently break workspaces whose committed config still\n * carries a bare form — exported and pinned by\n * `generated-install-script.test.ts`.\n */\nexport function looksLikeGeneratedInstallScript(command: string): boolean {\n const normalized = command.trim();\n const installCommand =\n \"(?:npm\\\\s+(?:install|ci)|pnpm\\\\s+install(?:\\\\s+--(?:no-)?frozen-lockfile)?|yarn\\\\s+install(?:\\\\s+--immutable)?|bun\\\\s+install)\";\n return (\n new RegExp(`^${installCommand}$`).test(normalized) ||\n new RegExp(\n `^cd \\\\.\\\\. && ${installCommand} && cd tempo && ${installCommand}$`,\n ).test(normalized)\n );\n}\n\nasync function commandAvailable(command: string): Promise<boolean> {\n return await new Promise<boolean>((resolve) => {\n const child = spawn(`${command} --version`, {\n shell: true,\n stdio: \"ignore\",\n windowsHide: true,\n env: buildManagedCommandEnv(),\n });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => resolve(code === 0));\n });\n}\n\nasync function resolvePackageManagerCommand(\n pm: PackageManagerName,\n cwd: string,\n): Promise<string> {\n if (await commandAvailable(pm)) {\n return pm;\n }\n if (\n (pm === \"pnpm\" || pm === \"yarn\") &&\n (await commandAvailable(\"corepack\"))\n ) {\n return `corepack ${pm}`;\n }\n throw new ToolMissingError(pm, cwd);\n}\n\n/**\n * Test-isolation guard. When `TEMPO_DEVSERVER_PROTECTED_ROOTS` is set\n * (colon-separated absolute paths), returns the protected root that `cwd` is\n * at-or-inside, or `null` when none applies.\n *\n * Why this exists: in the Electron e2e suite the app runs from inside the\n * Tempo monorepo checkout, and that checkout's own `tempo/` dir is a real\n * pnpm-workspace member (its package.json uses `workspace:*` deps). If a\n * devserver/canvas spec resolves the project to that checkout, ANY package\n * manager mutation run there — `pnpm install` / `npm install` / `npm update` —\n * walks UP to the monorepo workspace root and RE-LINKS the entire monorepo's\n * node_modules mid-run, transiently deleting the `playwright-core` symlink and\n * crashing every concurrent Playwright worker with\n * `MODULE_NOT_FOUND: playwright-core/lib/utils` at 0ms. The CI job sets this\n * env to `$GITHUB_WORKSPACE` so the supervisor refuses to mutate the runner's\n * own tree. Unset in production (and for real user projects, which never live\n * inside the protected tree), so this is a no-op outside CI.\n *\n * Every code path that shells out a package-manager install/update against a\n * caller-supplied cwd MUST consult this before running — not just the\n * synthesized install steps (`filterProtectedInstallSteps`), but also the\n * SDK-spec refresh (`ensureTempoSdkLatestSpec`) and the tempobook\n * auto-mirror handler.\n */\nexport function protectedRootFor(cwd: string): string | null {\n const raw = process.env.TEMPO_DEVSERVER_PROTECTED_ROOTS;\n if (!raw) return null;\n const protectedRoots = raw\n .split(path.delimiter)\n .map((p) => p.trim())\n .filter((p) => p.length > 0)\n .map((p) => path.resolve(p));\n if (protectedRoots.length === 0) return null;\n\n const resolved = path.resolve(cwd);\n for (const root of protectedRoots) {\n const rel = path.relative(root, resolved);\n // Inside (or equal to) root iff rel doesn't climb out and isn't absolute.\n if (rel === \"\" || (!rel.startsWith(\"..\") && !path.isAbsolute(rel))) {\n return root;\n }\n }\n return null;\n}\n\n/**\n * Drop any synthesized install step whose cwd is at or inside a\n * `TEMPO_DEVSERVER_PROTECTED_ROOTS` entry. See {@link protectedRootFor}.\n */\nfunction filterProtectedInstallSteps(steps: InstallStep[]): InstallStep[] {\n return steps.filter((step) => {\n const hit = protectedRootFor(step.cwd);\n if (hit) {\n devLog(\"supervisor:install_step_skipped_protected\", {\n cwd: step.cwd,\n protectedRoot: hit,\n label: step.label,\n command: step.command,\n });\n return false;\n }\n return true;\n });\n}\n\nasync function buildSafeInstallCommand(\n manager: DetectedPackageManager,\n cwd: string,\n): Promise<string> {\n const resolved = await resolvePackageManagerCommand(manager.pm, cwd);\n const installSuffix = manager.installCmd.replace(/^[^\\s]+/, \"\");\n return `${resolved}${installSuffix}`;\n}\n\nfunction parseFirstSemverMajor(value: string): number | null {\n const match = value.match(/(\\d+)(?:\\.\\d+)?(?:\\.\\d+)?/);\n if (!match?.[1]) return null;\n const major = Number.parseInt(match[1], 10);\n return Number.isFinite(major) ? major : null;\n}\n\nfunction extractObservedNodeVersion(output: string): string | null {\n return (\n /(?:Node(?:\\.js)?(?: version)?|node)\\s+v?(\\d+\\.\\d+(?:\\.\\d+)?)/i.exec(\n output,\n )?.[1] ?? null\n );\n}\n\nfunction parseNodeVersionParts(value: string): {\n major: number;\n minor: number;\n patch: number;\n} | null {\n const match = value\n .trim()\n .replace(/^v/, \"\")\n .match(/^(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!match?.[1] || !match[2] || !match[3]) return null;\n return {\n major: Number.parseInt(match[1], 10),\n minor: Number.parseInt(match[2], 10),\n patch: Number.parseInt(match[3], 10),\n };\n}\n\nfunction isNodeVersionCompatibleWithVite7(value: string): boolean {\n const parsed = parseNodeVersionParts(value);\n if (!parsed) return false;\n if (parsed.major === 20) return parsed.minor >= 19;\n if (parsed.major === 22) return parsed.minor >= 12;\n return parsed.major > 22;\n}\n\nasync function resolveManagedNodeVersion(\n options: { skipBundledNode?: boolean } = {},\n): Promise<string | null> {\n return await probeNodeVersion(\n buildManagedCommandEnv({ skipBundledNode: options.skipBundledNode }),\n );\n}\n\n/**\n * A resolved \"run managed commands under THIS Node\" decision for one tree.\n * `bundled: true` means the binDir is the host's bundled-Node shim dir and the\n * spawn env additionally needs `ELECTRON_RUN_AS_NODE=1` (applyBundledNodeEnv).\n */\nexport type ProjectNodeOverride = {\n binDir: string;\n version: string;\n source: \"nvm\" | \"nodenv\" | \"nvm-windows\" | \"bundled\";\n bundled: boolean;\n};\n\n// Per-tree memo for the override decision (TEM-1536). Settled \"none\"/\"override\"\n// decisions are cached as promises for the process lifetime — same posture as\n// the bundled-node latch — EXCEPT the unsatisfied case, which is deliberately\n// re-resolved on every call: the user's remediation is \"install a matching\n// Node\", and Retry must be able to pick the fresh install up without an app\n// restart. A cached override re-verifies its binDir still exists on every hit\n// (an uninstalled version falls back to re-resolution instead of spawning\n// commands against a dead PATH entry).\nconst projectNodeOverrideCache = new Map<\n string,\n Promise<ProjectNodeOverride | null>\n>();\n// Last unsatisfied outcome per tree, for the compatibility error's hint line.\nconst projectNodeUnsatisfiedByDir = new Map<\n string,\n { requirement: string; currentVersion: string | null }\n>();\n\n/** Test-only: reset the project-Node override memo. */\nexport function resetProjectNodeOverrideCacheForTests(): void {\n projectNodeOverrideCache.clear();\n projectNodeUnsatisfiedByDir.clear();\n}\n\n/**\n * Shape an override for buildManagedCommandEnv's `projectNode` option.\n * nodenv-sourced overrides carry their version so the spawn env can set\n * `NODENV_VERSION` — the only channel nodenv's shims (the typical `pnpm` /\n * `npm` entrypoint on a nodenv machine) actually honor.\n */\nfunction toProjectNodeEnvOption(\n override: ProjectNodeOverride | null,\n): { binDir: string; bundled: boolean; nodenvVersion?: string | null } | null {\n if (!override) return null;\n return {\n binDir: override.binDir,\n bundled: override.bundled,\n nodenvVersion: override.source === \"nodenv\" ? override.version : null,\n };\n}\n\n/**\n * The actionable hint for a tree whose latest resolution found the project's\n * `engines.node` unsatisfiable with every Node on the machine, or null when\n * the last resolution succeeded (or never ran). Rendered into\n * `CanvasHostIdentityError` so a served-identity failure explains the actual\n * package-manager blocker instead of only reporting a stale/inactive plugin.\n */\nexport function getProjectNodeUnsatisfiedHint(cwd: string): string | null {\n const unsatisfied = projectNodeUnsatisfiedByDir.get(path.resolve(cwd));\n return unsatisfied ? buildUnsatisfiedNodeHint(unsatisfied) : null;\n}\n\n/**\n * Which Node should managed package-manager commands for `cwd` run under?\n * Null in the overwhelmingly common case (no declared `engines.node`, or the\n * current managed Node already satisfies it — never shadow a Node that works).\n * An override only when the project's declared requirement rules the current\n * Node out and a satisfying install exists: a version-manager Node (nvm /\n * nodenv / nvm-windows, steered by `.nvmrc`/`.node-version`), else the host's\n * bundled Node. See project-node-version.ts (TEM-1536).\n *\n * Exported for unit tests; production callers are runShellCommand and the\n * supervisor's selectNodeRuntimeForStart.\n */\nexport async function resolveProjectNodeOverrideForDir(\n cwd: string,\n): Promise<ProjectNodeOverride | null> {\n const dir = path.resolve(cwd);\n // The bundled-Node latch is process-global and monotonic, and flipping it\n // changes which Node the managed env resolves — so a decision made before\n // activation (e.g. \"current satisfies\") can be stale after another\n // supervisor in this process activates the bundled toolchain. Keying the\n // memo by latch state forces one re-resolution across the flip.\n const key = `${dir}|bundled:${isBundledNodeActivated()}`;\n const cached = projectNodeOverrideCache.get(key);\n if (cached) {\n const override = await cached;\n if (override === null) return null;\n if (\n await access(override.binDir).then(\n () => true,\n () => false,\n )\n ) {\n return override;\n }\n projectNodeOverrideCache.delete(key);\n }\n\n const resolution = (async (): Promise<ProjectNodeOverride | null> => {\n const requirement = await readProjectNodeRequirement(dir);\n if (requirement.engines.length === 0) {\n projectNodeUnsatisfiedByDir.delete(dir);\n return null;\n }\n\n // The Vite-7 Node floor only binds trees that actually host Vite 7+ —\n // the same `requireFloor` gate selectNodeRuntimeForStart applies. For\n // every other surface (Next, Expo, Vite 6, the project root install) an\n // old declared Node is legitimate: rejecting an installed Node 18 for an\n // engine-strict Node-18 project would leave it unsatisfiable for no\n // reason.\n const treePackageJson = await readJsonFile<{\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n }>(path.join(dir, \"package.json\"));\n const viteSpec =\n treePackageJson?.dependencies?.vite ??\n treePackageJson?.devDependencies?.vite;\n const viteMajor =\n typeof viteSpec === \"string\" ? parseFirstSemverMajor(viteSpec) : null;\n const requireFloor = viteMajor != null && viteMajor >= 7;\n const floorOk = requireFloor\n ? isNodeVersionCompatibleWithVite7\n : () => true;\n\n const currentVersion = await resolveManagedNodeVersion();\n const decision = selectNodeOverride({\n currentVersion,\n requirement,\n candidates: await discoverVersionManagerNodes({\n env: process.env,\n homeDir: os.homedir(),\n platform: process.platform,\n }),\n floorOk,\n });\n\n if (decision.kind === \"none\") {\n projectNodeUnsatisfiedByDir.delete(dir);\n return null;\n }\n if (decision.kind === \"override\") {\n projectNodeUnsatisfiedByDir.delete(dir);\n devLog(\"supervisor:project_node_override\", {\n cwd: dir,\n currentVersion,\n chosenVersion: decision.candidate.version,\n source: decision.candidate.source,\n binDir: decision.candidate.binDir,\n engines: requirement.engines,\n versionFileSpec: requirement.versionFileSpec,\n });\n return { ...decision.candidate, bundled: false };\n }\n\n // No version-manager install satisfies. Last resort: the host's bundled\n // Node (Electron's embedded runtime — currently Node 24.x), when it\n // satisfies the requirement. Probed against the SYSTEM env with the shim\n // dir applied, mirroring how the spawn env will actually resolve it.\n const bundledBinDir = getBundledNodeBinDir();\n const bundledVersion = bundledBinDir\n ? await probeNodeVersion(\n applyBundledNodeEnv(\n buildManagedCommandEnv({ skipBundledNode: true }),\n bundledBinDir,\n ),\n )\n : null;\n if (\n bundledBinDir &&\n bundledVersion &&\n versionSatisfiesRequirement(bundledVersion, requirement) &&\n floorOk(bundledVersion)\n ) {\n projectNodeUnsatisfiedByDir.delete(dir);\n devLog(\"supervisor:project_node_override\", {\n cwd: dir,\n currentVersion,\n chosenVersion: bundledVersion,\n source: \"bundled\",\n binDir: bundledBinDir,\n engines: requirement.engines,\n versionFileSpec: requirement.versionFileSpec,\n });\n return {\n binDir: bundledBinDir,\n version: bundledVersion,\n source: \"bundled\",\n bundled: true,\n };\n }\n\n projectNodeUnsatisfiedByDir.set(dir, {\n requirement: decision.requirement,\n currentVersion: decision.currentVersion,\n });\n devLog(\"supervisor:project_node_unsatisfied\", {\n cwd: dir,\n currentVersion,\n requirement: decision.requirement,\n bundledVersion,\n });\n // Not cached: the user installing a matching Node + Retry must re-resolve.\n projectNodeOverrideCache.delete(key);\n return null;\n })().catch((error): null => {\n // Resolution is advisory — a failure here must degrade to \"no override\"\n // (current behavior), never break the spawn, and never wedge the memo.\n projectNodeOverrideCache.delete(key);\n devLog(\"supervisor:project_node_resolution_failed\", {\n cwd: dir,\n message: error instanceof Error ? error.message : String(error),\n });\n return null;\n });\n\n projectNodeOverrideCache.set(key, resolution);\n return await resolution;\n}\n\nasync function getAvailableLocalPort(): Promise<number> {\n return await new Promise<number>((resolve, reject) => {\n const server = createServer();\n server.unref();\n server.on(\"error\", reject);\n server.listen(0, \"127.0.0.1\", () => {\n const address = server.address();\n const port = address && typeof address === \"object\" ? address.port : null;\n server.close(() => {\n if (port) {\n resolve(port);\n } else {\n reject(new Error(\"Could not allocate a local port\"));\n }\n });\n });\n });\n}\n\n// The dev-server CLI dialects we know how to bind to a chosen port. Drives the\n// host/port flag form in `injectDevServerPort`.\nexport type DevServerStartFramework = \"next\" | \"vite\" | \"expo\";\n\nexport interface ManagedStartPlan {\n command: string;\n tempoHostUrl: string | null;\n injectedPort: number;\n portWasTempoAllocated: boolean;\n env: {\n TEMPO_PORT: string;\n PORT?: string;\n };\n}\n\n/**\n * Return an explicitly-spelled port from `urls.tempoHost`, or null when the\n * URL is absent, templated, or relies on the scheme's implicit 80/443 default.\n * An origin's default request port is not evidence that the launched command\n * should bind that privileged port; proxy-fronted URLs commonly omit it.\n */\nexport function portFromConfiguredTempoHostUrl(\n tempoHostUrl: HttpUrlSource | null,\n): number | null {\n if (\n !tempoHostUrl ||\n typeof tempoHostUrl !== \"string\" ||\n tempoHostUrl.includes(\"${PORT}\")\n )\n return null;\n try {\n const parsed = new URL(tempoHostUrl);\n if (parsed.port) return Number(parsed.port);\n } catch {\n // Config validation owns malformed URLs. Keep this helper total so direct\n // TempoDevServer callers degrade to the normal allocation path.\n }\n return null;\n}\n\n/**\n * Apply the canvas-host port contract after the effective port is known.\n * Exported as a pure helper so the cross-platform `${PORT}` behavior and the\n * generic PORT opt-in rule are pinned without spawning a shell.\n */\nexport function buildManagedStartPlan(options: {\n command: string;\n tempoHostUrl: HttpUrlSource | null;\n port: number;\n framework: DevServerStartFramework;\n}): ManagedStartPlan {\n const commandUsesPortToken = options.command.includes(\"${PORT}\");\n const configuredPort = portFromConfiguredTempoHostUrl(options.tempoHostUrl);\n const urlUsesPortToken =\n typeof options.tempoHostUrl === \"string\" &&\n options.tempoHostUrl.includes(\"${PORT}\");\n const recognizedCommand = canInjectDevServerPort(options.command);\n const command = commandUsesPortToken\n ? substituteAppPort(options.command, options.port)\n : recognizedCommand\n ? injectDevServerPort(options.command, options.port, options.framework)\n : options.command;\n const tempoHostUrl =\n typeof options.tempoHostUrl === \"string\"\n ? substituteAppPort(options.tempoHostUrl, options.port)\n : null;\n const shouldExportGenericPort =\n commandUsesPortToken ||\n urlUsesPortToken ||\n recognizedCommand ||\n options.tempoHostUrl !== null;\n\n return {\n command,\n tempoHostUrl,\n injectedPort: options.port,\n portWasTempoAllocated: configuredPort === null,\n env: {\n TEMPO_PORT: String(options.port),\n ...(shouldExportGenericPort ? { PORT: String(options.port) } : {}),\n },\n };\n}\n\n// True when `command` launches Expo's Metro bundler (`expo start`) rather than a\n// Vite/Next dev server. Single source of truth for the command shape — Metro\n// reads CI to DISABLE fast-refresh, so the canvas reconcile loop needs this\n// detected to keep HMR on (see `buildManagedCommandEnv`'s `metroFastRefresh`),\n// and the start-framework + port-injection paths key off the same shape. The\n// `\\s+` between the words rules out lookalikes like `export start` / `expo-doctor`.\n// Exported for unit testing (regression guard — a silent regex regression here\n// would re-disable Expo HMR in local dev with the env test still green).\nexport function isExpoStartCommand(command: string): boolean {\n return /\\bexpo\\s+start\\b/.test(command);\n}\n\n// True when we know how to rewrite `command` to bind the dev server to a\n// specific port. Matches PM-wrapped dev scripts (`npm run dev`, `pnpm dev`,\n// `yarn dev`, `bun run dev`) plus the direct framework binaries we scaffold\n// (`next dev`, `vite`, `expo start`). Anything else (a bare `node server.js`, a\n// custom launcher) is left untouched and keeps whatever port it defaults to.\nexport function canInjectDevServerPort(command: string): boolean {\n return (\n /\\b(?:npm|pnpm|bun)\\s+(?:run\\s+)?dev\\b/.test(command) ||\n /\\byarn\\s+dev\\b/.test(command) ||\n /\\bnext\\s+dev\\b/.test(command) ||\n /\\bvite\\b/.test(command) ||\n isExpoStartCommand(command)\n );\n}\n\nexport function shouldRetryWithBundledNode(options: {\n command: string;\n hintClass: DevServerHint[\"class\"] | null;\n bundledNodeAvailable: boolean;\n bundledNodeAlreadyActive: boolean;\n}): boolean {\n return (\n options.hintClass === \"node-version-unsupported\" &&\n canInjectDevServerPort(options.command) &&\n options.bundledNodeAvailable &&\n !options.bundledNodeAlreadyActive\n );\n}\n\n// Rewrite `command` to bind the dev server to `port`. Each framework's CLI has\n// its own host/port flag dialect:\n// - next: `-H 127.0.0.1 -p <port>`\n// - vite: `--host 127.0.0.1 --port <port>` (also the form for a generic\n// PM-wrapped `npm run dev` whose underlying binary we can't see)\n// - expo: `--host localhost --port <port>` — Expo's `--host` takes a MODE\n// keyword (localhost|lan|tunnel), NOT an IP, so `localhost` is what\n// pins it to the loopback the canvas probes for. Leaving Expo on its\n// default host risks a LAN-IP URL the readiness check would reject.\nexport function injectDevServerPort(\n command: string,\n port: number,\n framework: DevServerStartFramework,\n): string {\n const args =\n framework === \"next\"\n ? `-H 127.0.0.1 -p ${port}`\n : framework === \"expo\"\n ? `--host localhost --port ${port}`\n : `--host 127.0.0.1 --port ${port}`;\n // Direct binary invocations take the flags straight; a PM wrapper\n // (`npm run dev`) needs an explicit `--` so they reach the script, not the PM.\n if (/\\bnext\\s+dev\\b|\\bvite\\b|\\bexpo\\s+start\\b/.test(command)) {\n return `${command} ${args}`;\n }\n return command.includes(\" -- \")\n ? `${command} ${args}`\n : `${command} -- ${args}`;\n}\n\nfunction shouldRetryPortCollision(\n result: DevServerReadyResult,\n processRef: TempoHostProcess,\n): boolean {\n if (result.ok) {\n return false;\n }\n if (processRef.getLastHint()?.class === \"port-in-use\") {\n return true;\n }\n return /EADDRINUSE|address already in use/i.test(result.message ?? \"\");\n}\n\n/**\n * Map a `waitUntilReady` result to the diagnostic fields of a\n * `HostStartAttemptEntry` (outcome + parsed exit code/signal + a short detail).\n * Pure + exported for unit testing — the exit-code/signal are parsed from the\n * `ProcessCrashedError` message shape `\"… exited before readiness (code N)\"` /\n * `\"(signal X)\"`.\n */\nexport function mapResultToAttemptOutcome(result: DevServerReadyResult): {\n outcome: HostStartAttemptEntry[\"outcome\"];\n exitCode: number | null;\n signal: string | null;\n detail: string;\n} {\n const detail = (result.url ?? result.message ?? \"\").slice(0, 200);\n if (result.ok && result.url) {\n return { outcome: \"ready\", exitCode: null, signal: null, detail };\n }\n if (result.crashed) {\n const codeMatch = /\\(code (\\d+)\\)/i.exec(result.message ?? \"\");\n const signalMatch = /\\(signal ([A-Z0-9]+)\\)/i.exec(result.message ?? \"\");\n return {\n outcome: \"crashed\",\n exitCode: codeMatch ? Number(codeMatch[1]) : null,\n signal: signalMatch ? signalMatch[1] : null,\n detail,\n };\n }\n if (result.probeBlocked) {\n return { outcome: \"probe-blocked\", exitCode: null, signal: null, detail };\n }\n if (result.wrongResponder) {\n return { outcome: \"wrong-responder\", exitCode: null, signal: null, detail };\n }\n if (result.timedOut) {\n return { outcome: \"timed-out\", exitCode: null, signal: null, detail };\n }\n return { outcome: \"not-ready\", exitCode: null, signal: null, detail };\n}\n\nfunction portFromReadyUrl(url: string | null | undefined): number | null {\n if (!url) return null;\n const match = /:(\\d+)(?:\\/|$)/.exec(url);\n return match ? Number(match[1]) : null;\n}\n\n/**\n * Filesystem syscalls a lock holder can block. A package manager reifying a\n * tree performs all of these against files a running dev server may have open\n * — most notably `copyfile` of a loaded native addon\n * (`lightningcss.win32-x64-msvc.node`), which Windows refuses outright while\n * the image is mapped.\n */\nconst INSTALL_LOCK_SYSCALLS = \"unlink|rename|rmdir|copyfile\";\n\n/**\n * `[\\s\\S]` rather than `.`, because `.` does not match a newline and npm\n * prints the errno and the syscall on SEPARATE lines:\n *\n * ```\n * npm error code EBUSY\n * npm error syscall copyfile\n * ```\n *\n * With `.*?` that alternation could never fire on real npm output — the\n * customer's paste in TEM-1349 only classified at all because\n * `resource busy or locked` happens to appear inline further down. A bare\n * multi-line EPERM (the common shape) fell through unclassified and dumped the\n * raw log at the user. Bounded to keep the scan linear on a 4k stderr tail.\n */\nconst INSTALL_LOCK_PATTERN = new RegExp(\n `(?:EPERM|EACCES|EBUSY)[\\\\s\\\\S]{0,400}?(?:${INSTALL_LOCK_SYSCALLS})|(?:${INSTALL_LOCK_SYSCALLS})[\\\\s\\\\S]{0,400}?(?:EPERM|EACCES|EBUSY)|operation not permitted|permission denied|resource busy or locked`,\n \"i\",\n);\n\n/**\n * npm's fingerprint for a `node_modules` tree the install could not reify\n * cleanly. The full shape, from two installs racing in the same folder\n * (diagnostic t175n51yhjbemx3dxvf2a7n4j18dq9dy: Tempo's dependency\n * preparation overlapped an agent-run `npm install` in `tempo/`):\n *\n * ```\n * npm warn tar TAR_ENTRY_ERROR ENOENT: no such file or directory, open '…'\n * npm error code ENOTEMPTY\n * npm error syscall rename\n * npm error ENOTEMPTY: directory not empty, rename '…' -> '…'\n * ```\n *\n * The two markers are NOT equal evidence. `TAR_ENTRY_ERROR` (an extracted\n * entry's parent directory vanished mid-extract) only happens while something\n * else is rewriting the tree. `ENOTEMPTY` (rename over a populated directory)\n * also happens with nobody else around: npm's retire path is deterministic\n * (`.<name>-<sha1 prefix>` beside the package), its rename recovery only\n * handles `EEXIST`, and macOS/Linux report a populated target as `ENOTEMPTY`\n * — so one install killed mid-reify leaves a leftover every later install\n * trips on. `ENOENT` alone is deliberately no signal at all: npm prints it for\n * a missing `package.json` or a bad path too. Exported for unit testing.\n */\nexport function classifyInterruptedInstallOutput(\n output: string,\n): InstallInterruptedEvidence | null {\n if (/\\bTAR_ENTRY_ERROR\\b/.test(output)) return \"concurrent-install\";\n if (/\\bENOTEMPTY\\b/.test(output)) return \"inconsistent-tree\";\n return null;\n}\n\n/**\n * Classify a package-manager install failure's captured output into a typed\n * devserver error, or null when it doesn't match a known signature. Exported\n * for unit testing (and reused by the Electron adapter's canvas dependency\n * preparation, so the preflight install and the supervisor's own install\n * report the same typed codes).\n *\n * A locked file and a permission error are reported as DIFFERENT codes\n * (TEM-1349): `install_file_locked` means something has the file open and the\n * remedy is to close it; `install_permission_denied` means the tree is\n * genuinely unwritable. Telling a Windows user whose addon was mapped by our\n * own Vite child that they have a \"file permission error\" sent them auditing\n * folder permissions for a problem that was never about permissions.\n */\nexport function classifyInstallFailure(\n command: string,\n output: string,\n): Error | null {\n // FIRST: the shell never got as far as running the command. Everything below\n // interprets package-manager output, which cannot exist when cmd.exe aborted\n // while parsing a PowerShell-shaped command. See TEM-436.\n if (isShellParseAbortOutput(output)) {\n return new InstallShellDialectMismatchError(command, output);\n }\n if (\n /ERESOLVE|unable to resolve dependency tree|could not resolve/i.test(output)\n ) {\n return new InstallDependencyConflictError(command, output);\n }\n const interruptedEvidence = classifyInterruptedInstallOutput(output);\n if (interruptedEvidence) {\n return new InstallInterruptedError(command, output, interruptedEvidence);\n }\n // EBUSY is unambiguously \"someone holds this open\" — check it before the\n // permission branch, whose `operation not permitted` alternative would\n // otherwise swallow a Windows EBUSY that also prints that phrase.\n if (/\\bEBUSY\\b|resource busy or locked/i.test(output)) {\n return new InstallFileLockedError(command, output);\n }\n if (INSTALL_LOCK_PATTERN.test(output)) {\n return new InstallPermissionDeniedError(command, output);\n }\n const connectivity = describeInstallConnectivity(output);\n if (connectivity)\n return new InstallNetworkError(command, output, connectivity);\n return null;\n}\n\n/**\n * pnpm v10 does not run dependency build scripts by default (security), and\n * under `CI` — which {@link buildManagedCommandEnv} sets to dodge\n * `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY` — it promotes the resulting\n * \"Ignored build scripts\" notice to a FATAL `ERR_PNPM_IGNORED_BUILDS` exit.\n * But that error only fires AFTER the dependency tree has resolved and linked:\n * the deps ARE installed; only their (unneeded-by-the-canvas-host) postinstall\n * scripts were skipped. So this specific failure, paired with a populated\n * `node_modules`, is a successful install — not a devserver error to crash-loop\n * the canvas on. Exported for unit testing.\n */\nexport function isIgnoredBuildScriptsOnlyFailure(output: string): boolean {\n return /ERR_PNPM_IGNORED_BUILDS/.test(output);\n}\n\n/**\n * Backoff schedule for retrying an install command whose failure output\n * carries a transient file-lock signature. On Windows, `stop()` on the\n * previous devserver child resolves after its 6s-graceful + 2s-forced wait\n * whether or not the process actually died, so the first install attempt can\n * race a zombie child still holding a native addon open (TEM-1349 — npm\n * copyfile EBUSY on lightningcss's .node). The lock clears when the child\n * finally exits; this schedule spans that tail.\n */\nconst INSTALL_LOCK_RETRY_DELAYS_MS = [1_000, 3_000, 6_000] as const;\n\n/** Run an install command, retrying transient file-lock failures. Exported for unit testing. */\nexport async function runInstallCommandWithLockRetry(\n command: string,\n cwd: string,\n runCommandFn: (\n command: string,\n cwd: string,\n signal?: AbortSignal,\n ) => Promise<void> = runShellCommand,\n retryDelaysMs: readonly number[] = INSTALL_LOCK_RETRY_DELAYS_MS,\n signal?: AbortSignal,\n): Promise<void> {\n for (let attempt = 0; ; attempt += 1) {\n throwIfLaunchAborted(signal);\n try {\n await runCommandFn(command, cwd, signal);\n return;\n } catch (error) {\n // Timeout is terminal even if the retained tail mentions an earlier\n // file lock. Retrying would spend another full inactivity budget.\n if (error instanceof InstallTimeoutError) throw error;\n const output = error instanceof Error ? error.message : String(error);\n const delay = retryDelaysMs[attempt];\n if (delay === undefined || !isTransientFsLockOutput(output)) throw error;\n devLog(\"supervisor:install_lock_retry\", { command, attempt, delay });\n await waitForLaunchDelay(delay, signal);\n }\n }\n}\n\nasync function runInstallStep(\n step: InstallStep,\n signal?: AbortSignal,\n onOutput?: (data: string) => void,\n): Promise<void> {\n throwIfLaunchAborted(signal);\n // Invalidate BEFORE the package manager mutates anything. Keeping an old\n // receipt until success lets a cancelled forced reinstall look complete\n // after a restart, even when its dependency fingerprint hasn't changed.\n await Promise.all(\n [\n ...new Set([\n ...(step.markerRoots ?? []),\n ...(step.externalLayoutMarkerRoots ?? []),\n ]),\n ].map(invalidateInstallCompletionMarkers),\n );\n throwIfLaunchAborted(signal);\n if (step.cleanBeforeInstall) {\n // Wipe `node_modules` so the install resolves a complete tree from scratch\n // instead of topping-up a possibly-partial one. Best-effort: a failed wipe\n // just degrades to an incremental install. See TEM-820.\n await rm(path.join(step.cwd, \"node_modules\"), {\n recursive: true,\n force: true,\n }).catch((error) => {\n console.warn(\n `[tempo-devserver] ${step.label}: failed to clean node_modules before install:`,\n error,\n );\n });\n throwIfLaunchAborted(signal);\n }\n try {\n await runInstallCommandWithLockRetry(\n step.command,\n step.cwd,\n (command, cwd, signal) => runShellCommand(command, cwd, signal, onOutput),\n INSTALL_LOCK_RETRY_DELAYS_MS,\n signal,\n );\n } catch (error) {\n throwIfLaunchAborted(signal);\n if (error instanceof InstallTimeoutError) throw error;\n const output = error instanceof Error ? error.message : String(error);\n if (\n isIgnoredBuildScriptsOnlyFailure(output) &&\n (await nodeModulesHasMeaningfulEntries(step.cwd))\n ) {\n console.warn(\n `[tempo-devserver] ${step.label}: pnpm skipped dependency build scripts (ERR_PNPM_IGNORED_BUILDS) but node_modules is populated — treating as a successful install.`,\n );\n await Promise.all(\n (step.markerRoots ?? []).map(writeInstallCompletionMarker),\n );\n await Promise.all(\n (step.externalLayoutMarkerRoots ?? []).map(\n writeExternalLayoutInstallMarker,\n ),\n );\n return;\n }\n throw classifyInstallFailure(step.command, output) ?? error;\n }\n // Install command exited 0 — record completion so the next start can trust\n // this tree instead of reinstalling (and, crucially, so a NON-completing\n // install never gets here and never writes the marker). See TEM-820.\n throwIfLaunchAborted(signal);\n await Promise.all((step.markerRoots ?? []).map(writeInstallCompletionMarker));\n await Promise.all(\n (step.externalLayoutMarkerRoots ?? []).map(\n writeExternalLayoutInstallMarker,\n ),\n );\n}\n\nasync function sleepForStatusRetry(delayMs: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n}\n\nasync function writeStatusTextBestEffort(\n statusPath: string,\n text: string,\n): Promise<void> {\n await mkdir(path.dirname(statusPath), { recursive: true });\n const delays = [25, 75, 150];\n let lastError: unknown = null;\n for (let attempt = 0; attempt <= delays.length; attempt += 1) {\n try {\n await atomicWriteText(statusPath, text);\n return;\n } catch (error) {\n lastError = error;\n if (attempt < delays.length) {\n await sleepForStatusRetry(delays[attempt]);\n }\n }\n }\n\n try {\n await writeFile(statusPath, text, \"utf-8\");\n return;\n } catch (error) {\n throw error ?? lastError;\n }\n}\n\n/**\n * Prepend the well-known Windows install locations of the managed toolchain\n * (nodejs, npm-global, pnpm standalone, bun, volta) to PATH. GUI-launched\n * Electron processes inherit a login PATH that misses per-user installer\n * locations like `%LOCALAPPDATA%\\pnpm`, so without this a pnpm the user DOES\n * have installed is invisible to package-manager spawns. No-op off Windows.\n * Shared with tempo-project's install-command resolution (TEM-1492).\n */\nexport function augmentManagedPath(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n if (process.platform !== \"win32\") {\n return env;\n }\n\n const candidates = [\n process.env.ProgramFiles\n ? path.join(process.env.ProgramFiles, \"nodejs\")\n : null,\n process.env.APPDATA ? path.join(process.env.APPDATA, \"npm\") : null,\n process.env.LOCALAPPDATA\n ? path.join(process.env.LOCALAPPDATA, \"pnpm\")\n : null,\n process.env.USERPROFILE\n ? path.join(process.env.USERPROFILE, \".bun\", \"bin\")\n : null,\n process.env.USERPROFILE\n ? path.join(process.env.USERPROFILE, \".volta\", \"bin\")\n : null,\n ].filter((value): value is string => Boolean(value));\n\n const pathKey = Object.keys(env).find((key) => key.toLowerCase() === \"path\");\n const existingPath = pathKey ? env[pathKey] : env.PATH;\n const nextPath = [...candidates, existingPath]\n .filter(Boolean)\n .join(path.delimiter);\n return {\n ...env,\n [pathKey ?? \"PATH\"]: nextPath,\n };\n}\n\n// Exported for unit testing (regression guard for the Expo Metro fast-refresh\n// CI carve-out). Not part of the public devserver API.\nexport function buildManagedCommandEnv(\n options: {\n metroFastRefresh?: boolean;\n skipBundledNode?: boolean;\n /**\n * Project Node override for this tree (see\n * resolveProjectNodeOverrideForDir / project-node-version.ts, TEM-1536):\n * its binDir is prepended LAST, so it wins over both the inherited PATH\n * and the bundled-Node shims. `bundled: true` routes through\n * applyBundledNodeEnv so the shimmed Electron binary actually runs as\n * Node. `nodenvVersion` is set for nodenv-sourced overrides: a nodenv\n * user's `pnpm`/`npm` usually resolve through `~/.nodenv/shims`, and a\n * shim picks its Node from `NODENV_VERSION` — not from PATH — so the\n * prepended bin dir alone would not re-route the package manager itself.\n */\n projectNode?: {\n binDir: string;\n bundled: boolean;\n nodenvVersion?: string | null;\n } | null;\n } = {},\n): NodeJS.ProcessEnv {\n const base: NodeJS.ProcessEnv = {\n ...process.env,\n // The Tempo host runs a Vite dev server that must serve React in development\n // mode. Without this, Vite inherits the parent's NODE_ENV (often \"production\"\n // in Electron or \"test\" in Playwright), which causes React to run in\n // production mode and skip attaching fiber keys to DOM elements — breaking\n // fiber-based features like element selection on component storyboards.\n NODE_ENV: \"development\",\n // Signals to the tempoVitePlugin Vite plugin that it should be active.\n // The plugin is a no-op without this, so users can keep it in their\n // vite config permanently without affecting normal dev runs.\n //\n // Injected here rather than inlined in the generated dev/start scripts\n // (e.g. `TEMPO=true next dev`): a leading `VAR=value` token is a POSIX\n // shell-ism that Windows cmd.exe parses as a program name, exiting 1 and\n // crash-looping the canvas host. Keeping every env var in the spawn env\n // makes the scripts cross-platform (the Vite host has always relied on it).\n TEMPO: \"true\",\n // Suppresses Expo CLI's auto-open of a system browser tab (it would render\n // App.tsx's non-`/tempo-host` fallback — visible noise that reads as a\n // broken canvas; the host is embedded in an iframe at `/tempo-host`).\n // Harmless for Vite/Next, which ignore BROWSER.\n BROWSER: \"none\",\n // When `pnpm`/`yarn` aren't on PATH we shell out via `corepack <pm>`\n // (see resolvePackageManagerCommand). With no pinned `packageManager`\n // field, corepack resolves the LATEST package-manager release and verifies\n // its download signature against corepack's BUNDLED signing keys. On any\n // machine whose corepack predates npm's key rotation, that check throws\n // `Error: Cannot find matching keyid` and the canvas install dies before a\n // single dependency is fetched — an install failure the user can't act on\n // (their Node is fine). Setting COREPACK_INTEGRITY_KEYS=0 skips the\n // package-manager-BINARY signature check only; the user's dependencies\n // still resolve through the registry with their own lockfile integrity\n // hashes, so this is safe for the managed canvas sandbox.\n COREPACK_INTEGRITY_KEYS: \"0\",\n };\n\n // Mark this as a non-interactive run so install tools that branch on\n // TTY presence take their non-interactive path:\n // - pnpm: required when `--frozen-lockfile` needs to remove a stale\n // `node_modules` directory. Without CI=true / CI=1, pnpm errors out\n // with `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY` since Electron\n // subprocesses don't have a TTY. The pnpm error message itself\n // instructs users to set CI=true.\n // - npm: suppresses the \"found N vulnerabilities\" interactive banner\n // and a few other progress UIs.\n // - yarn: opts out of interactive prompts (e.g. yarn 2+ `init`).\n // Harmless for Vite/Next dev servers (they ignore CI). But EXPO's Metro\n // reads CI to DISABLE fast-refresh + file-watching (\"Metro is running in CI\n // mode, reloads are disabled\"). The canvas reconcile loop depends on HMR\n // re-rendering the storyboard iframe (source write → fast-refresh → React\n // re-render → fiber commit → combined capture → surface `on_reconcile` →\n // optimistic preview drop). With CI=1 that chain never fires on Expo, so\n // canvas edits never settle to canonical source AND undo never visually\n // reverts (the source reverts on disk but the stale optimistic `el.style`\n // mutation persists). So for the Expo dev-server START — which is NOT an\n // install step, so the pnpm/TTY reason doesn't apply — we do NOT add CI.\n // We only skip ADDING it; we never delete an ambient CI (CI runners / e2e\n // intentionally keep Metro in CI mode), so this only enables HMR in real\n // local dev where CI is otherwise unset.\n if (!options.metroFastRefresh) {\n base.CI = \"1\";\n }\n\n const nextEnv: NodeJS.ProcessEnv = augmentManagedPath(base);\n\n for (const key of Object.keys(nextEnv)) {\n const normalizedKey = key.toLowerCase();\n if (\n normalizedKey.startsWith(\"npm_\") ||\n normalizedKey.startsWith(\"pnpm_\") ||\n key === \"INIT_CWD\" ||\n key === \"NODE_PATH\"\n ) {\n delete nextEnv[key];\n }\n }\n\n // When the user's system Node is unusable we fall back to the host's bundled\n // Node toolchain (see selectNodeRuntimeForStart / bundled-node.ts): prepend\n // its shim dir to PATH and flip Electron into Node mode.\n //\n // skipBundledNode builds the SYSTEM-Node probe env instead: not only is the\n // prepend skipped, the shim dir the host APPENDED to the global PATH (the\n // last-resort fallback for shells) is stripped — otherwise a machine with no\n // Node at all would resolve the shim in the probe and report it as the\n // system Node, masking \"node missing\" and preventing activation.\n const bundledBinDir = getBundledNodeBinDir();\n if (options.skipBundledNode) {\n return bundledBinDir\n ? removeDirFromPathEnv(nextEnv, bundledBinDir)\n : nextEnv;\n }\n const withBundled = isBundledNodeActivated()\n ? applyBundledNodeEnv(nextEnv, bundledBinDir)\n : nextEnv;\n\n if (options.projectNode) {\n const withProjectNode = options.projectNode.bundled\n ? applyBundledNodeEnv(withBundled, options.projectNode.binDir)\n : prependDirToPathEnv(withBundled, options.projectNode.binDir);\n return options.projectNode.nodenvVersion\n ? {\n ...withProjectNode,\n NODENV_VERSION: options.projectNode.nodenvVersion,\n }\n : withProjectNode;\n }\n return withBundled;\n}\n\nfunction spawnManagedLongRunningCommand(options: {\n command: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n /** When set, launch through the generated watchdog script instead of\n * directly: the watchdog runs the command and kills its tree the moment\n * this process's end of the watchdog's stdin pipe closes — i.e. when the\n * Tempo app dies, however it dies. See devserver-watchdog-source.ts. */\n watchdogScriptPath?: string | null;\n}): ChildProcess {\n if (options.watchdogScriptPath) {\n return spawn(\n process.execPath,\n [options.watchdogScriptPath, options.command],\n {\n cwd: options.cwd,\n detached: process.platform !== \"win32\",\n // stdin MUST be a pipe — it is the watchdog's parent-death signal.\n // stdout/stderr carry the devserver's inherited output exactly as\n // the direct spawn would.\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n windowsHide: true,\n // In the packaged app process.execPath is Electron; RUN_AS_NODE\n // makes it behave as plain Node for the watchdog script. Harmless\n // when execPath already is Node (tests, dev harnesses).\n env: {\n ...options.env,\n TEMPO_WATCHDOG_CHILD_ELECTRON_RUN_AS_NODE:\n options.env.ELECTRON_RUN_AS_NODE ?? \"__TEMPO_UNSET__\",\n ELECTRON_RUN_AS_NODE: \"1\",\n },\n },\n );\n }\n return spawn(options.command, {\n cwd: options.cwd,\n shell: true,\n detached: process.platform !== \"win32\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n // Electron main is a GUI-subsystem process with no console, so without\n // CREATE_NO_WINDOW every shell child pops a visible cmd.exe window on\n // Windows. FTUE users Ctrl+C / close that mystery window, killing the\n // devserver with 0xC000013A in a restart loop (see\n // isWindowsTeardownExitCode). No-op off Windows.\n windowsHide: true,\n env: options.env,\n });\n}\n\n/**\n * Run `taskkill` and report how it settled: the exit code (0 = killed,\n * 128 = no such process, 1 = access denied are the common ones), or `null`\n * when taskkill itself couldn't spawn. Callers record this in the attempt\n * ledger — a reap that silently failed is indistinguishable from one that\n * never ran otherwise (the t17cesvbq… bundle blind spot).\n */\nasync function runTaskkill(\n pid: number,\n force: boolean,\n): Promise<number | null> {\n return await new Promise<number | null>((resolvePromise) => {\n const args = [\"/pid\", String(pid), \"/t\"];\n if (force) {\n args.push(\"/f\");\n }\n\n const child = spawn(\"taskkill\", args, {\n stdio: \"ignore\",\n windowsHide: true,\n });\n\n child.on(\"error\", () => {\n resolvePromise(null);\n });\n child.on(\"exit\", (code) => {\n resolvePromise(code);\n });\n });\n}\n\n/**\n * Parse a whitespace/newline-separated list of PIDs (e.g. `lsof -t` or `pgrep`\n * output) into a deduped list of positive integers, excluding our own PID.\n * Pure — exported for unit testing the discovery layer without spawning.\n */\nexport function parsePidList(output: string, selfPid: number): number[] {\n const pids = new Set<number>();\n for (const token of output.split(/\\s+/)) {\n const n = Number(token.trim());\n if (Number.isInteger(n) && n > 0 && n !== selfPid) {\n pids.add(n);\n }\n }\n return [...pids];\n}\n\n/** Run a bounded process query used only for recorded process ownership. */\nasync function runProcessQuery(bin: string, args: string[]): Promise<string> {\n if (process.platform === \"win32\") {\n return \"\";\n }\n return await new Promise<string>((resolvePromise) => {\n let settled = false;\n const finish = (out: string): void => {\n if (!settled) {\n settled = true;\n resolvePromise(out);\n }\n };\n try {\n const child = spawn(bin, args, {\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n windowsHide: true,\n });\n let out = \"\";\n child.stdout?.on(\"data\", (chunk) => {\n out += String(chunk);\n });\n child.on(\"error\", () => finish(\"\"));\n child.on(\"exit\", () => finish(out));\n // Hard cap so a hung discovery never blocks a devserver start. Callers run\n // these in parallel, so the whole discovery layer is bounded by this.\n setTimeout(() => {\n try {\n child.kill(\"SIGKILL\");\n } catch {}\n finish(out);\n }, 2_500);\n } catch {\n finish(\"\");\n }\n });\n}\n\n/** Stable-enough process birth identity used to reject recycled PIDs. */\nexport async function readProcessStartedAt(\n pid: number,\n): Promise<string | null> {\n return await readPidStartIdentity(pid);\n}\n\n/**\n * Resolve the full descendant PID subtree of `rootPid` (the detached shell we\n * spawned) — `npm`/`yarn` plus the real `next dev` worker. Persisted in the\n * process marker so a LATER session can reap the worker by PID even after the\n * shell parent has died and the worker reparented to init (the orphan case),\n * when lsof discovery is unavailable.\n */\nasync function resolveDescendantPids(rootPid: number): Promise<number[]> {\n if (process.platform === \"win32\") {\n return [];\n }\n const found = new Set<number>();\n const queue: number[] = [rootPid];\n // Bounded BFS — guards against a pathological cycle in malformed pgrep output.\n let guard = 0;\n while (queue.length > 0 && guard < 256) {\n guard += 1;\n const parent = queue.shift();\n if (parent == null) break;\n const out = await runProcessQuery(\"pgrep\", [\"-P\", String(parent)]);\n for (const child of parsePidList(out, process.pid)) {\n if (!found.has(child)) {\n found.add(child);\n queue.push(child);\n }\n }\n }\n found.delete(rootPid);\n return [...found];\n}\n\n/**\n * Kill a process tree by root pid. Returns the `taskkill` exit code on\n * win32 (see runTaskkill), or `null` on POSIX / when the pid was already\n * dead — the POSIX kills are syscalls with no exit code to report.\n */\nasync function killProcessTreeByPid(pid: number): Promise<number | null> {\n if (!isPidAlive(pid)) {\n return null;\n }\n\n if (process.platform === \"win32\") {\n const taskkillExitCode = await runTaskkill(pid, true);\n // taskkill's /T tree-walk has known failure modes on reparented orphans.\n // Verify the root actually died and finish the job with a direct\n // TerminateProcess (same mechanism as `Stop-Process -Force`, the remedy\n // that demonstrably works on these orphans in the field — TEM-1491).\n if (!(await waitForPidGone(pid, 1_500))) {\n devLog(\"supervisor:taskkill_survivor_direct_kill\", { pid });\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {}\n }\n return taskkillExitCode;\n }\n\n try {\n process.kill(-pid, \"SIGKILL\");\n return null;\n } catch {}\n\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {}\n return null;\n}\n\nasync function terminateProcessTreeByPid(\n pid: number,\n gracefulShutdownMs: number,\n forcedShutdownMs: number,\n): Promise<void> {\n if (!isPidAlive(pid)) return;\n if (process.platform === \"win32\") {\n await killProcessTreeByPid(pid);\n await waitForPidGone(pid, forcedShutdownMs);\n return;\n }\n try {\n process.kill(-pid, \"SIGTERM\");\n } catch {\n try {\n process.kill(pid, \"SIGTERM\");\n } catch {}\n }\n if (await waitForPidGone(pid, gracefulShutdownMs)) return;\n await killProcessTreeByPid(pid);\n await waitForPidGone(pid, forcedShutdownMs);\n}\n\nasync function signalManagedLongRunningProcess(\n processRef: ChildProcess,\n signal: NodeJS.Signals,\n): Promise<void> {\n const pid = processRef.pid;\n if (!pid) {\n return;\n }\n\n if (process.platform === \"win32\") {\n await runTaskkill(pid, signal === \"SIGKILL\");\n return;\n }\n\n try {\n process.kill(-pid, signal);\n return;\n } catch {}\n\n try {\n processRef.kill(signal);\n } catch {}\n}\n\nfunction forceKillManagedLongRunningProcess(\n processRef: ChildProcess,\n hasParentDeathWatchdog: boolean,\n): void {\n const pid = processRef.pid;\n if (!pid || processRef.exitCode !== null || processRef.signalCode !== null) {\n return;\n }\n\n if (process.platform === \"win32\") {\n try {\n const child = spawn(\"taskkill\", [\"/pid\", String(pid), \"/t\", \"/f\"], {\n stdio: \"ignore\",\n windowsHide: true,\n detached: true,\n });\n child.unref();\n } catch {}\n return;\n }\n\n if (hasParentDeathWatchdog) {\n // The watchdog's child owns a DIFFERENT POSIX process group. SIGKILLing\n // the watchdog group would bypass its handler and orphan that child. A\n // TERM/pipe close makes the watchdog synchronously SIGKILL its child group.\n // destroy(), not end(): the SIGTERM right below tears the pipe down while\n // an end()-filed uv_shutdown could still be in flight — the ELECTRON-2NS\n // native UAF shape. destroy() closes the pipe without a shutdown request;\n // the watchdog sees EOF either way.\n try {\n processRef.stdin?.destroy();\n } catch {}\n try {\n process.kill(-pid, \"SIGTERM\");\n return;\n } catch {}\n }\n\n try {\n process.kill(-pid, \"SIGKILL\");\n return;\n } catch {}\n\n try {\n processRef.kill(\"SIGKILL\");\n } catch {}\n}\n\nasync function terminateManagedLongRunningProcess(\n processRef: ChildProcess,\n options: {\n gracefulShutdownMs: number;\n forcedShutdownMs: number;\n },\n): Promise<boolean> {\n const rootPid = processRef.pid;\n if (!rootPid) return true;\n const processTreePids = [\n rootPid,\n ...(await resolveDescendantPids(rootPid).catch(() => [])),\n ];\n if (!processTreePids.some(isPidAlive)) return true;\n\n await signalManagedLongRunningProcess(processRef, \"SIGTERM\");\n if (\n await waitForProcessTreePidsGone(\n processTreePids,\n options.gracefulShutdownMs,\n )\n ) {\n return true;\n }\n\n await signalManagedLongRunningProcess(processRef, \"SIGKILL\");\n if (process.platform === \"win32\") {\n // taskkill /T can lose descendants when the shell exits first. The\n // pre-TERM snapshot is still session-owned evidence, so finish any\n // survivors directly rather than discovering new listener PIDs.\n for (const pid of processTreePids) {\n if (!isPidAlive(pid)) continue;\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {}\n }\n }\n return await waitForProcessTreePidsGone(\n processTreePids,\n options.forcedShutdownMs,\n );\n}\n\nasync function waitForProcessTreePidsGone(\n pids: readonly number[],\n timeoutMs: number,\n): Promise<boolean> {\n const startedAt = Date.now();\n while (Date.now() - startedAt < timeoutMs) {\n if (!pids.some(isPidAlive)) return true;\n await new Promise((resolvePromise) => setTimeout(resolvePromise, 50));\n }\n return !pids.some(isPidAlive);\n}\n\nfunction isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n // EPERM means the process EXISTS but is not signalable from here (POSIX\n // semantics; on Windows, OpenProcess was denied). Treating it as dead\n // made the orphan reaper skip live lock holders entirely (TEM-1491).\n return (error as NodeJS.ErrnoException)?.code === \"EPERM\";\n }\n}\n\nasync function waitForPidToExit(pid: number, timeoutMs: number): Promise<void> {\n await waitForPidGone(pid, timeoutMs);\n}\n\n/** Like {@link waitForPidToExit}, but reports whether the pid actually died. */\nasync function waitForPidGone(\n pid: number,\n timeoutMs: number,\n): Promise<boolean> {\n const startedAt = Date.now();\n while (Date.now() - startedAt < timeoutMs) {\n if (!isPidAlive(pid)) {\n return true;\n }\n await new Promise((resolvePromise) => setTimeout(resolvePromise, 50));\n }\n return !isPidAlive(pid);\n}\n\ninterface BoundedShellCommandResult {\n ok: boolean;\n timedOut: boolean;\n exitCode: number | null;\n output: string;\n}\n\nasync function runBoundedShellCommand(options: {\n command: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs: number;\n}): Promise<BoundedShellCommandResult> {\n const lifted = extractLeadingEnvAssignments(options.command);\n return await new Promise<BoundedShellCommandResult>((resolvePromise) => {\n let settled = false;\n let output = \"\";\n const recordOutput = (chunk: string | Buffer): void => {\n output = `${output}${chunk.toString()}`.slice(-4000);\n };\n const finish = (result: BoundedShellCommandResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolvePromise(result);\n };\n let child: ChildProcess;\n try {\n child = spawn(lifted.command, {\n cwd: options.cwd,\n shell: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n windowsHide: true,\n detached: process.platform !== \"win32\",\n env: { ...options.env, ...lifted.env },\n });\n } catch {\n return resolvePromise({\n ok: false,\n timedOut: false,\n exitCode: null,\n output,\n });\n }\n child.stdout?.on(\"data\", recordOutput);\n child.stderr?.on(\"data\", recordOutput);\n const timeout = setTimeout(() => {\n forceKillManagedLongRunningProcess(child, false);\n finish({\n ok: false,\n timedOut: true,\n exitCode: child.exitCode,\n output,\n });\n }, options.timeoutMs);\n child.on(\"error\", () =>\n finish({ ok: false, timedOut: false, exitCode: null, output }),\n );\n child.on(\"exit\", (code) =>\n finish({ ok: code === 0, timedOut: false, exitCode: code, output }),\n );\n });\n}\n\nasync function runShellCommand(\n command: string,\n cwd: string,\n signal?: AbortSignal,\n onOutput?: (data: string) => void,\n): Promise<void> {\n throwIfLaunchAborted(signal);\n // Reconcile the project's declared Node before spawning: a repo pinning\n // `engines.node` newer than the user's default Node makes pnpm refuse the\n // managed install/upgrade outright (ERR_PNPM_UNSUPPORTED_ENGINE, TEM-1536).\n const projectNode = toProjectNodeEnvOption(\n await resolveProjectNodeOverrideForDir(cwd),\n );\n // Lift leading `NAME=value` prefixes (e.g. `CI=1 npm install`) into the\n // spawn env — sh applies them itself, cmd.exe parses them as a program\n // name and fails before installing anything (TEM-436).\n const lifted = extractLeadingEnvAssignments(command);\n const result = await runAbortableShellCommand({\n command: lifted.command,\n cwd,\n signal,\n env: { ...buildManagedCommandEnv({ projectNode }), ...lifted.env },\n onOutput,\n idleTimeoutMs: INSTALL_IDLE_TIMEOUT_MS,\n });\n if (result.aborted) throw launchAbortError();\n if (result.timedOut) throw new InstallTimeoutError(command, result.output);\n if (result.spawnError) throw result.spawnError;\n if (result.exitCode === 0) return;\n const output = result.output.trim();\n throw new InstallFailedError(command, result.exitCode, output);\n}\n\n/**\n * Outcome of the managed `tempo-sdk` upgrade attempt. This upgrade is the\n * rollout mechanism for CanvasHost protocol bumps. The running host's served\n * identity remains authoritative; this result is diagnostic evidence when a\n * managed update could not converge the sidecar before spawn.\n */\ntype TempoSdkUpgradeResult = {\n /**\n * The upgrade path genuinely ran to a decision — either the update command\n * executed, or the registry confirmed there is nothing newer to install.\n * `false` when the update never ran: skipped (protected/pinned tree) OR\n * bailed before the command (registry unreachable). The distinction is\n * load-bearing for diagnostics: claiming \"Tempo could not update\" when no\n * update was ever run sends the user chasing a package-manager failure that\n * does not exist.\n */\n attempted: boolean;\n error?: string;\n /**\n * The package-manager-correct commands for this tree. Present even when the\n * upgrade is skipped, because the compatibility error still has to tell the\n * user what to run — handing a pnpm-workspace user an `npm install` line is\n * how this bug stayed invisible for two SDK floor bumps.\n */\n plan: TempoSdkUpgradePlan;\n};\n\n/**\n * Ensures the Tempo-owned package always records `tempo-sdk` as `\"latest\"`.\n * If the installed SDK is stale, updates it through the package manager that\n * owns this tree while preserving that package.json spec. Never install\n * `tempo-sdk@<resolved version>` here: the resolved version may get persisted\n * and make generated Tempo projects drift.\n * An explicit update command owns maintenance in custom environments. Its\n * result is verified by the served host identity instead of local files.\n *\n * Exported for tests: the honesty of the returned `attempted`/`error` pair is\n * pinned directly (TEM-1338).\n */\nexport async function ensureTempoSdkLatestSpec(\n cwd: string,\n signal?: AbortSignal,\n options: {\n updateCommand?: string;\n runCommand?: typeof runShellCommand;\n } = {},\n): Promise<TempoSdkUpgradeResult> {\n throwIfLaunchAborted(signal);\n const configuredUpdateCommand = options.updateCommand?.trim();\n const runCommandFn = options.runCommand ?? runShellCommand;\n // A per-attempt empty metadata cache. Without it a stale `latest` dist-tag\n // in the package manager's cache silently pins the SDK to an old version:\n // the upgrade command exits 0, reports success, and nothing moves. See\n // buildCommands for why this is the only lever that works on pnpm.\n const metadataCacheDir = configuredUpdateCommand\n ? null\n : await mkdtemp(path.join(os.tmpdir(), \"tempo-sdk-meta-\")).catch(\n () => null,\n );\n const cleanupMetadataCache = async () => {\n if (!metadataCacheDir) return;\n await rm(metadataCacheDir, { recursive: true, force: true }).catch(\n () => {},\n );\n };\n const nativePlan = await resolveTempoSdkUpgradePlan(cwd, {\n ...(metadataCacheDir ? { metadataCacheDir } : {}),\n }).catch(async (error) => {\n await cleanupMetadataCache();\n throw error;\n });\n const plan = configuredUpdateCommand\n ? {\n ...nativePlan,\n updateCommand: configuredUpdateCommand,\n remediationCommand: configuredUpdateCommand,\n }\n : nativePlan;\n if (signal?.aborted) {\n await cleanupMetadataCache();\n throw launchAbortError();\n }\n // Never mutate a protected tree (CI's own monorepo checkout). Writing\n // `tempo-sdk: latest` into its package.json and running `npm update tempo-sdk`\n // here re-links the workspace node_modules mid-test and crashes every\n // concurrent Playwright worker with `MODULE_NOT_FOUND: playwright-core/lib/utils`.\n // See protectedRootFor. No-op in production (env unset).\n const protectedRoot = protectedRootFor(cwd);\n if (protectedRoot) {\n devLog(\"supervisor:sdk_spec_skipped_protected\", { cwd, protectedRoot });\n await cleanupMetadataCache();\n return { attempted: false, plan };\n }\n const devSource = await prepareTempoSdkDevSource(cwd).catch(async (error) => {\n await cleanupMetadataCache();\n throw error;\n });\n if (signal?.aborted) {\n await cleanupMetadataCache();\n throw launchAbortError();\n }\n if (devSource.source === \"local\") {\n devLog(\"supervisor:sdk_source_local\", {\n cwd,\n localSdkRoot: devSource.localSdkRoot,\n });\n await cleanupMetadataCache();\n return { attempted: false, plan };\n }\n if (devSource.removedLocalLink) {\n devLog(\"supervisor:sdk_source_restoring_published\", { cwd });\n }\n // `attempted` must mean \"the update command actually started\", including on\n // the error path: a throw BEFORE the command (malformed registry JSON, an\n // unreadable manifest) is a did-not-run, not a failed update (TEM-1338).\n let updateCommandStarted = false;\n let ignoredBuildScriptsAfterLink = false;\n try {\n const packageJsonPath = path.join(cwd, \"package.json\");\n const ensureSpecLatest = async (): Promise<boolean | \"pinned-local\"> => {\n const raw = await readFile(packageJsonPath, \"utf-8\").catch(() => null);\n if (!raw) return false;\n\n const parsed = JSON.parse(raw) as {\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n };\n const dependencies =\n parsed.dependencies &&\n typeof parsed.dependencies === \"object\" &&\n !Array.isArray(parsed.dependencies)\n ? parsed.dependencies\n : undefined;\n const devDependencies =\n parsed.devDependencies &&\n typeof parsed.devDependencies === \"object\" &&\n !Array.isArray(parsed.devDependencies)\n ? parsed.devDependencies\n : undefined;\n\n let targetBlock: Record<string, unknown>;\n if (devDependencies?.[\"tempo-sdk\"] !== undefined) {\n targetBlock = devDependencies;\n } else if (dependencies?.[\"tempo-sdk\"] !== undefined) {\n targetBlock = dependencies;\n } else {\n parsed.devDependencies = devDependencies ?? {};\n targetBlock = parsed.devDependencies;\n }\n\n // A locally-linked SDK (`workspace:*` in our own monorepo, `file:` for\n // a packed-tarball install — the shape the packed release gate and any\n // local SDK development use) is a deliberate pin: rewriting it to\n // `latest` would sever the link and silently swap the artifact under\n // test for whatever the registry serves. Signal the caller to skip the\n // whole upgrade path.\n const currentSpec = targetBlock[\"tempo-sdk\"];\n if (\n typeof currentSpec === \"string\" &&\n (currentSpec.startsWith(\"workspace:\") ||\n currentSpec.startsWith(\"file:\"))\n ) {\n return \"pinned-local\";\n }\n if (currentSpec === \"latest\") {\n return false;\n }\n\n targetBlock[\"tempo-sdk\"] = \"latest\";\n await writeFile(packageJsonPath, `${JSON.stringify(parsed, null, 2)}\\n`);\n return true;\n };\n\n const specChanged = await ensureSpecLatest();\n if (specChanged === \"pinned-local\") {\n devLog(\"supervisor:sdk_spec_pinned_local\", { cwd });\n return { attempted: false, plan };\n }\n if (configuredUpdateCommand) {\n const dialectIssue = findPowerShellSyntax(configuredUpdateCommand);\n if (dialectIssue) {\n throw new InstallShellDialectMismatchError(\n configuredUpdateCommand,\n describePowerShellRejection(dialectIssue),\n );\n }\n // A custom launcher can install into a Docker volume or a remote tree.\n // Neither this machine's SDK version nor its registry access says\n // whether that environment needs an update (TEM-2278). Let its explicit\n // command own the update; /__tempo/identity still decides readiness.\n updateCommandStarted = true;\n await runCommandFn(plan.updateCommand, plan.cwd, signal);\n throwIfLaunchAborted(signal);\n await ensureSpecLatest();\n return { attempted: true, plan };\n }\n const nodeModulesPath = path.join(cwd, \"node_modules\");\n const installedPkgPath = path.join(\n nodeModulesPath,\n \"tempo-sdk\",\n \"package.json\",\n );\n const [installedRaw, registryResponse] = await Promise.all([\n readFile(installedPkgPath, \"utf-8\").catch(() => null),\n fetch(\"https://registry.npmjs.org/tempo-sdk/latest\", {\n signal: signal\n ? AbortSignal.any([signal, AbortSignal.timeout(3000)])\n : AbortSignal.timeout(3000),\n }).catch(() => null),\n ]);\n throwIfLaunchAborted(signal);\n\n // Registry failure ⇒ the update command never runs. Reporting\n // `attempted: true` here was the TEM-1338 honesty bug: the compatibility\n // error claimed \"Tempo could not update the managed Canvas package\" for\n // what was actually a skipped update with a knowable reason.\n if (!registryResponse?.ok) {\n return {\n attempted: false,\n error: `could not reach the npm registry to resolve the latest tempo-sdk (${\n registryResponse\n ? `HTTP ${registryResponse.status}`\n : \"network error/timeout\"\n })`,\n plan,\n };\n }\n const registryData = (await registryResponse.json()) as {\n version?: string;\n };\n const latestVersion = registryData.version;\n if (!latestVersion) {\n return {\n attempted: false,\n error: \"the npm registry returned no latest version for tempo-sdk\",\n plan,\n };\n }\n\n const installedVersion = installedRaw\n ? (JSON.parse(installedRaw) as { version?: string }).version\n : null;\n\n if (!specChanged && installedVersion === latestVersion) {\n // Nothing newer exists. If the floor check still fails after this, the\n // remediation command is useless — say so instead of \"could not update\".\n return {\n attempted: true,\n error: `installed tempo-sdk ${installedVersion} is already the newest published version`,\n plan,\n };\n }\n\n updateCommandStarted = true;\n try {\n await runCommandFn(plan.updateCommand, plan.cwd, signal);\n } catch (error) {\n const output = error instanceof Error ? error.message : String(error);\n if (\n !isIgnoredBuildScriptsOnlyFailure(output) ||\n !(await nodeModulesHasMeaningfulEntries(plan.cwd))\n ) {\n throw error;\n }\n // pnpm emits ERR_PNPM_IGNORED_BUILDS only after resolving and linking\n // the dependency tree. The published tempo-sdk ships a prebuilt `dist`\n // and has no install/postinstall script of its own, so continue to the\n // installed-version assertion below to prove the SDK really moved.\n ignoredBuildScriptsAfterLink = true;\n console.warn(\n \"[tempo-devserver] tempo-sdk update skipped dependency build scripts \" +\n \"(ERR_PNPM_IGNORED_BUILDS); verifying the installed SDK version.\",\n );\n }\n throwIfLaunchAborted(signal);\n await ensureSpecLatest();\n\n // Exit 0 is NOT proof the upgrade took. Every stale-metadata failure mode\n // observed in the wild — pnpm resolving `latest` from a cached packument,\n // an unknown flag that pnpm rejects while still exiting 0 — leaves the old\n // version on disk and reports success. Assert against the registry version\n // we already fetched above, so the failure surfaces as a real\n // `upgradeError` instead of a canvas that mysteriously stays broken.\n const upgradedRaw = await readFile(installedPkgPath, \"utf-8\").catch(\n () => null,\n );\n const upgradedVersion = upgradedRaw\n ? (JSON.parse(upgradedRaw) as { version?: string }).version\n : null;\n if (upgradedVersion !== latestVersion) {\n const commandOutcome = ignoredBuildScriptsAfterLink\n ? `linked dependencies, then reported ERR_PNPM_IGNORED_BUILDS, but tempo-sdk is still`\n : `reported success but tempo-sdk is still`;\n throw new Error(\n `\\`${plan.updateCommand}\\` ${commandOutcome} ` +\n `${upgradedVersion ?? \"not installed\"} (registry latest is ${latestVersion}). ` +\n `This is usually a stale ${plan.packageManager} registry metadata cache.`,\n );\n }\n return { attempted: true, plan };\n } catch (error) {\n // Non-fatal — a custom launcher may use a different dependency tree. The\n // running server's identity echo decides compatibility; retain this detail\n // in diagnostics so an SDK convergence failure is still explainable.\n const message = error instanceof Error ? error.message : String(error);\n devLog(\"supervisor:sdk_upgrade_failed\", {\n cwd: plan.cwd,\n command: plan.updateCommand,\n packageManager: plan.packageManager,\n updateCommandStarted,\n message,\n });\n return { attempted: updateCommandStarted, error: message, plan };\n } finally {\n await cleanupMetadataCache();\n }\n}\n\nasync function readJsonFile<T>(filePath: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(filePath, \"utf-8\")) as T;\n } catch {\n return null;\n }\n}\n\nasync function readTempoProcessMarker(\n markerPath: string,\n): Promise<{ pid: number; workerPids: number[] } | null> {\n const marker = await readJsonFile<{\n owner?: unknown;\n pid?: unknown;\n workerPids?: unknown;\n }>(markerPath);\n if (\n marker?.owner === \"tempo\" &&\n typeof marker.pid === \"number\" &&\n Number.isInteger(marker.pid) &&\n marker.pid > 0\n ) {\n const workerPids = Array.isArray(marker.workerPids)\n ? marker.workerPids.filter(\n (value): value is number =>\n typeof value === \"number\" && Number.isInteger(value) && value > 0,\n )\n : [];\n return { pid: marker.pid, workerPids };\n }\n return null;\n}\n\nconst LOCAL_DEV_SERVER_HOSTNAMES = new Set([\n \"127.0.0.1\",\n \"localhost\",\n \"::1\",\n \"[::1]\",\n \"0.0.0.0\",\n \"::\",\n]);\n\nfunction normalizeServerUrl(url: string): string | null {\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return null;\n }\n\n if (parsed.hostname === \"0.0.0.0\" || parsed.hostname === \"::\") {\n parsed.hostname = \"127.0.0.1\";\n }\n\n const normalizedPath = parsed.pathname.replace(/\\/+$/, \"\");\n return `${parsed.origin}${normalizedPath === \"/\" ? \"\" : normalizedPath}`;\n } catch {\n return null;\n }\n}\n\nfunction extractServerUrl(text: string): string | null {\n // Strip ANSI escape sequences so colored terminal output (e.g. Vite)\n // doesn't corrupt matched URLs and cause `new URL()` to throw.\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI ESC is the sequence being stripped.\n const stripped = text.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n const matches = stripped.match(/https?:\\/\\/[^\\s\"'`<>]+/gi);\n if (!matches || matches.length === 0) {\n return null;\n }\n\n for (const rawMatch of matches) {\n const cleaned = rawMatch.replace(/[),.;]+$/, \"\");\n const normalized = normalizeServerUrl(cleaned);\n if (!normalized) {\n continue;\n }\n\n try {\n const parsed = new URL(normalized);\n if (LOCAL_DEV_SERVER_HOSTNAMES.has(parsed.hostname.toLowerCase())) {\n return normalized;\n }\n } catch {\n // Ignore invalid candidates.\n }\n }\n\n return null;\n}\n\nfunction buildTempoHostUrl(baseUrl: string): string {\n const normalized = normalizeServerUrl(baseUrl);\n if (!normalized) {\n return `${baseUrl.replace(/\\/+$/, \"\")}${TEMPO_HOST_ROUTE}`;\n }\n\n const parsed = new URL(normalized);\n if (\n parsed.pathname === TEMPO_HOST_ROUTE ||\n parsed.pathname.startsWith(`${TEMPO_HOST_ROUTE}/`)\n ) {\n return normalized;\n }\n return `${parsed.origin}${TEMPO_HOST_ROUTE}`;\n}\n\nfunction stripTempoHostRoute(url: string): string {\n const normalized = normalizeServerUrl(url);\n if (!normalized) {\n return url.replace(/\\/tempo-host\\/?$/, \"\") || url;\n }\n\n const parsed = new URL(normalized);\n if (\n parsed.pathname === TEMPO_HOST_ROUTE ||\n parsed.pathname.startsWith(`${TEMPO_HOST_ROUTE}/`)\n ) {\n return parsed.origin;\n }\n\n return normalized;\n}\n\n// A real tempo-host serves an identifiable harness document at `/tempo-host`:\n// the static harness sets `<title>tempo-host</title>` and the injected Tempo\n// SDK adds a `<meta name=\"tempo-project-root\" …>` tag. A plain SPA — the\n// Electron renderer's own dev origin, or a *sibling* workspace's app server —\n// answers `/tempo-host` with its OWN index.html via history-fallback: 200 OK,\n// but with neither marker.\n//\n// Gating readiness/liveness on these markers instead of bare HTTP reachability\n// (`response.ok || status > 0`) is what stops the canvas binding to the WRONG\n// devserver. Without it a stale/colliding URL that merely answers 200 is\n// accepted, every storyboard iframe loads a full Tempo application instead of\n// an isolated component, and a handful of those recursively exhaust the main\n// process heap — the OOM this guards against. This is the primary identity\n// gate: framework-agnostic positive identification of the harness, so it stays\n// correct for any real tempo-host while rejecting every non-host responder.\n// `fetchIsTempoHost` then layers an optional workspace-root match on top to\n// also reject a *different* workspace's tempo-host that collided onto the port.\nfunction isTempoHostDocument(body: string): boolean {\n return (\n body.includes(\"tempo-project-root\") ||\n /<title>\\s*tempo-host\\s*<\\/title>/i.test(body)\n );\n}\n\n// The workspace root a tempo-host advertises via its injected\n// `<meta name=\"tempo-project-root\" content=\"…\">`, or null when the marker is\n// absent (older SDKs ship the title but not the meta).\nconst TEMPO_HOST_PROJECT_ROOT_META =\n /<meta\\b[^>]*\\bname=[\"']tempo-project-root[\"'][^>]*\\bcontent=[\"']([^\"']*)[\"']/i;\n\nfunction readAdvertisedProjectRoot(body: string): string | null {\n const match = TEMPO_HOST_PROJECT_ROOT_META.exec(body);\n return match ? match[1] : null;\n}\n\n// Diagnostic-only comparison for the root a host advertises. The\n// meta points at the framework's resolved root — the `tempo/` subdir for Vite,\n// the project dir for Next — so an exact string match is too strict; an\n// ancestor/descendant relationship is the useful comparison. Paths are realpath-\n// normalized; if normalization throws we fall back to a literal resolve rather\n// than reject, so a transient FS error can't strand an otherwise-valid host.\nfunction advertisedRootBelongsToWorkspace(\n advertised: string,\n expected: string,\n): boolean {\n const norm = (p: string): string => {\n try {\n return fs.realpathSync(p);\n } catch {\n return path.resolve(p);\n }\n };\n const a = norm(advertised);\n const e = norm(expected);\n if (a === e) return true;\n const aWithSep = a.endsWith(path.sep) ? a : `${a}${path.sep}`;\n const eWithSep = e.endsWith(path.sep) ? e : `${e}${path.sep}`;\n return a.startsWith(eWithSep) || e.startsWith(aWithSep);\n}\n\n// ── Readiness-probe transports ───────────────────────────────────────────────\n// A probe is only as trustworthy as its transport. TEM-873: on some Windows\n// machines a firewall / antivirus loopback filter blocks one HTTP client\n// stack while the dev server is perfectly healthy — the supervisor then\n// killed a good child on \"URL did not become reachable\". Probe through\n// MULTIPLE independent client stacks (undici `fetch`, node:http, plus any\n// host-registered extras such as Electron's Chromium `net`) and accept a\n// status/body response from ANY of them. Transports run in parallel; the first\n// non-null response wins.\nexport type ProbeResponse = {\n status: number;\n body: string;\n};\n\nexport type ProbeTransport = (\n url: string,\n timeoutMs: number,\n) => Promise<ProbeResponse | null>;\n\nconst MAX_PROBE_RESPONSE_BYTES = 64 * 1024;\n\nasync function readBoundedFetchBody(response: Response): Promise<string> {\n if (!response.body) return \"\";\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let remaining = MAX_PROBE_RESPONSE_BYTES;\n let body = \"\";\n try {\n while (remaining > 0) {\n const { done, value } = await reader.read();\n if (done) break;\n const bounded = value.subarray(0, remaining);\n remaining -= bounded.length;\n body += decoder.decode(bounded, { stream: remaining > 0 });\n if (bounded.length < value.length) break;\n }\n body += decoder.decode();\n return body;\n } finally {\n if (remaining === 0) await reader.cancel().catch(() => {});\n }\n}\n\nasync function fetchBodyTextViaFetch(\n url: string,\n timeoutMs: number,\n): Promise<ProbeResponse | null> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(url, {\n signal: controller.signal,\n headers: { accept: \"text/html\" },\n });\n const body = await readBoundedFetchBody(response);\n return { status: response.status, body };\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n\n// node:http has its own client implementation (separate from undici's\n// connect pool), so a filter that hooks one stack doesn't necessarily catch\n// the other. http-only: the canvas host is always plain http on loopback.\nfunction fetchBodyTextViaNodeHttp(\n url: string,\n timeoutMs: number,\n): Promise<ProbeResponse | null> {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return Promise.resolve(null);\n }\n if (parsed.protocol !== \"http:\") {\n return Promise.resolve(null);\n }\n return new Promise((resolve) => {\n const request = httpRequest(\n parsed,\n { headers: { accept: \"text/html\" } },\n (response) => {\n const chunks: Buffer[] = [];\n let length = 0;\n response.on(\"data\", (chunk: Buffer) => {\n if (length >= MAX_PROBE_RESPONSE_BYTES) return;\n const remaining = MAX_PROBE_RESPONSE_BYTES - length;\n const bounded = chunk.subarray(0, remaining);\n chunks.push(bounded);\n length += bounded.length;\n });\n response.on(\"end\", () =>\n resolve({\n status: response.statusCode ?? 0,\n body: Buffer.concat(chunks).toString(\"utf-8\"),\n }),\n );\n response.on(\"error\", () => resolve(null));\n },\n );\n request.setTimeout(timeoutMs, () => {\n request.destroy();\n resolve(null);\n });\n request.on(\"error\", () => resolve(null));\n request.end();\n });\n}\n\n// Host-registered extra transports (e.g. the Electron adapter registers a\n// Chromium-network-stack transport, which takes a different OS path than\n// Node's sockets and honors system proxy settings). Module-level so the\n// registration doesn't have to thread through every supervisor layer.\nlet extraProbeTransports: readonly ProbeTransport[] = [];\nexport function setExtraProbeTransports(\n transports: readonly ProbeTransport[],\n): void {\n extraProbeTransports = [...transports];\n}\n\n// Resolve with the first non-null response; null only after ALL transports have\n// settled without one. Never rejects.\nfunction firstNonNullResponse(\n attempts: Array<Promise<ProbeResponse | null>>,\n): Promise<ProbeResponse | null> {\n if (attempts.length === 0) {\n return Promise.resolve(null);\n }\n return new Promise((resolve) => {\n let pending = attempts.length;\n let settled = false;\n for (const attempt of attempts) {\n void attempt\n .then(\n (response) => response,\n () => null,\n )\n .then((response) => {\n pending -= 1;\n if (settled) return;\n if (response !== null) {\n settled = true;\n resolve(response);\n } else if (pending === 0) {\n resolve(null);\n }\n });\n }\n });\n}\n\n// GET `url` and return a bounded status/body response, or null when every transport\n// failed at the network level. Shared by the HTML identity check and the\n// identity-endpoint fallback.\nasync function fetchProbeResponse(\n url: string,\n timeoutMs: number,\n): Promise<ProbeResponse | null> {\n const transports: ProbeTransport[] = [\n fetchBodyTextViaFetch,\n fetchBodyTextViaNodeHttp,\n ...extraProbeTransports,\n ];\n return firstNonNullResponse(\n transports.map((transport) => transport(url, timeoutMs).catch(() => null)),\n );\n}\n\n// One probe attempt, classified:\n// - \"ok\" — a responder answered AND it's our tempo-host;\n// - \"wrong-responder\" — something answered HTTP, but it isn't our host\n// (another server recycled the port, a sibling\n// workspace's host, …);\n// - \"unreachable\" — nothing answered at the network level on ANY\n// transport.\n// The ok/not-ok split drives readiness; the wrong-responder/unreachable split\n// drives the TEM-873 probe-blocked classification (a URL the child advertised\n// that NOTHING ever answers, while the child stays alive, means loopback HTTP\n// from this process is being blocked — not that the server is down).\ntype TempoHostProbeOutcome =\n | \"ok\"\n | \"wrong-responder\"\n | \"identity-incompatible\"\n | \"unreachable\";\n\n// Report whether the responder at a candidate `/tempo-host` URL proves the\n// current workspace's served-host contract. Managed Vite/Next hosts must echo\n// the generated workspace nonce and compatible SDK/plugin fields from\n// `/__tempo/identity`; the served path meta is diagnostics only because\n// containers and remote hosts legitimately advertise a different filesystem.\n// Expo and low-level legacy callers have no HTTP identity endpoint and retain\n// marker-only acceptance.\nasync function probeTempoHost(\n url: string,\n timeoutMs: number,\n expectedProjectRoot?: string | null,\n expectedIdentity?: ExpectedTempoHostIdentity | null,\n): Promise<TempoHostProbeOutcome> {\n const response = await fetchProbeResponse(url, timeoutMs);\n const body = response?.body ?? null;\n const hasLegacyMarker = body !== null && isTempoHostDocument(body);\n let advertisedRootMismatch = false;\n\n if (body !== null && expectedProjectRoot) {\n const advertised = readAdvertisedProjectRoot(body);\n if (\n advertised &&\n !advertisedRootBelongsToWorkspace(advertised, expectedProjectRoot)\n ) {\n advertisedRootMismatch = true;\n // Root paths are diagnostics only. Symlinks, containers, and remotes can\n // legitimately disagree; the per-worktree nonce is the ownership gate.\n devLog(\"probe:project_root_diagnostic_mismatch\", {\n advertised,\n expectedProjectRoot,\n });\n }\n }\n\n // If the expected Vite/Next nonce is unavailable, retain the old sibling\n // rejection instead of silently weakening readiness to a bare marker. This\n // is the compatibility lane for projects that predate workspace-nonce.\n if (!expectedIdentity && advertisedRootMismatch) return \"wrong-responder\";\n\n // Expo/legacy health checks have no identity contract. Avoid requesting the\n // endpoint on every periodic tick when the document marker already proves\n // the only contract available in this lane.\n if (!expectedIdentity && hasLegacyMarker) return \"ok\";\n\n const identity = await probeTempoIdentityEndpoint(\n url,\n timeoutMs,\n expectedIdentity,\n );\n if (identity === \"ok\") return \"ok\";\n // The endpoint echoed OUR nonce before failing a toolchain check\n // (pluginActive/sdk floor/framework): the responder is provably this\n // workspace's host, so the sdk-incompatible remedy applies no matter what\n // the served document looks like. Next host pages carry no legacy marker,\n // so gating this on the marker would misreport the flagship \"TEMPO=true\n // not forwarded into the container\" case as wrong-responder.\n if (identity === \"verified-incompatible\") return \"identity-incompatible\";\n\n if (expectedIdentity) {\n if (identity === \"wrong-responder\") return \"wrong-responder\";\n if (hasLegacyMarker && identity === \"identity-incompatible\") {\n return \"identity-incompatible\";\n }\n return body !== null ? \"wrong-responder\" : \"unreachable\";\n }\n\n return body !== null ? \"wrong-responder\" : identity;\n}\n\nasync function fetchIsTempoHost(\n url: string,\n timeoutMs: number,\n expectedProjectRoot?: string | null,\n expectedIdentity?: ExpectedTempoHostIdentity | null,\n): Promise<boolean> {\n return (\n (await probeTempoHost(\n url,\n timeoutMs,\n expectedProjectRoot,\n expectedIdentity,\n )) === \"ok\"\n );\n}\n\n// Identity probe: hit the SDK's namespaced endpoint and validate the nonce,\n// plugin/framework fields, and the running SDK version. Contract versions are\n// a forward-additive floor: newer SDKs must retain v1 fields for old clients.\n// \"verified-incompatible\" is the nonce-authenticated failure shape: the\n// endpoint proved it is THIS workspace's host (nonce echo) and then failed a\n// toolchain check. It is distinguished from the anonymous \"identity-incompatible\"\n// shapes (404 / non-JSON / foreign document) because only the verified shape\n// licenses the sdk-incompatible diagnosis without further document evidence.\nasync function probeTempoIdentityEndpoint(\n tempoHostUrl: string,\n timeoutMs: number,\n expectedIdentity?: ExpectedTempoHostIdentity | null,\n): Promise<TempoHostProbeOutcome | \"verified-incompatible\"> {\n let identityUrl: string;\n try {\n identityUrl = `${new URL(tempoHostUrl).origin}/__tempo/identity`;\n } catch {\n return \"unreachable\";\n }\n const response = await fetchProbeResponse(identityUrl, timeoutMs);\n if (response === null) {\n return \"unreachable\";\n }\n if (response.status < 200 || response.status >= 300) {\n return \"identity-incompatible\";\n }\n let parsed: {\n tempoHost?: unknown;\n contractVersion?: unknown;\n workspaceNonce?: unknown;\n sdkVersion?: unknown;\n pluginActive?: unknown;\n framework?: unknown;\n };\n try {\n parsed = JSON.parse(response.body);\n } catch {\n return \"identity-incompatible\";\n }\n if (parsed.tempoHost !== true) {\n return \"identity-incompatible\";\n }\n if (!expectedIdentity) return \"ok\";\n if (parsed.workspaceNonce !== expectedIdentity.workspaceNonce) {\n return \"wrong-responder\";\n }\n if (\n typeof parsed.contractVersion !== \"number\" ||\n parsed.contractVersion < 1 ||\n parsed.pluginActive !== true ||\n parsed.framework !== expectedIdentity.framework ||\n typeof parsed.sdkVersion !== \"string\" ||\n !tempoSdkVersionSatisfiesCanvasHost(parsed.sdkVersion)\n ) {\n return \"verified-incompatible\";\n }\n return \"ok\";\n}\n\nasync function waitForHttpReachable(\n url: string,\n timeoutMs: number,\n expectedProjectRoot?: string | null,\n expectedIdentity?: ExpectedTempoHostIdentity | null,\n): Promise<{\n reachable: boolean;\n sawResponder: boolean;\n identityFailure: boolean;\n}> {\n const startedAt = Date.now();\n let sawResponder = false;\n let identityFailure = false;\n while (Date.now() - startedAt < timeoutMs) {\n const outcome = await probeTempoHost(\n url,\n 1200,\n expectedProjectRoot,\n expectedIdentity,\n );\n if (outcome === \"ok\") {\n return { reachable: true, sawResponder: true, identityFailure: false };\n }\n if (outcome !== \"unreachable\") {\n sawResponder = true;\n }\n identityFailure ||= outcome === \"identity-incompatible\";\n await sleep(120);\n }\n\n return { reachable: false, sawResponder, identityFailure };\n}\n\nasync function waitForFirstHttpReachable(\n urls: readonly string[],\n timeoutMs: number,\n expectedProjectRoot?: string | null,\n expectedIdentity?: ExpectedTempoHostIdentity | null,\n isAlive?: () => boolean,\n): Promise<{\n url: string | null;\n sawResponder: boolean;\n identityFailure: boolean;\n}> {\n const startedAt = Date.now();\n let sawResponder = false;\n let identityFailure = false;\n while (Date.now() - startedAt < timeoutMs) {\n for (const url of urls) {\n const result = await waitForHttpReachable(\n url,\n 250,\n expectedProjectRoot,\n expectedIdentity,\n );\n sawResponder = sawResponder || result.sawResponder;\n identityFailure = identityFailure || result.identityFailure;\n if (result.reachable) {\n return { url, sawResponder, identityFailure: false };\n }\n }\n\n // The process died before any URL answered — further polling can only\n // burn the whole timeout (a doomed `next dev` will never serve). Bail so\n // the caller classifies this as a crash and auto-retries, instead of\n // waiting out the full budget then reporting a non-retryable timeout.\n // (Probe FIRST, then check liveness, so a process that served right before\n // exiting is still caught.) (bundle t17dsrf95f4r46daj9b74eczd988yy1w)\n if (isAlive && !isAlive()) {\n return { url: null, sawResponder, identityFailure };\n }\n\n await sleep(120);\n }\n\n return { url: null, sawResponder, identityFailure };\n}\n\n// Single raw TCP connect to the probe target — refines the probe-blocked\n// message: connect-succeeds-but-HTTP-fails points at an HTTP-layer filter,\n// connect-fails at the firewall blocking the connection outright.\nasync function tcpConnectOnce(\n url: string,\n timeoutMs: number,\n): Promise<boolean> {\n let host: string;\n let port: number;\n try {\n const parsed = new URL(url);\n host = parsed.hostname;\n port = Number(parsed.port) || (parsed.protocol === \"https:\" ? 443 : 80);\n } catch {\n return false;\n }\n return await new Promise((resolve) => {\n const socket = netConnect({ host, port });\n const finish = (connected: boolean) => {\n socket.destroy();\n resolve(connected);\n };\n socket.setTimeout(timeoutMs, () => finish(false));\n socket.once(\"connect\", () => finish(true));\n socket.once(\"error\", () => finish(false));\n });\n}\n\n// Some loopback filters key on the literal host form — intercepting\n// `127.0.0.1` but not `localhost` (or vice versa). Probe both forms of every\n// candidate, originals first so the returned URL is unchanged whenever the\n// advertised form works.\nexport function expandLoopbackHostVariants(urls: readonly string[]): string[] {\n const variants: string[] = [];\n for (const url of urls) {\n try {\n const parsed = new URL(url);\n if (parsed.hostname === \"127.0.0.1\") {\n parsed.hostname = \"localhost\";\n } else if (parsed.hostname === \"localhost\") {\n parsed.hostname = \"127.0.0.1\";\n } else {\n continue;\n }\n variants.push(parsed.toString());\n } catch {\n // Not a URL — nothing to expand.\n }\n }\n const all = [...urls, ...variants];\n return all.filter((value, index) => all.indexOf(value) === index);\n}\n\nasync function waitForHttpUnreachable(\n url: string,\n timeoutMs: number,\n): Promise<boolean> {\n const startedAt = Date.now();\n while (Date.now() - startedAt < timeoutMs) {\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 1000);\n await fetch(url, { signal: controller.signal });\n clearTimeout(timeout);\n } catch {\n return true;\n }\n await sleep(120);\n }\n\n return false;\n}\n\n// Single-shot tempo-host identity check. Used by the periodic health probe and\n// the repair() I-4 invariant to test the bound dev-server URL without the\n// polling loop in waitForHttpReachable. Identity-aware (see fetchIsTempoHost):\n// a URL whose port has been recycled to a non-host (e.g. the renderer's own\n// origin) fails the check and is cleared, instead of being trusted forever\n// because something answers 200.\nasync function probeUrlOnce(\n url: string,\n timeoutMs: number,\n expectedProjectRoot?: string | null,\n expectedIdentity?: ExpectedTempoHostIdentity | null,\n): Promise<boolean> {\n return fetchIsTempoHost(\n url,\n timeoutMs,\n expectedProjectRoot,\n expectedIdentity,\n );\n}\n\nasync function waitForProcessExit(\n processRef: ChildProcess,\n timeoutMs: number,\n): Promise<boolean> {\n if (processRef.exitCode !== null || processRef.signalCode !== null) {\n return true;\n }\n\n return await withTimeout(\n new Promise<boolean>((resolve) => {\n processRef.once(\"exit\", () => resolve(true));\n }),\n timeoutMs,\n \"Process exit timeout\",\n ).catch(() => false);\n}\n\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n message: string,\n): Promise<T> {\n return await new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(message));\n }, timeoutMs);\n\n promise\n .then((value) => {\n clearTimeout(timer);\n resolve(value);\n })\n .catch((error) => {\n clearTimeout(timer);\n reject(error);\n });\n });\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n","/**\n * Watch Provider Seam\n *\n * A process-scoped slot for a WatchImplementation that overrides how the\n * package-level watch() executes. This exists so a host application can\n * reroute EVERY watch() call in the process — including ones made by other\n * bundled packages it embeds — through one policy boundary (e.g. Electron\n * main hosting native subscriptions in a supervised utility process),\n * without threading a watcher dependency through each caller.\n *\n * REVIEWED EXCEPTION to the modules/ rule that a primitive must never\n * secretly change its execution topology: this slot is deliberately mutable\n * process-global state, because its purpose is a SAFETY boundary — the host\n * must be able to guarantee that no watch() call in the process (direct or\n * from an embedded package it cannot edit) executes a crash-capable native\n * backend in-process. Composing at each call site cannot give that\n * guarantee; a bypassed caller reintroduces the crash. The escape hatches\n * keep the topology explicit and observable: watchLocal() is the documented\n * always-in-process bypass, and getInstalledWatchProvider() exposes whether\n * (and which) provider owns routing. Do not copy this pattern for\n * convenience wiring — it is justified only where a host must own an\n * invariant across callers it does not control.\n *\n * The slot lives on globalThis under Symbol.for() so the singleton survives\n * bundlers emitting this file into multiple chunks within one process.\n */\n\nimport type { WatchImplementation } from './types';\n\nconst PROVIDER_SLOT = Symbol.for('@modules/file-watcher:watch-provider');\n\ninterface ProviderSlot {\n provider: WatchImplementation | null;\n}\n\nfunction getSlot(): ProviderSlot {\n const holder = globalThis as { [PROVIDER_SLOT]?: ProviderSlot };\n holder[PROVIDER_SLOT] ??= { provider: null };\n return holder[PROVIDER_SLOT];\n}\n\n/**\n * Install the process-wide watch implementation. Idempotent for the same\n * instance; throws if a different provider is already installed — two live\n * providers means two subsystems both believe they own watch routing, which\n * must fail loudly rather than silently last-write-wins.\n */\nexport function installWatchProvider(provider: WatchImplementation): void {\n const slot = getSlot();\n if (slot.provider === provider) return;\n if (slot.provider !== null) {\n throw new Error(\n 'A different watch provider is already installed in this process',\n );\n }\n slot.provider = provider;\n}\n\n/** The installed provider, or null when watch() should run locally. */\nexport function getInstalledWatchProvider(): WatchImplementation | null {\n return getSlot().provider;\n}\n\n/**\n * Clear the installed provider. For tests and for orderly teardown of the\n * subsystem that installed it; production code must not use this to swap\n * providers mid-flight (existing subscriptions keep their transport).\n */\nexport function resetWatchProvider(): void {\n getSlot().provider = null;\n}\n","/**\n * File Watcher\n *\n * Wraps @parcel/watcher with:\n * - Event debouncing and batching\n * - Event deduplication (CREATE+DELETE→nothing, DELETE+CREATE→UPDATE)\n * - Path normalization (unicode, symlinks)\n * - File kind detection (file/directory/unknown)\n * - Buffer overflow protection\n * - Auto-restart on EINTR errors (common in containers)\n */\n\nimport { lstat, realpath, stat, unlink } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative } from \"node:path\";\nimport type { AsyncSubscription, Event as ParcelEvent } from \"@parcel/watcher\";\n\nimport { createEventKeepFilter, splitIgnorePatterns } from \"./ignore\";\nimport {\n diffScans,\n readScanSnapshot,\n scanTree,\n type TreeScan,\n writeScanSnapshot,\n} from \"./polling-backend\";\nimport { getInstalledWatchProvider } from \"./provider\";\nimport type {\n FileEvent,\n FileEventType,\n FileKind,\n GetEventsSinceOptions,\n SymlinkInfo,\n WatchCallback,\n WatchOptions,\n WatchSubscription,\n} from \"./types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Defaults\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_DEBOUNCE_MS = 100;\nconst DEFAULT_MAX_BATCH_SIZE = 1000;\nconst DEFAULT_MAX_BUFFERED_EVENTS = 30000;\n\nconst DEFAULT_POLLING_INTERVAL_MS = 500;\nconst MIN_POLLING_INTERVAL_MS = 10;\nconst EINTR_RESTART_DELAY_MS = 100;\nconst MAX_RECOVERY_RESTARTS = 50; // Max restarts before giving up\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Native addon loading\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * @parcel/watcher is loaded ON DEMAND, never at module scope (ELECTRON-25).\n *\n * A static import maps `watcher.node` into whatever process imports this file\n * — including Electron main, whose whole containment strategy is to never host\n * that addon. Deferring the load means a process that only ever uses the\n * `polling` backend never brings the crash-capable binary into its address\n * space at all, so the invariant is enforced by the module loader instead of\n * by discipline. It also keeps the addon out of the crash's loaded-module list,\n * which is how this was misdiagnosed as injected third-party code for days.\n *\n * The deferred load is a require, NOT `import(\"@parcel/watcher\")`: Electron\n * hosts compile their main bundle (which inlines this module) to V8 bytecode,\n * and bytecode-compiled code has no dynamic-import callback — a dynamic\n * import() there throws \"A dynamic import callback was not specified\" on every\n * call, which silently broke the native backend in shipped builds.\n *\n * `createRequire` is anchored on `__filename` when it exists: some hosts\n * (the canvas demo's esbuild bundle) inline this module into a CJS bundle,\n * where `import.meta.url` compiles to `undefined` — `createRequire(undefined)`\n * then throws at module-load time and kills the process before it does\n * anything. Same dual-format guard as canvas-pages/page-parser.ts.\n */\nconst requireModule = createRequire(\n typeof __filename !== \"undefined\" ? __filename : import.meta.url,\n);\n\nlet nativeWatcherPromise: Promise<typeof import(\"@parcel/watcher\")> | null =\n null;\n\nfunction loadNativeWatcher(): Promise<typeof import(\"@parcel/watcher\")> {\n nativeWatcherPromise ??= Promise.resolve().then(() => {\n const loaded = requireModule(\"@parcel/watcher\") as typeof import(\n \"@parcel/watcher\"\n ) & { default?: typeof import(\"@parcel/watcher\") };\n return loaded.default ?? loaded;\n });\n return nativeWatcherPromise;\n}\n\n/** Whether the native addon has been pulled into this process yet. */\nexport function isNativeWatcherLoaded(): boolean {\n return nativeWatcherPromise !== null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main API\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Watch a directory for file system changes.\n *\n * Delegates to the process-wide provider when one is installed (see\n * provider.ts) — this is how a host application reroutes every watch() call\n * through one execution policy (e.g. an out-of-process native watcher host).\n * With no provider installed it watches locally in this process.\n *\n * @param rootPath Directory to watch recursively\n * @param callback Called with batches of events\n * @param options Watch configuration\n * @returns Subscription that can be unsubscribed\n */\nexport async function watch(\n rootPath: string,\n callback: WatchCallback,\n options: WatchOptions = {},\n): Promise<WatchSubscription> {\n const provider = getInstalledWatchProvider();\n if (provider) {\n return provider.watch(rootPath, callback, options);\n }\n return watchLocal(rootPath, callback, options);\n}\n\n/**\n * Watch a directory for file system changes IN THIS PROCESS, bypassing any\n * installed provider. This is the concrete implementation watch() falls back\n * to, and what provider implementations themselves build on (a utility-process\n * host calls watchLocal inside the child; a polling fallback calls it with\n * backend: 'polling' in the host process).\n *\n * Events are debounced, batched, and optionally deduplicated before delivery\n * to minimize redundant processing during rapid changes (git checkout, npm install, etc.).\n *\n * With autoRecover enabled, on any error (EINTR, buffer overflow, etc.):\n * 1. Saves a snapshot of the current filesystem state\n * 2. Flushes any pending events to the callback\n * 3. Recursively restarts watching with snapshot recovery\n *\n * Some events may be lost between error and recovery, but the watcher\n * will automatically resume without manual intervention.\n */\nexport async function watchLocal(\n rootPath: string,\n callback: WatchCallback,\n options: WatchOptions = {},\n): Promise<WatchSubscription> {\n const { backend = \"native\" } = options;\n\n if (backend === \"polling\") {\n return watchWithPolling(rootPath, callback, options);\n }\n\n // Past this point we are committed to the native backend, so this is the\n // first and only place the addon enters the process (ELECTRON-25).\n const watcher = await loadNativeWatcher();\n\n const {\n ignore = [],\n debounceMs = DEFAULT_DEBOUNCE_MS,\n maxBatchSize = DEFAULT_MAX_BATCH_SIZE,\n coalesce = true,\n onError = (err) => {\n console.error(\"[file-watcher] Error:\", err.message);\n },\n autoRecover = false,\n snapshotPath,\n resumeFromSnapshot,\n } = options;\n\n // Generate a snapshot path if auto-recovery enabled but no path provided\n const effectiveSnapshotPath =\n snapshotPath ??\n (autoRecover\n ? `/tmp/file-watcher-snapshot-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`\n : undefined);\n\n // Resolve symlinks for the root path\n const rootSymlinkInfo = await resolveRootPath(rootPath);\n\n // Globs must never reach @parcel/watcher's native layer — its per-event\n // std::regex matching crashes the process on long paths (see ignore.ts).\n const { nativeIgnorePaths, relativePathFilters } =\n splitIgnorePatterns(ignore);\n const keepEvent = createEventKeepFilter(\n rootSymlinkInfo.realPath,\n relativePathFilters,\n );\n\n let eventBuffer: ParcelEvent[] = [];\n let debounceTimer: NodeJS.Timeout | null = null;\n let disposed = false;\n let currentSubscription: AsyncSubscription | null = null;\n let recoveryRestartCount = 0;\n\n // Every in-flight @parcel/watcher native async operation (subscribe,\n // unsubscribe, writeSnapshot, getEventsSince). unsubscribe() drains this set\n // so that NO native async work outlives it. A @parcel/watcher operation that\n // completes after the Node environment begins tearing down at app quit fires\n // the addon's threadsafe callback into a freed V8 isolate, which aborts the\n // whole process via napi_fatal_exception → node::OnFatalError → abort()\n // (ELECTRON-H / ELECTRON-22: a hard, dialog-less crash in watcher.node).\n const pendingNativeOps = new Set<Promise<unknown>>();\n\n function trackNativeOp<T>(op: Promise<T>): Promise<T> {\n pendingNativeOps.add(op);\n const forget = () => {\n pendingNativeOps.delete(op);\n };\n op.then(forget, forget);\n return op;\n }\n\n // Wait until every tracked native op has settled. Draining one op can enqueue\n // another (a resubscribe resolving into its own teardown unsubscribe), so we\n // loop until the set is genuinely empty rather than snapshotting once.\n async function drainNativeOps(): Promise<void> {\n while (pendingNativeOps.size > 0) {\n await Promise.allSettled([...pendingNativeOps]);\n }\n }\n\n function scheduleFlush() {\n if (debounceTimer || disposed) return;\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n flush().catch((err) => {\n onError(err instanceof Error ? err : new Error(String(err)));\n });\n }, debounceMs);\n }\n\n async function flush(): Promise<void> {\n if (disposed) return;\n if (eventBuffer.length === 0) return;\n\n const batch = eventBuffer.splice(0, maxBatchSize);\n\n // Convert parcel events to our FileEvent format with kind detection\n const events = await Promise.all(\n batch.map((e) => parcelEventToFileEvent(e, rootSymlinkInfo)),\n );\n\n // Re-check disposed after async operation to prevent callback after unsubscribe\n if (disposed) return;\n\n // Apply deduplication if enabled\n const processed = coalesce ? dedupeEvents(events) : events;\n\n if (processed.length > 0) {\n callback(processed);\n }\n\n // Schedule next batch if more events remain\n if (eventBuffer.length > 0 && !disposed) {\n scheduleFlush();\n }\n }\n\n // Flush all pending events immediately (used before recovery restart)\n async function flushAll(): Promise<void> {\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n while (eventBuffer.length > 0 && !disposed) {\n await flush();\n }\n }\n\n // Save snapshot and restart with recovery\n async function handleErrorWithRecovery(errorMessage: string): Promise<void> {\n if (disposed) return;\n\n // Step 1: Immediately unsubscribe to stop receiving callbacks\n // This prevents re-entry without needing a flag\n const sub = currentSubscription;\n currentSubscription = null;\n if (sub) {\n try {\n await trackNativeOp(sub.unsubscribe());\n } catch {\n // Ignore unsubscribe errors during recovery\n }\n }\n\n if (disposed) return;\n\n recoveryRestartCount++;\n if (recoveryRestartCount > MAX_RECOVERY_RESTARTS) {\n onError(\n new Error(\n `Too many recovery restarts (${MAX_RECOVERY_RESTARTS}), giving up`,\n ),\n );\n return;\n }\n\n // Step 2: Save a snapshot of current filesystem state\n if (effectiveSnapshotPath) {\n try {\n await trackNativeOp(\n watcher.writeSnapshot(\n rootSymlinkInfo.realPath,\n effectiveSnapshotPath,\n ),\n );\n } catch (snapshotErr) {\n onError(\n new Error(\n `Snapshot failed during recovery: ${snapshotErr instanceof Error ? snapshotErr.message : String(snapshotErr)}`,\n ),\n );\n }\n }\n\n // Step 3: Flush any pending events\n await flushAll();\n\n // Step 4: Wait a bit before restarting\n await new Promise((resolve) => setTimeout(resolve, EINTR_RESTART_DELAY_MS));\n\n if (disposed) return;\n\n console.log(\n `[file-watcher] Restarting after ${errorMessage} (attempt ${recoveryRestartCount}/${MAX_RECOVERY_RESTARTS})...`,\n );\n\n // Step 5: Restart with snapshot recovery\n await subscribe(effectiveSnapshotPath);\n }\n\n // Subscribe to watcher, optionally recovering from a snapshot\n async function subscribe(recoverFromSnapshot?: string): Promise<void> {\n if (disposed) return;\n\n // If recovering from a snapshot, get all events since that snapshot first\n if (recoverFromSnapshot) {\n try {\n const recoveredEvents = (\n await trackNativeOp(\n watcher.getEventsSince(\n rootSymlinkInfo.realPath,\n recoverFromSnapshot,\n { ignore: nativeIgnorePaths },\n ),\n )\n ).filter((event) => keepEvent(event.path));\n if (recoveredEvents.length > 0) {\n // Process recovered events through the normal pipeline\n eventBuffer.push(...recoveredEvents);\n scheduleFlush();\n }\n } catch (err) {\n onError(\n new Error(\n `Recovery from snapshot failed: ${err instanceof Error ? err.message : String(err)}`,\n ),\n );\n }\n }\n\n try {\n const sub = await trackNativeOp(\n watcher.subscribe(\n rootSymlinkInfo.realPath,\n (err, events) => {\n if (disposed) return;\n\n if (err) {\n const isEintr = err.message?.includes(\"Interrupted system call\");\n if (autoRecover) {\n handleErrorWithRecovery(isEintr ? \"EINTR\" : \"error\");\n return;\n }\n onError(err);\n return;\n }\n\n // Reset restart counter on successful event delivery\n recoveryRestartCount = 0;\n\n const kept = events.filter((event) => keepEvent(event.path));\n if (kept.length === 0) return;\n\n // Respect buffer limit to prevent OOM during bulk operations\n const remaining = DEFAULT_MAX_BUFFERED_EVENTS - eventBuffer.length;\n if (remaining <= 0) {\n if (autoRecover) {\n handleErrorWithRecovery(\"buffer overflow\");\n return;\n }\n onError(new Error(\"Event buffer full, dropping events\"));\n return;\n }\n\n eventBuffer.push(...kept.slice(0, remaining));\n scheduleFlush();\n },\n { ignore: nativeIgnorePaths },\n ),\n );\n\n // If disposal happened while this native subscribe was in flight, do NOT\n // retain the resolved subscription — tear it down immediately. A live\n // @parcel/watcher backend that outlives unsubscribe() (and thus the Node\n // environment) fires its threadsafe callback into a freed isolate and\n // aborts the process. This is the auto-recovery resubscribe race behind\n // the ELECTRON-H / ELECTRON-22 shutdown crashes.\n if (disposed) {\n await trackNativeOp(sub.unsubscribe()).catch(() => {});\n return;\n }\n currentSubscription = sub;\n } catch (err) {\n if (!disposed) {\n onError(err instanceof Error ? err : new Error(String(err)));\n }\n }\n }\n\n // Initial subscription, optionally resuming from a snapshot\n await subscribe(resumeFromSnapshot);\n\n return {\n async unsubscribe() {\n disposed = true;\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n eventBuffer = [];\n const sub = currentSubscription;\n currentSubscription = null;\n if (sub) {\n await trackNativeOp(sub.unsubscribe());\n }\n // Barrier: do not resolve until every @parcel/watcher native operation\n // this watcher started has settled (including a resubscribe still in\n // flight and its follow-up teardown). Once this resolves there is zero\n // native async work outstanding, so a subsequent Node env teardown at\n // app quit cannot fire a threadsafe callback into a freed isolate.\n await drainNativeOps();\n },\n };\n}\n\nasync function watchWithPolling(\n rootPath: string,\n callback: WatchCallback,\n options: WatchOptions,\n): Promise<WatchSubscription> {\n const {\n ignore = [],\n debounceMs = DEFAULT_DEBOUNCE_MS,\n maxBatchSize = DEFAULT_MAX_BATCH_SIZE,\n coalesce = true,\n onError = (err) => {\n console.error(\"[file-watcher] Error:\", err.message);\n },\n autoRecover = false,\n snapshotPath,\n resumeFromSnapshot,\n pollingIntervalMs = DEFAULT_POLLING_INTERVAL_MS,\n } = options;\n\n const rootSymlinkInfo = await resolveRootPath(rootPath);\n // `nativeIgnorePaths` keeps its name and meaning — subtree roots the scan\n // must not descend into — but is now consumed by the node:fs walk rather\n // than handed to native code.\n const { nativeIgnorePaths, relativePathFilters } =\n splitIgnorePatterns(ignore);\n const keepEvent = createEventKeepFilter(\n rootSymlinkInfo.realPath,\n relativePathFilters,\n );\n const pollingInterval = Math.max(MIN_POLLING_INTERVAL_MS, pollingIntervalMs);\n const generatedSnapshotPath = join(\n tmpdir(),\n `file-watcher-polling-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`,\n );\n const effectiveSnapshotPath =\n resumeFromSnapshot ?? snapshotPath ?? generatedSnapshotPath;\n const shouldCleanupSnapshot = !snapshotPath && !resumeFromSnapshot;\n\n // The baseline the next poll diffs against. Held in memory (the on-disk\n // snapshot exists only to survive a restart), so the steady-state poll costs\n // one tree walk and no file I/O for the baseline.\n let baseline: TreeScan | null = null;\n if (resumeFromSnapshot) {\n baseline = await readScanSnapshot(resumeFromSnapshot);\n if (!baseline) {\n onError(\n new Error(\n `Resume snapshot missing or unreadable: ${resumeFromSnapshot}`,\n ),\n );\n }\n }\n\n let eventBuffer: ParcelEvent[] = [];\n let debounceTimer: NodeJS.Timeout | null = null;\n let pollTimer: NodeJS.Timeout | null = null;\n let pollInFlight: Promise<void> | null = null;\n let disposed = false;\n\n function scheduleFlush() {\n if (debounceTimer || disposed) return;\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n flush().catch((err) => {\n onError(err instanceof Error ? err : new Error(String(err)));\n });\n }, debounceMs);\n }\n\n async function flush(): Promise<void> {\n if (disposed) return;\n if (eventBuffer.length === 0) return;\n\n const batch = eventBuffer.splice(0, maxBatchSize);\n\n const events = await Promise.all(\n batch.map((event) => parcelEventToFileEvent(event, rootSymlinkInfo)),\n );\n\n if (disposed) return;\n\n const processed = coalesce ? dedupeEvents(events) : events;\n if (processed.length > 0) {\n callback(processed);\n }\n\n if (eventBuffer.length > 0 && !disposed) {\n scheduleFlush();\n }\n }\n\n async function pollOnce(): Promise<void> {\n if (disposed) return;\n if (pollInFlight) return;\n\n pollInFlight = (async () => {\n try {\n const scan = await scanTree(\n rootSymlinkInfo.realPath,\n nativeIgnorePaths,\n );\n if (disposed) return;\n\n // First poll (or a post-error re-baseline) only establishes state —\n // reporting the whole tree as `create` would be a false storm.\n const previous = baseline;\n baseline = scan;\n // Persist for `resumeFromSnapshot` across restarts. Best-effort: a\n // failed write costs resume fidelity, never the live subscription.\n void writeScanSnapshot(effectiveSnapshotPath, scan).catch(() => {});\n if (!previous) return;\n\n const polledEvents = diffScans(previous, scan).filter((event) =>\n keepEvent(event.path),\n );\n if (disposed || polledEvents.length === 0) return;\n\n const remaining = DEFAULT_MAX_BUFFERED_EVENTS - eventBuffer.length;\n if (remaining <= 0) {\n onError(new Error(\"Event buffer full, dropping events\"));\n } else {\n eventBuffer.push(...polledEvents.slice(0, remaining));\n scheduleFlush();\n }\n } catch (err) {\n onError(err instanceof Error ? err : new Error(String(err)));\n if (autoRecover) {\n // Drop the baseline so the next poll re-establishes it cleanly\n // rather than diffing against a half-built scan.\n baseline = null;\n }\n } finally {\n pollInFlight = null;\n }\n })();\n\n await pollInFlight;\n }\n\n await pollOnce();\n\n if (!disposed) {\n pollTimer = setInterval(() => {\n pollOnce().catch((err) => {\n onError(err instanceof Error ? err : new Error(String(err)));\n });\n }, pollingInterval);\n }\n\n return {\n async unsubscribe() {\n disposed = true;\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (debounceTimer) {\n clearTimeout(debounceTimer);\n debounceTimer = null;\n }\n if (pollInFlight) {\n try {\n await pollInFlight;\n } catch {\n // Polling errors are already routed to onError\n }\n }\n eventBuffer = [];\n\n if (shouldCleanupSnapshot) {\n try {\n await unlink(effectiveSnapshotPath);\n } catch {\n // Snapshot file may not exist or may already be cleaned up.\n }\n }\n },\n };\n}\n\n/**\n * Deduplicate a list of file events.\n *\n * Coalescing rules:\n * - CREATE followed by DELETE → removed (file appeared then disappeared)\n * - DELETE followed by CREATE → UPDATE (file was replaced)\n * - Multiple events for same path → keep latest\n *\n * The key insight is tracking two independent states:\n * 1. Did the file exist before this batch? (existedBefore)\n * 2. Does the file exist after processing this event? (existsNow)\n *\n * Final event type is determined by the transition:\n * - existed → exists = UPDATE (or no change if same content, but we emit UPDATE)\n * - existed → gone = DELETE\n * - didn't exist → exists = CREATE\n * - didn't exist → gone = nothing (cancel out)\n *\n * This is a pure function useful for consumers doing custom batching\n * who still want deduplication.\n */\nexport function dedupeEvents(events: FileEvent[]): FileEvent[] {\n // Track the original state (did file exist before batch) and current event\n // existedBefore: true if first event was UPDATE or DELETE (implies file existed)\n // false if first event was CREATE (file didn't exist)\n const byPath = new Map<\n string,\n { existedBefore: boolean; current: FileEvent }\n >();\n\n for (const event of events) {\n const existing = byPath.get(event.path);\n\n if (!existing) {\n // First event for this path - determine if file existed before\n const existedBefore = event.type !== \"create\";\n byPath.set(event.path, { existedBefore, current: event });\n continue;\n }\n\n // Determine if file exists now based on new event\n const existsNow = event.type !== \"delete\";\n\n // If file didn't exist before and doesn't exist now, cancel out\n if (!existing.existedBefore && !existsNow) {\n byPath.delete(event.path);\n continue;\n }\n\n // Determine the correct event type based on state transition\n let finalType: FileEventType;\n if (!existing.existedBefore && existsNow) {\n // Didn't exist → exists = CREATE\n finalType = \"create\";\n } else if (existing.existedBefore && !existsNow) {\n // Existed → gone = DELETE\n finalType = \"delete\";\n } else {\n // Existed → exists = UPDATE (file was modified or replaced)\n finalType = \"update\";\n }\n\n byPath.set(event.path, {\n existedBefore: existing.existedBefore,\n current: { ...event, type: finalType },\n });\n }\n\n return Array.from(byPath.values()).map((entry) => entry.current);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Snapshot API\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Save a snapshot of the current filesystem state.\n *\n * Use with getEventsSince() to detect changes that occurred while not watching.\n * This is useful for:\n * - Detecting changes between program runs\n * - Creating a baseline before bulk operations\n * - Manual recovery from watcher issues\n *\n * @param rootPath Directory to snapshot\n * @param snapshotPath Path to save the snapshot file\n */\nexport async function writeSnapshot(\n rootPath: string,\n snapshotPath: string,\n): Promise<void> {\n const rootInfo = await resolveRootPath(rootPath);\n const watcher = await loadNativeWatcher();\n await watcher.writeSnapshot(rootInfo.realPath, snapshotPath);\n}\n\n/**\n * Get all filesystem events that occurred since a snapshot was taken.\n *\n * Compares the current filesystem state against a previously saved snapshot\n * to identify all changes. Events are enriched with file kind detection and\n * optionally deduplicated.\n *\n * @param rootPath Directory that was snapshotted\n * @param snapshotPath Path to the snapshot file\n * @param options Configuration options\n * @returns Array of events that occurred since the snapshot\n */\nexport async function getEventsSince(\n rootPath: string,\n snapshotPath: string,\n options: GetEventsSinceOptions = {},\n): Promise<FileEvent[]> {\n const { ignore = [], coalesce = true } = options;\n\n const rootInfo = await resolveRootPath(rootPath);\n const { nativeIgnorePaths, relativePathFilters } =\n splitIgnorePatterns(ignore);\n const keepEvent = createEventKeepFilter(\n rootInfo.realPath,\n relativePathFilters,\n );\n\n // Get raw events from parcel — globs stay JS-side (see ignore.ts)\n const watcher = await loadNativeWatcher();\n const parcelEvents = (\n await watcher.getEventsSince(rootInfo.realPath, snapshotPath, {\n ignore: nativeIgnorePaths,\n })\n ).filter((event) => keepEvent(event.path));\n\n // Convert to our FileEvent format with kind detection\n const events = await Promise.all(\n parcelEvents.map((e) => parcelEventToFileEvent(e, rootInfo)),\n );\n\n // Apply deduplication if enabled\n return coalesce ? dedupeEvents(events) : events;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface RootPathInfo {\n /** The path the user provided */\n originalPath: string;\n /** The resolved real path (symlinks resolved) */\n realPath: string;\n /** True if originalPath was a symlink */\n isSymlink: boolean;\n}\n\n/**\n * Resolve the root path and detect if it's a symlink.\n *\n * Always resolves the full path via realpath() to handle intermediate symlinks\n * (e.g., /var -> /private/var on macOS). This ensures the root path matches\n * the resolved paths that @parcel/watcher reports in events.\n */\nasync function resolveRootPath(rootPath: string): Promise<RootPathInfo> {\n try {\n const stats = await lstat(rootPath);\n const isSymlink = stats.isSymbolicLink();\n const realPath = await realpath(rootPath);\n\n return {\n originalPath: rootPath,\n realPath,\n isSymlink,\n };\n } catch {\n // Path doesn't exist or can't be accessed, use as-is\n return {\n originalPath: rootPath,\n realPath: rootPath,\n isSymlink: false,\n };\n }\n}\n\n/**\n * Determine the kind of a filesystem path.\n */\nasync function getFileKind(absPath: string): Promise<FileKind> {\n try {\n const stats = await stat(absPath);\n if (stats.isDirectory()) return \"directory\";\n if (stats.isFile()) return \"file\";\n return \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\n/**\n * Convert a parcel watcher event to our FileEvent format.\n */\nasync function parcelEventToFileEvent(\n event: ParcelEvent,\n rootInfo: RootPathInfo,\n): Promise<FileEvent> {\n let absPath = event.path;\n\n // Unicode normalization (macOS returns NFD, we want NFC for consistency)\n if (process.platform === \"darwin\") {\n absPath = absPath.normalize(\"NFC\");\n }\n\n // Determine kind (can't stat deleted files)\n const kind: FileKind =\n event.type === \"delete\" ? \"unknown\" : await getFileKind(absPath);\n\n // Calculate relative path from the real root\n const relativePath = relative(rootInfo.realPath, absPath);\n\n // Build symlink info if the root was a symlink\n let symlink: SymlinkInfo | undefined;\n if (rootInfo.isSymlink) {\n // Use path slicing instead of .replace() to avoid matching substrings elsewhere in path\n const symlinkAbsPath =\n rootInfo.originalPath + absPath.slice(rootInfo.realPath.length);\n symlink = {\n path: symlinkAbsPath,\n relativePath: relative(rootInfo.originalPath, symlinkAbsPath),\n };\n }\n\n return {\n type: event.type as FileEvent[\"type\"],\n kind,\n path: absPath,\n relativePath,\n symlink,\n };\n}\n","/**\n * Ignore-pattern handling.\n *\n * @parcel/watcher accepts globs in its `ignore` option, but its NATIVE side\n * compiles each glob to a `std::regex` and runs `regex_match` against every\n * event's relative path on the watcher thread. MSVC's `std::regex` matcher\n * recurses proportionally to input length, so a relative path of ~300+ chars\n * — an ordinary length inside a `node_modules` tree mid-install — overflows\n * that thread's stack and kills the whole host process with a native fault\n * that JS cannot catch (parcel-bundler/watcher#250).\n *\n * Plain non-glob `ignore` entries take a different native lane entirely: a\n * string prefix compare against the resolved path, safe at any length.\n *\n * The rule here is therefore: **no glob ever reaches the native layer.**\n *\n * - Subtree globs (`node_modules/**`) become plain path entries for the\n * native prefix lane. Same coverage as the glob, evaluated without regex.\n * - Every other pattern (`*.log`) is compiled with picomatch — the same\n * library parcel itself uses to build the native regex source — and\n * applied in JS to each event's root-relative path after it crosses back\n * from native code. V8 regexes don't have the native-stack failure mode.\n */\nimport picomatch from \"picomatch\";\n\n/** Result of partitioning `ignore` entries by which layer evaluates them. */\nexport interface SplitIgnorePatterns {\n /**\n * Entries safe to hand to @parcel/watcher: contain no glob syntax, so its\n * wrapper routes them to the native prefix-compare lane (`ignorePaths`),\n * which ignores the entry itself plus its whole subtree.\n */\n nativeIgnorePaths: string[];\n /**\n * Full-match regexes applied JS-side to each event's root-relative path\n * (forward-slash separators on all platforms). An event matching ANY of\n * these is dropped before buffering.\n */\n relativePathFilters: RegExp[];\n}\n\n/**\n * Matches `dir/**` / `dir/` / `dir` shapes whose `dir` part contains no glob\n * syntax — the only shapes that translate exactly to a native subtree entry.\n * (The path lane also suppresses events for `dir` itself, which the glob\n * form technically did not; strictly less noise, same protection.)\n */\nfunction asSubtreePath(pattern: string): string | null {\n const withoutSuffix = pattern.endsWith(\"/**\")\n ? pattern.slice(0, -3)\n : pattern.endsWith(\"/\")\n ? pattern.slice(0, -1)\n : pattern;\n if (withoutSuffix.length === 0) return null;\n if (picomatch.scan(withoutSuffix).isGlob) return null;\n return withoutSuffix;\n}\n\n/**\n * Partition raw `ignore` entries into native-safe path entries and JS-side\n * relative-path regexes. Glob semantics are preserved from parcel's own\n * wrapper (picomatch, `dot: true`, full-string match) — e.g. `*.log` still\n * matches only root-level `.log` files, `**/*.log` matches at any depth.\n */\nexport function splitIgnorePatterns(\n ignore: readonly string[],\n): SplitIgnorePatterns {\n const nativeIgnorePaths: string[] = [];\n const relativePathFilters: RegExp[] = [];\n for (const pattern of ignore) {\n const subtree = asSubtreePath(pattern);\n if (subtree !== null) {\n nativeIgnorePaths.push(subtree);\n } else {\n relativePathFilters.push(picomatch.makeRe(pattern, { dot: true }));\n }\n }\n return { nativeIgnorePaths, relativePathFilters };\n}\n\n/**\n * Build a predicate deciding whether an event with absolute path `absPath`\n * (as reported by @parcel/watcher, OS-native separators) survives the\n * JS-side glob filters. Returns true when the event should be KEPT.\n */\nexport function createEventKeepFilter(\n rootRealPath: string,\n relativePathFilters: readonly RegExp[],\n): (absPath: string) => boolean {\n if (relativePathFilters.length === 0) return () => true;\n return (absPath: string) => {\n let relative = absPath.startsWith(rootRealPath)\n ? absPath.slice(rootRealPath.length)\n : absPath;\n if (process.platform === \"win32\") relative = relative.replaceAll(\"\\\\\", \"/\");\n if (relative.startsWith(\"/\")) relative = relative.slice(1);\n for (const filter of relativePathFilters) {\n if (filter.test(relative)) return false;\n }\n return true;\n };\n}\n","/**\n * Native-free filesystem scanning for the `polling` backend.\n *\n * WHY THIS FILE EXISTS (ELECTRON-25): the polling backend used to be built on\n * @parcel/watcher's `writeSnapshot()` / `getEventsSince()`. Those are N-API\n * calls into `watcher.node` — the same native binary whose Windows backend has\n * a use-after-free. So \"polling\", which the ELECTRON-25 containment treats as\n * the SAFE in-process option, was in fact running the crash-capable addon\n * inside Electron main twice a second. 50/50 sampled ELECTRON-25 minidumps\n * fault inside `watcher.node`, and the 0.0.88 ones are browser-process crashes\n * on the polling default — that path and no other.\n *\n * The fix is not to be careful with the addon; it is for the safe path to have\n * no addon at all. Hence a separate module: this file imports only `node:fs`,\n * so \"polling never touches native code\" is a property you can verify by\n * reading the import list rather than by auditing call sites. Keep it that way\n * — adding an `@parcel/watcher` import here silently re-arms the crash.\n *\n * Semantics deliberately match what the native brute-force backend provided,\n * so this is a drop-in for `watchWithPolling`:\n * - `create` / `update` / `delete` events with absolute real paths;\n * - `ignorePaths` skip the entry itself AND its subtree (parcel's\n * `ignore`-as-path lane), so heavy dirs are never descended into;\n * - symlinks are stamped but never followed, matching @parcel/watcher.\n */\nimport { lstat, readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { isAbsolute, join, resolve } from \"node:path\";\n\n/** Event shape shared with @parcel/watcher — a plain object, no native types. */\nexport interface PollingEvent {\n path: string;\n type: \"create\" | \"update\" | \"delete\";\n}\n\n/**\n * What we remember per entry. `mtimeMs`+`size` is the change signal for files;\n * directories carry zeroes because a directory's own mtime moves whenever a\n * child is added or removed, which would emit an `update` for the parent on\n * top of the child's own event.\n */\nexport interface EntryStamp {\n mtimeMs: number;\n size: number;\n isDir: boolean;\n}\n\n/** Absolute real path → stamp. */\nexport type TreeScan = Map<string, EntryStamp>;\n\n/**\n * How many `lstat` calls to keep in flight. Bounded because a poll runs on the\n * Electron main loop: unbounded fan-out over a large tree would spike the\n * libuv threadpool and stall unrelated fs work.\n */\nconst STAT_CONCURRENCY = 32;\n\n/**\n * Depth guard. Cycles are already impossible (symlinks are never followed),\n * but a pathological tree should degrade rather than exhaust memory.\n */\nconst MAX_DEPTH = 64;\n\nfunction resolveIgnoreRoots(\n realRoot: string,\n ignorePaths: readonly string[],\n): Set<string> {\n const out = new Set<string>();\n for (const entry of ignorePaths) {\n if (!entry) continue;\n out.add(isAbsolute(entry) ? resolve(entry) : resolve(realRoot, entry));\n }\n return out;\n}\n\n/**\n * Walk `realRoot` and stamp every entry. Unreadable directories are skipped\n * rather than thrown: a poll that hits one transient EPERM (a file being\n * replaced mid-scan is routine) must still report the rest of the tree.\n */\nexport async function scanTree(\n realRoot: string,\n ignorePaths: readonly string[] = [],\n): Promise<TreeScan> {\n const ignored = resolveIgnoreRoots(realRoot, ignorePaths);\n const scan: TreeScan = new Map();\n const pendingStats: string[] = [];\n const queue: Array<{ dir: string; depth: number }> = [\n { dir: realRoot, depth: 0 },\n ];\n\n for (let next = queue.pop(); next !== undefined; next = queue.pop()) {\n const { dir, depth } = next;\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n continue; // Vanished or unreadable — not this poll's problem.\n }\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (ignored.has(full)) continue;\n\n // A symlink is recorded as an entry but never descended into, so the\n // scan can't loop and can't double-report a shared subtree.\n if (entry.isDirectory() && !entry.isSymbolicLink()) {\n scan.set(full, { mtimeMs: 0, size: 0, isDir: true });\n if (depth < MAX_DEPTH) queue.push({ dir: full, depth: depth + 1 });\n } else {\n pendingStats.push(full);\n }\n }\n }\n\n for (let i = 0; i < pendingStats.length; i += STAT_CONCURRENCY) {\n const slice = pendingStats.slice(i, i + STAT_CONCURRENCY);\n await Promise.all(\n slice.map(async (path) => {\n try {\n const st = await lstat(path);\n scan.set(path, {\n mtimeMs: st.mtimeMs,\n size: st.size,\n isDir: false,\n });\n } catch {\n // Deleted between readdir and lstat — the next poll sees it gone.\n }\n }),\n );\n }\n\n return scan;\n}\n\n/**\n * Events that turn `previous` into `next`. Directories only ever produce\n * `create`/`delete` (see EntryStamp) — their stamps are constant.\n */\nexport function diffScans(previous: TreeScan, next: TreeScan): PollingEvent[] {\n const events: PollingEvent[] = [];\n for (const [path, now] of next) {\n const before = previous.get(path);\n if (!before) {\n events.push({ path, type: \"create\" });\n } else if (\n !now.isDir &&\n (before.mtimeMs !== now.mtimeMs || before.size !== now.size)\n ) {\n events.push({ path, type: \"update\" });\n }\n }\n for (const path of previous.keys()) {\n if (!next.has(path)) events.push({ path, type: \"delete\" });\n }\n return events;\n}\n\n/**\n * On-disk snapshot format. Versioned so a snapshot written by the old\n * native backend (an opaque @parcel/watcher blob) is rejected as unreadable\n * rather than misparsed — the caller then re-baselines, which costs one poll.\n */\ninterface SnapshotFile {\n version: 1;\n entries: Array<[string, number, number, 0 | 1]>;\n}\n\nexport async function writeScanSnapshot(\n snapshotPath: string,\n scan: TreeScan,\n): Promise<void> {\n const payload: SnapshotFile = {\n version: 1,\n entries: [...scan].map(([p, s]) => [p, s.mtimeMs, s.size, s.isDir ? 1 : 0]),\n };\n await writeFile(snapshotPath, JSON.stringify(payload));\n}\n\n/** The persisted scan, or null when absent/unreadable/foreign-format. */\nexport async function readScanSnapshot(\n snapshotPath: string,\n): Promise<TreeScan | null> {\n try {\n const parsed = JSON.parse(\n await readFile(snapshotPath, \"utf8\"),\n ) as SnapshotFile;\n if (parsed?.version !== 1 || !Array.isArray(parsed.entries)) return null;\n const scan: TreeScan = new Map();\n for (const [p, mtimeMs, size, isDir] of parsed.entries) {\n scan.set(p, { mtimeMs, size, isDir: isDir === 1 });\n }\n return scan;\n } catch {\n return null;\n }\n}\n","/** A busy install may take longer; only continuous silence spends this budget. */\nexport const INSTALL_IDLE_TIMEOUT_MS = 5 * 60_000;\n\nconst CONNECTION_CODE =\n /\\b(?:ETIMEDOUT|ESOCKETTIMEDOUT|ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ENETUNREACH|EHOSTUNREACH|ERR_SOCKET_TIMEOUT|UND_ERR_CONNECT_TIMEOUT|UND_ERR_SOCKET)\\b/;\nconst CERTIFICATE_CODE =\n /\\b(?:CERT_HAS_EXPIRED|CERT_NOT_YET_VALID|SELF_SIGNED_CERT_IN_CHAIN|DEPTH_ZERO_SELF_SIGNED_CERT|UNABLE_TO_VERIFY_LEAF_SIGNATURE|UNABLE_TO_GET_ISSUER_CERT_LOCALLY|ERR_TLS_CERT_ALTNAME_INVALID)\\b/;\n\n/** Package-manager evidence only; a slow install alone says nothing about connectivity. */\nexport function describeInstallConnectivity(output: string): {\n code: string;\n hint: string;\n} | null {\n const relevant = output\n .replace(/\\u001b\\[[0-9;]*m/g, \"\")\n .split(/\\r?\\n/)\n .filter((line) => !/\\b(?:warn|warning)\\s+deprecated\\b/i.test(line))\n .join(\"\\n\");\n // A terminal npm/pnpm error outranks earlier recoverable warnings. pnpm\n // sometimes wraps the actual network code in ERR_PNPM_*; its generic\n // META_FETCH_FAIL instead carries the cause in the same failure's detail.\n const terminal = [\n ...relevant.matchAll(/npm (?:error|ERR!) code\\s+(\\w+)|\\bERR_PNPM_(\\w+)/g),\n ].at(-1);\n const terminalCode = terminal?.[1] ?? terminal?.[2];\n const failureDetail =\n terminal?.[2] === \"META_FETCH_FAIL\"\n ? relevant.slice(terminal.index)\n : (terminalCode ?? relevant);\n if (\n terminalCode &&\n terminalCode !== \"META_FETCH_FAIL\" &&\n !CONNECTION_CODE.test(terminalCode) &&\n !CERTIFICATE_CODE.test(terminalCode)\n )\n return null;\n const certificate = failureDetail.match(CERTIFICATE_CODE)?.[0];\n if (certificate)\n return {\n code: certificate,\n hint: \"Check your proxy or registry certificate trust, then retry.\",\n };\n const connection = failureDetail.match(CONNECTION_CODE)?.[0];\n if (connection)\n return {\n code: connection,\n hint: \"Check your internet connection, VPN, or proxy, then retry.\",\n };\n return null;\n}\n","{\n \"name\": \"tempo-sdk\",\n \"version\": \"0.0.51\",\n \"type\": \"module\",\n \"description\": \"Tempo SDK — Vite, Next.js, and Expo plugins for JSX annotation and shared page/storyboard types\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/TempoLabsAI/tempo-monorepo.git\",\n \"directory\": \"tempo-modules/tempo-sdk\"\n },\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./canvas\": {\n \"types\": \"./dist/canvas/index.d.ts\",\n \"import\": \"./dist/canvas/index.js\"\n },\n \"./canvas/svelte\": {\n \"types\": \"./dist/canvas/svelte.d.ts\",\n \"import\": \"./dist/canvas/svelte.js\"\n },\n \"./canvas/interact-projection\": {\n \"types\": \"./dist/canvas/interact-projection.d.ts\",\n \"import\": \"./dist/canvas/interact-projection.js\"\n },\n \"./canvas/parser\": {\n \"types\": \"./dist/canvas/parser.d.ts\",\n \"import\": \"./dist/canvas/parser.js\"\n },\n \"./canvas/editor\": {\n \"types\": \"./dist/canvas/editor.d.ts\",\n \"import\": \"./dist/canvas/editor.js\"\n },\n \"./assets\": {\n \"types\": \"./dist/assets/index.d.ts\",\n \"import\": \"./dist/assets/index.js\"\n },\n \"./assets/parser\": {\n \"types\": \"./dist/assets/parser.d.ts\",\n \"import\": \"./dist/assets/parser.js\"\n },\n \"./assets/editor\": {\n \"types\": \"./dist/assets/editor.d.ts\",\n \"import\": \"./dist/assets/editor.js\"\n },\n \"./nextjs\": {\n \"types\": \"./dist/nextjs.d.ts\",\n \"import\": \"./dist/nextjs.js\",\n \"require\": \"./dist/nextjs.cjs\"\n },\n \"./nextjs/plugin\": {\n \"types\": \"./dist/nextjs/plugin.d.ts\",\n \"import\": \"./dist/nextjs/plugin.js\"\n },\n \"./expo\": {\n \"types\": \"./dist/expo-babel-plugin.d.ts\",\n \"import\": \"./dist/expo-babel-plugin.js\",\n \"require\": \"./dist/expo-babel-plugin.cjs\"\n },\n \"./expo/plugin\": {\n \"types\": \"./dist/expo/plugin.d.ts\",\n \"import\": \"./dist/expo/plugin.js\",\n \"require\": \"./dist/expo/plugin.cjs\"\n },\n \"./svelte\": {\n \"types\": \"./dist/svelte.d.ts\",\n \"import\": \"./dist/svelte.js\"\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"clean\": \"rm -rf dist\",\n \"build\": \"pnpm run clean && tsup\",\n \"build:if-missing\": \"test -f dist/index.js || pnpm run build\",\n \"prepare\": \"pnpm run build\",\n \"prepublishOnly\": \"pnpm build\",\n \"demo\": \"TEMPO=true pnpm --filter @tempo-modules/vite-annotate-plugin-demo dev\",\n \"demo:nextjs\": \"TEMPO=true pnpm --filter @tempo-modules/tempo-sdk-nextjs-demo dev\",\n \"test\": \"vitest run\",\n \"test:browser\": \"vitest run asset-live-preview-browser.test.ts asset-popover-lifecycle-browser.test.ts component-controls-browser.test.ts canvas-controls-browser.test.ts canvas-capture-determinism-browser.test.ts canvas-host-vite-e2e.test.ts canvas-host-packed-sdk-e2e.test.ts error-tracking-e2e.test.ts\",\n \"test:watch\": \"vitest\",\n \"test:nextjs:unit\": \"vitest run nextjs-loader.test.ts nextjs-wrapper.test.ts nextjs-fixtures.test.ts nextjs-published.test.ts\",\n \"test:nextjs:integration\": \"vitest run nextjs-e2e-correctness.test.ts\",\n \"test:nextjs:production\": \"vitest run nextjs-production.test.ts\",\n \"test:nextjs\": \"vitest run nextjs-loader.test.ts nextjs-wrapper.test.ts nextjs-fixtures.test.ts nextjs-published.test.ts nextjs-e2e-correctness.test.ts nextjs-production.test.ts\"\n },\n \"dependencies\": {\n \"@rollup/pluginutils\": \"^5.1.0\",\n \"oxc-parser\": \"^0.110.0\"\n },\n \"peerDependencies\": {\n \"@babel/core\": \"^7.0.0\",\n \"next\": \"^13 || ^14 || ^15 || ^16\",\n \"react\": \"^18 || ^19\",\n \"svelte\": \"^5.0.0\",\n \"vite\": \"^5 || ^6 || ^7 || ^8\"\n },\n \"peerDependenciesMeta\": {\n \"@babel/core\": {\n \"optional\": true\n },\n \"next\": {\n \"optional\": true\n },\n \"svelte\": {\n \"optional\": true\n },\n \"vite\": {\n \"optional\": true\n }\n },\n \"devDependencies\": {\n \"@babel/core\": \"^7.29.0\",\n \"@babel/types\": \"^7.29.0\",\n \"@modules/annotation\": \"workspace:*\",\n \"@modules/file-parser\": \"workspace:*\",\n \"@modules/svelte-editor\": \"workspace:*\",\n \"@types/babel__core\": \"^7.20.5\",\n \"@types/node\": \"^25.0.8\",\n \"@types/react\": \"catalog:\",\n \"@types/react-dom\": \"catalog:\",\n \"@vitejs/plugin-react\": \"catalog:\",\n \"playwright\": \"^1.58.2\",\n \"react-dom\": \"catalog:\",\n \"svelte\": \"5.56.9\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"catalog:\",\n \"vite\": \"catalog:\",\n \"vitest\": \"catalog:\"\n }\n}\n","import tempoSdkPackage from \"../tempo-sdk/package.json\" with { type: \"json\" };\n\n/** Strict x.y.z parse; anything else is treated as not satisfying the range. */\nfunction parseVersionTriple(\n version: string,\n): readonly [number, number, number] | null {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(version.trim());\n if (!match) return null;\n return [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\n/**\n * The CanvasHost contract this client requires from the SDK reported by the\n * running host's `/__tempo/identity` response. The served echo is authoritative\n * because a custom launcher may use a container dependency tree unrelated to\n * the supervisor machine's `node_modules`.\n *\n * The floor has moved twice, each time because an older SDK degrades\n * silently instead of erroring: 0.0.20 was the first version whose\n * CanvasHost supports literal canvas module loaders plus the in-document\n * recovery feed the generated Vite recovery adapter subscribes to. 0.0.22\n * is required because the frame preload's projection producer only opens\n * its watch after the SDK stamps `data-render-admitted=\"true\"` on the\n * storyboard content slot (two-phase admission, PR #2118) — an SDK older\n * than 0.0.22 never stamps that attribute, so the canvas hangs on \"Loading\n * Storyboards\" forever with no error.\n *\n * The floor is DERIVED from the workspace tempo-sdk's package version, not a\n * hand-maintained constant. The devserver and tempo-sdk ship in one client\n * artifact (this module is raw TS bundled into the Electron main process\n * alongside the sibling SDK), so the tempo-sdk version present in the\n * checkout at build time is by definition the contract this client was built\n * against. A wire-contract change already requires bumping that version\n * (publishing is a no-op otherwise), so the bump that ships the new contract\n * moves the floor with it — there is no second constant to forget.\n */\nexport const REQUIRED_CANVAS_HOST_SDK_RANGE = `>=${tempoSdkPackage.version}`;\n\nconst MINIMUM_CANVAS_HOST_SDK_VERSION = (() => {\n const parsed = parseVersionTriple(tempoSdkPackage.version);\n // A malformed workspace SDK version must fail builds and tests loudly at\n // module init, never silently degrade the compatibility check at runtime.\n if (!parsed) {\n throw new Error(\n `tempo-sdk package version \"${tempoSdkPackage.version}\" is not a strict x.y.z version; cannot derive the CanvasHost SDK floor`,\n );\n }\n return parsed;\n})();\n\nexport function tempoSdkVersionSatisfiesCanvasHost(version: string): boolean {\n const parsed = parseVersionTriple(version);\n if (!parsed) return false;\n for (let i = 0; i < 3; i += 1) {\n if (parsed[i] > MINIMUM_CANVAS_HOST_SDK_VERSION[i]) return true;\n if (parsed[i] < MINIMUM_CANVAS_HOST_SDK_VERSION[i]) return false;\n }\n return true;\n}\n","// The emission source for the generated `.tempo/canvas-host-recovery.ts`\n// module on EXPO/Metro hosts. `canvas-host-recovery-metro-template.ts` is the\n// readable, unit-tested source of truth; this constant is the byte-for-byte\n// copy the reconcile writer emits, kept in sync by an equality test (bundled\n// consumers cannot read the template file from disk at runtime).\nexport const CANVAS_HOST_RECOVERY_METRO_MODULE_SOURCE = `/* THIS FILE IS AUTO-GENERATED BY TEMPO. DO NOT EDIT. */\n/*\n * CanvasHost Metro (Expo) recovery adapter.\n *\n * Expo serves its web dev bundle with Metro LAZY BUNDLING on, which keeps\n * every \\`import()\\`-reachable module out of the bundle until a separate\n * request asks for it. The generated host wrapper registers each canvas as\n * \\`lazy(() => import(...))\\`, so a canvas demand-registered while this\n * document is already live arrives as a Fast Refresh swap of the wrapper\n * that references a canvas module this client never received:\n * \\`Requiring unknown module \"N\"\\`. \\`CanvasImportBoundary\\` catches it, so the\n * storyboard just stays blank — permanently, because the wrapper DID update,\n * so the runtime advertises the canvas in \\`canvasPaths\\`, the component\n * pool's staleness check is satisfied, and nothing ever reloads the document\n * that a full page load would rebuild the bundle for. See TEM-1410.\n *\n * The repair is one document reload, and the ONLY question is when it is\n * safe to issue. The wrapper stamps a generation counter on every\n * evaluation: 1 on a cold document load, 2+ once Fast Refresh has swapped\n * it. That single bit is what separates the two failure shapes:\n *\n * - Generation 1 (COLD): the canvas module was in the graph when the\n * bundle was built, so a load failure here is the canvas genuinely\n * being broken — an unresolvable import, a module that throws at\n * evaluation. A reload would rebuild the same broken bundle, so this\n * adapter deliberately does NOTHING. The failure stays scoped to its\n * own canvas through the SDK's import boundary and every sibling canvas\n * keeps rendering. This is the isolation invariant \\`buildTempoHostWrapper\\`\n * exists to protect; do not \"fix\" a cold failure by reloading.\n *\n * - Generation 2+ (HOT): the wrapper was swapped after this bundle was\n * built, so a load failure is the lazy-bundling gap above — the module\n * is missing from THIS client, not broken. Exactly one reload rebuilds\n * the bundle with it included.\n *\n * Termination is structural, not a counter: a reload produces a fresh\n * document at generation 1, where this adapter is inert by construction. A\n * genuinely broken canvas therefore costs at most one reload and then stays\n * an isolated, boundary-scoped failure — there is no reload loop to bound.\n *\n * Deliberately NOT used here: ESM import metadata (Metro bundles web as a\n * classic script and does not transform it, so a module referencing it throws\n * on load — the same hazard the sidecar's metro config disables package\n * exports to avoid; the equality test pins its absence), Metro/React-Refresh\n * internals, module paths, cycle payloads, or owner identity. The adapter\n * reads one global integer and one product fact off the shared feed.\n *\n * This module installs itself exactly once per document: the generated host\n * wrapper imports it for side effects only, so wrapper re-evaluation under\n * Fast Refresh reuses the cached module instead of stacking listeners.\n */\n\ntype CanvasHostRecoveryEvent = {\n type: \"module-load-failure\" | \"module-update-dropped\";\n /** Failed module path. Diagnostic only — this adapter never matches on it. */\n path: string;\n message?: string;\n};\n\ntype CanvasHostRecoveryFeed = {\n publish(event: CanvasHostRecoveryEvent): void;\n subscribe(listener: (event: CanvasHostRecoveryEvent) => void): () => void;\n};\n\n// Mirror of tempo-sdk's create-if-absent feed bootstrap: whichever side\n// evaluates first creates the bus, the other reuses it, so evaluation order\n// between the SDK and this generated module never matters.\nfunction getCanvasHostRecoveryFeed(): CanvasHostRecoveryFeed {\n const globals = globalThis as typeof globalThis & {\n __tempoCanvasHostRecoveryFeed?: CanvasHostRecoveryFeed;\n };\n if (!globals.__tempoCanvasHostRecoveryFeed) {\n const listeners = new Set<(event: CanvasHostRecoveryEvent) => void>();\n globals.__tempoCanvasHostRecoveryFeed = {\n publish(event) {\n for (const listener of Array.from(listeners)) listener(event);\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n }\n return globals.__tempoCanvasHostRecoveryFeed;\n}\n\n/** Global the generated host wrapper increments on every evaluation. 1 is a\n * cold document load; 2+ means Fast Refresh swapped the wrapper. */\nexport const WRAPPER_GENERATION_KEY = \"__tempoCanvasHostWrapperGeneration\";\n\nfunction readWrapperGeneration(): number {\n const value = (globalThis as Record<string, unknown>)[WRAPPER_GENERATION_KEY];\n return typeof value === \"number\" && Number.isFinite(value) ? value : 0;\n}\n\n/** How long the reload decision waits after a failure, so a burst of\n * per-canvas failures from one wrapper swap collapses into a single reload\n * instead of racing each other. */\nconst RELOAD_DECISION_GRACE_MS = 100;\n\nexport type CanvasHostMetroRecoveryOptions = {\n /** Test seam; defaults to reloading this document. */\n reload?: () => void;\n /** Test seam; defaults to the shared in-document recovery feed. */\n feed?: CanvasHostRecoveryFeed;\n /** Test seam; defaults to the wrapper-stamped global. */\n readGeneration?: () => number;\n};\n\nexport function installCanvasHostMetroRecovery(\n options: CanvasHostMetroRecoveryOptions = {},\n): () => void {\n const feed = options.feed ?? getCanvasHostRecoveryFeed();\n const readGeneration = options.readGeneration ?? readWrapperGeneration;\n const reload =\n options.reload ??\n (() => {\n window.location.reload();\n });\n\n let reloadTimer: ReturnType<typeof setTimeout> | null = null;\n let reloadIssued = false;\n let feedEventsSeen = 0;\n let coldFailuresIgnored = 0;\n let installed = true;\n\n // Always-current diagnostic mirror (the getters close over the adapter's\n // live variables). Read by e2e suites and bug-report tooling via frame\n // evaluation; never consumed by the adapter itself.\n (\n globalThis as typeof globalThis & {\n __tempoCanvasHostRecoveryDebug?: unknown;\n }\n ).__tempoCanvasHostRecoveryDebug = {\n adapter: \"metro\",\n get installed() {\n return installed;\n },\n get wrapperGeneration() {\n return readGeneration();\n },\n get reloadIssued() {\n return reloadIssued;\n },\n get feedEventsSeen() {\n return feedEventsSeen;\n },\n get coldFailuresIgnored() {\n return coldFailuresIgnored;\n },\n };\n\n const unsubscribe = feed.subscribe((event) => {\n feedEventsSeen += 1;\n // \"module-update-dropped\" is Vite vocabulary (a dropped hot update whose\n // module fetch failed); Metro never publishes it. Ignore rather than\n // treat an unrecognized flavor as a reload trigger.\n if (event.type !== \"module-load-failure\") return;\n if (reloadIssued || reloadTimer !== null) return;\n // Cold failure: the module was in the bundle and genuinely failed. A\n // reload rebuilds the same failure and would take every sibling canvas\n // down with it. Leave it to the import boundary.\n if (readGeneration() <= 1) {\n coldFailuresIgnored += 1;\n return;\n }\n reloadTimer = setTimeout(() => {\n reloadTimer = null;\n if (reloadIssued) return;\n reloadIssued = true;\n reload();\n }, RELOAD_DECISION_GRACE_MS);\n });\n\n return () => {\n installed = false;\n unsubscribe();\n if (reloadTimer !== null) {\n clearTimeout(reloadTimer);\n reloadTimer = null;\n }\n };\n}\n\n// Self-install: this generated module is imported for side effects by the\n// host wrapper. Guarded so a Node test import of the template stays side-\n// effect free.\nif (typeof window !== \"undefined\") {\n installCanvasHostMetroRecovery();\n}\n`;\n","// The emission source for the generated `.tempo/canvas-host-recovery.ts`\n// module. `canvas-host-recovery-template.ts` is the readable, unit-tested\n// source of truth; this constant is the byte-for-byte copy the reconcile\n// writer emits, kept in sync by an equality test (bundled consumers cannot\n// read the template file from disk at runtime).\nexport const CANVAS_HOST_RECOVERY_MODULE_SOURCE = `/* THIS FILE IS AUTO-GENERATED BY TEMPO. DO NOT EDIT. */\n/*\n * CanvasHost Vite recovery adapter.\n *\n * Vite and React own ordinary module propagation and Fast Refresh. This\n * adapter closes exactly one verified hole class — a module this document\n * needs failed to load and Vite will not converge it in place — and the\n * repair action is always a document reload. Two failure flavors arrive as\n * product facts over the in-document recovery feed shared with tempo-sdk,\n * and they differ in what a later HMR cycle can prove:\n *\n * - \"module-load-failure\": convergence in place is impossible. A\n * dependency's top-level evaluation threw (the throw prevented\n * plugin-react's accept footer from registering, so even a successful\n * re-fetch of the repaired module updates nothing), or a canvas's\n * cold-start import rejected. The first LATER cycle (a user edit that\n * plausibly repairs the code) triggers at most one reload — a\n * successful retry of the module is deliberately NOT trusted as\n * recovery, because with the accepts lost it heals nothing.\n *\n * - \"module-update-dropped\": Vite dropped a hot update whose module\n * fetch failed (accept callbacks ran with \\`undefined\\`; the update is\n * never retried by Vite itself). Here later lifecycle traffic carries a\n * real verdict, read from Vite's own payloads: a cycle that RETRIED\n * the dropped path resolves it (on success Vite just healed it in\n * place — during active editing the same file is usually re-saved\n * within the next beat, and reloading would be pure churn; on failure\n * the fresh event re-arms right behind the cycle), a PRUNE of the path\n * resolves it (the module left the graph, so its staleness is moot),\n * and a cycle that did neither can never heal it, proving the\n * staleness permanent: trigger at most one reload. The reload decision\n * itself waits one short beat, because Vite delivers a delete burst as\n * an importer update followed by the prune in a separate message.\n *\n * A reload creates a fresh document with a fresh adapter — if code is still\n * broken, the new document re-latches independently and waits for another\n * edit. Which canvases show which problems stays owner-scoped in the SDK\n * problem tracker; the adapter never models owners.\n *\n * It deliberately contains no importer traversal, source hashes, polling,\n * deadlines, or transaction receipts; the only path matching is the exact\n * identity between a dropped update's module path and Vite's own payload\n * paths. A clean error feed or a completed Vite update is never treated as\n * success for the load-failure flavor.\n *\n * This module installs itself exactly once per document: the generated host\n * wrapper imports it for side effects only, so wrapper re-evaluation under\n * HMR reuses the cached module instead of stacking listeners. If Tempo\n * rewrites this managed file, Vite re-evaluates it and the previous\n * instance's \\`hot.dispose\\` teardown removes its listeners first.\n */\n\ntype CanvasHostRecoveryEvent = {\n type: \"module-load-failure\" | \"module-update-dropped\";\n /** Failed module path. Diagnostic for load failures; for dropped updates\n * it is matched against Vite update payload paths, so it must be the\n * exact accepted-module path Vite reported. */\n path: string;\n message?: string;\n};\n\ntype CanvasHostRecoveryFeed = {\n publish(event: CanvasHostRecoveryEvent): void;\n subscribe(listener: (event: CanvasHostRecoveryEvent) => void): () => void;\n};\n\n// Mirror of tempo-sdk's create-if-absent feed bootstrap: whichever side\n// evaluates first creates the bus, the other reuses it, so evaluation order\n// between the SDK and this generated module never matters.\nfunction getCanvasHostRecoveryFeed(): CanvasHostRecoveryFeed {\n const globals = globalThis as typeof globalThis & {\n __tempoCanvasHostRecoveryFeed?: CanvasHostRecoveryFeed;\n };\n if (!globals.__tempoCanvasHostRecoveryFeed) {\n const listeners = new Set<(event: CanvasHostRecoveryEvent) => void>();\n globals.__tempoCanvasHostRecoveryFeed = {\n publish(event) {\n for (const listener of Array.from(listeners)) listener(event);\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n }\n return globals.__tempoCanvasHostRecoveryFeed;\n}\n\ntype RenderErrorRecoveryBeatBus = {\n emit(): void;\n subscribe(listener: () => void): () => void;\n};\n\n// Mirror of tempo-sdk's create-if-absent render-error-recovery beat bus.\n// Emitting on each completed Vite update announces \"new code may have\n// arrived\" to errored storyboard boundaries, which retry immediately instead\n// of waiting out their backoff timer. The SDK cannot observe Vite itself:\n// its built bundle is force-resolved into Expo's Metro web bundle, where a\n// literal \\`import.meta\\` is a whole-document parse error (TEM-1409), so this\n// adapter — the only party that owns \\`import.meta.hot\\` — is the beat's\n// producer.\nfunction getRenderErrorRecoveryBeatBus(): RenderErrorRecoveryBeatBus {\n const globals = globalThis as typeof globalThis & {\n __tempoRenderErrorRecoveryBeatBus?: RenderErrorRecoveryBeatBus;\n };\n if (!globals.__tempoRenderErrorRecoveryBeatBus) {\n const listeners = new Set<() => void>();\n globals.__tempoRenderErrorRecoveryBeatBus = {\n emit() {\n for (const listener of Array.from(listeners)) listener();\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n }\n return globals.__tempoRenderErrorRecoveryBeatBus;\n}\n\ntype ViteHotLike = {\n on?: (event: string, callback: (payload?: unknown) => void) => void;\n off?: (event: string, callback: (payload?: unknown) => void) => void;\n dispose?: (callback: (data: unknown) => void) => void;\n};\n\n/** Module paths a Vite update payload touched: both the changed path and the\n * accepting boundary, since dropped-update events name the accepted module. */\nfunction readUpdatePayloadPaths(payload: unknown): Set<string> {\n const paths = new Set<string>();\n const updates = (payload as { updates?: unknown })?.updates;\n if (!Array.isArray(updates)) return paths;\n for (const update of updates) {\n const path = (update as { path?: unknown })?.path;\n if (typeof path === \"string\") paths.add(path);\n const acceptedPath = (update as { acceptedPath?: unknown })?.acceptedPath;\n if (typeof acceptedPath === \"string\") paths.add(acceptedPath);\n }\n return paths;\n}\n\n/** How long a reload decision waits for trailing same-burst messages (a\n * delete's prune follows its importer update in a separate ws message)\n * before committing. Resolution during the wait cancels the reload. */\nconst RELOAD_DECISION_GRACE_MS = 100;\n\nexport type CanvasHostViteRecoveryOptions = {\n hot: ViteHotLike | undefined;\n /** Test seam; defaults to reloading this document. */\n reload?: () => void;\n /** Test seam; defaults to the shared in-document recovery feed. */\n feed?: CanvasHostRecoveryFeed;\n};\n\nexport function installCanvasHostViteRecovery(\n options: CanvasHostViteRecoveryOptions,\n): () => void {\n const hot = options.hot;\n // Production builds and non-Vite documents have no HMR lifecycle to\n // observe, and therefore no missed-accept hole to repair.\n if (!hot || typeof hot.on !== \"function\") return () => {};\n const feed = options.feed ?? getCanvasHostRecoveryFeed();\n const reload =\n options.reload ??\n (() => {\n window.location.reload();\n });\n\n let hmrCycle = 0;\n // A cold-start failure is naturally recorded at cycle 0. Later failures\n // never advance the latch: the earliest unresolved failure decides when a\n // subsequent edit counts as a repair attempt.\n let documentFailedAtCycle: number | null = null;\n let unconvergeableFailure = false;\n const droppedUpdatePaths = new Set<string>();\n let reloadTimer: ReturnType<typeof setTimeout> | null = null;\n let reloadIssued = false;\n let feedEventsSeen = 0;\n let installed = true;\n\n // Always-current diagnostic mirror of the latch state (the getters close\n // over the adapter's live variables). Read by e2e suites and bug-report\n // tooling via frame evaluation; never consumed by the adapter itself.\n (\n globalThis as typeof globalThis & {\n __tempoCanvasHostRecoveryDebug?: unknown;\n }\n ).__tempoCanvasHostRecoveryDebug = {\n get installed() {\n return installed;\n },\n get hmrCycle() {\n return hmrCycle;\n },\n get documentFailedAtCycle() {\n return documentFailedAtCycle;\n },\n get unconvergeableFailure() {\n return unconvergeableFailure;\n },\n get droppedUpdatePaths() {\n return Array.from(droppedUpdatePaths);\n },\n get reloadIssued() {\n return reloadIssued;\n },\n get feedEventsSeen() {\n return feedEventsSeen;\n },\n };\n\n const latchArmed = (): boolean =>\n unconvergeableFailure || droppedUpdatePaths.size > 0;\n\n // Every dropped path resolved (retried or pruned) with no load failure on\n // record: the document converged without us; stand down entirely.\n const settleIfResolved = (): void => {\n if (latchArmed()) return;\n documentFailedAtCycle = null;\n if (reloadTimer !== null) {\n clearTimeout(reloadTimer);\n reloadTimer = null;\n }\n };\n\n const unsubscribe = feed.subscribe((event) => {\n feedEventsSeen += 1;\n if (event.type === \"module-load-failure\") {\n unconvergeableFailure = true;\n } else if (event.type === \"module-update-dropped\") {\n droppedUpdatePaths.add(event.path);\n } else {\n return;\n }\n if (documentFailedAtCycle === null) documentFailedAtCycle = hmrCycle;\n });\n\n const onBeforeUpdate = (): void => {\n hmrCycle += 1;\n };\n // afterUpdate is only the trigger for a later repair attempt — never\n // evidence that anything recovered. A same-cycle failure (the failing\n // update itself) must not trigger the reload; only a later cycle may.\n const onAfterUpdate = (payload?: unknown): void => {\n // Unconditional: every completed update is a \"new code\" beat for the\n // SDK's errored boundaries, independent of this adapter's reload latch.\n getRenderErrorRecoveryBeatBus().emit();\n if (documentFailedAtCycle === null) return;\n if (hmrCycle <= documentFailedAtCycle) return;\n if (reloadIssued) return;\n // A retried dropped path leaves the latch: Vite either healed it in\n // place, or the retry's own failure event re-arms right behind this\n // cycle. Load failures never resolve this way, and a dropped path this\n // later cycle did NOT retry proves an unconvergeable document — after\n // one beat of grace for the burst's trailing prune message.\n if (!unconvergeableFailure) {\n for (const path of readUpdatePayloadPaths(payload)) {\n droppedUpdatePaths.delete(path);\n }\n settleIfResolved();\n if (documentFailedAtCycle === null) return;\n }\n if (reloadTimer !== null) return;\n reloadTimer = setTimeout(() => {\n reloadTimer = null;\n if (!latchArmed() || reloadIssued) return;\n reloadIssued = true;\n reload();\n }, RELOAD_DECISION_GRACE_MS);\n };\n // A pruned module left the graph: its dropped update can no longer matter.\n const onBeforePrune = (payload?: unknown): void => {\n const paths = (payload as { paths?: unknown })?.paths;\n if (!Array.isArray(paths)) return;\n for (const path of paths) {\n if (typeof path === \"string\") droppedUpdatePaths.delete(path);\n }\n if (!unconvergeableFailure) settleIfResolved();\n };\n\n hot.on(\"vite:beforeUpdate\", onBeforeUpdate);\n hot.on(\"vite:afterUpdate\", onAfterUpdate);\n hot.on(\"vite:beforePrune\", onBeforePrune);\n\n return () => {\n installed = false;\n unsubscribe();\n if (reloadTimer !== null) {\n clearTimeout(reloadTimer);\n reloadTimer = null;\n }\n hot.off?.(\"vite:beforeUpdate\", onBeforeUpdate);\n hot.off?.(\"vite:afterUpdate\", onAfterUpdate);\n hot.off?.(\"vite:beforePrune\", onBeforePrune);\n };\n}\n\n// Self-install: this generated module is imported for side effects by the\n// host wrapper. \\`import.meta.hot\\` is undefined in production builds and in\n// Node test imports of the template, so both stay side-effect free.\nconst selfHot = (import.meta as { hot?: ViteHotLike }).hot;\nif (selfHot) {\n const uninstall = installCanvasHostViteRecovery({ hot: selfHot });\n selfHot.dispose?.(() => {\n uninstall();\n });\n}\n`;\n","/**\n * Canvas host source generation helpers.\n *\n * V2 canvases are normal React default exports. The devserver registers\n * requested `index.canvas.tsx` files and gives `tempo-host.tsx` enough import\n * metadata to render them directly under `CanvasHost`.\n */\n\nimport { mkdir, readdir, readFile, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n atomicWriteText as hardenedAtomicWriteText,\n ensureTrailingNewline,\n} from \"./atomic-write\";\nimport { IoFailedError } from \"./errors\";\nimport { parsePageSource } from \"../canvas-pages/page-parser\";\n\nexport { ensureTrailingNewline };\n\n// ── Path utilities ───────────────────────────────────────────────────────────\n\nexport function toPosixPath(value: string): string {\n return value.replace(/\\\\/g, \"/\");\n}\n\n/** Produce a relative import specifier from `fromDir` to `absolutePath`. */\nexport function toRelativeImportSpecifier(\n fromDir: string,\n absolutePath: string,\n): string {\n const relativePath = toPosixPath(path.relative(fromDir, absolutePath));\n const extensionlessPath = relativePath.replace(/\\.(tsx|ts|jsx|js)$/, \"\");\n return extensionlessPath.startsWith(\".\")\n ? extensionlessPath\n : `./${extensionlessPath}`;\n}\n\n// ── File utilities ───────────────────────────────────────────────────────────\n\nexport async function readTextFileOptional(\n filePath: string,\n): Promise<string | null> {\n try {\n return await readFile(filePath, \"utf-8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return null;\n }\n throw error;\n }\n}\n\n/**\n * Atomic write for generated canvas-host files. Delegates to the shared\n * hardened writer (rename retry + in-place fallback on Windows transient locks)\n * and preserves the `IoFailedError` contract for failures that are genuinely\n * non-transient.\n */\nexport async function atomicWriteText(\n filePath: string,\n content: string,\n): Promise<void> {\n try {\n await hardenedAtomicWriteText(filePath, content);\n } catch (error) {\n throw new IoFailedError(\n filePath,\n error instanceof Error ? error.message : String(error),\n );\n }\n}\n\n// ── Discovery ────────────────────────────────────────────────────────────────\n// Diagnostics/listing still need to enumerate all canvases. Host generation is\n// demand-driven through CanvasRegistry.ensureReactCanvasHostEntry().\n\nconst CANVAS_FILE_NAME = \"index.canvas.tsx\";\nexport const REQUESTED_CANVASES_FILE_NAME = \".canvas-host-requested.json\";\n\nexport async function scanCanvasFiles(pagesDir: string): Promise<string[]> {\n const found: string[] = [];\n await walkPagesDir(pagesDir, found);\n found.sort();\n return found;\n}\n\nasync function walkPagesDir(\n directory: string,\n collector: string[],\n): Promise<void> {\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw error;\n }\n for (const entry of entries) {\n const fullPath = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n await walkPagesDir(fullPath, collector);\n } else if (entry.isFile() && entry.name === CANVAS_FILE_NAME) {\n collector.push(fullPath);\n }\n }\n}\n\nexport type ReactCanvasHostEntry = {\n importPath: string;\n importSpecifier: string;\n};\n\n// ── CanvasRegistry ───────────────────────────────────────────────────────────\n\nexport class CanvasRegistry {\n private readonly projectRoot: string;\n private readonly tempoRoot: string;\n private readonly requestedCanvasesPath: string;\n private readonly pruneRecheckDelayMs: number;\n private pendingRequestedCanvasPaths: string[] | null = null;\n private readonly requestedThisSession = new Set<string>();\n private requestedCanvasesQueue: Promise<void> = Promise.resolve();\n\n constructor(\n projectRoot: string,\n tempoRoot: string,\n _pagesDir: string,\n options?: { pruneRecheckDelayMs?: number },\n ) {\n this.projectRoot = projectRoot;\n this.tempoRoot = tempoRoot;\n this.requestedCanvasesPath = path.join(\n tempoRoot,\n \".tempo\",\n REQUESTED_CANVASES_FILE_NAME,\n );\n this.pruneRecheckDelayMs = options?.pruneRecheckDelayMs ?? 50;\n }\n\n dispose(): void {\n // No long-lived resources held; method kept for API symmetry with the\n // owning TempoDevServer's dispose path.\n }\n\n private async runRequestedCanvasesExclusive<T>(\n operation: () => Promise<T>,\n ): Promise<T> {\n const previous = this.requestedCanvasesQueue;\n let releaseCurrent!: () => void;\n this.requestedCanvasesQueue = new Promise<void>((resolve) => {\n releaseCurrent = resolve;\n });\n await previous;\n try {\n return await operation();\n } finally {\n releaseCurrent();\n }\n }\n\n private buildEntry(absolutePath: string): ReactCanvasHostEntry {\n return {\n importPath: toPosixPath(path.relative(this.projectRoot, absolutePath)),\n importSpecifier: toRelativeImportSpecifier(\n path.join(this.tempoRoot, \".tempo\"),\n absolutePath,\n ),\n };\n }\n\n async resetRequestedCanvases(): Promise<void> {\n await this.runRequestedCanvasesExclusive(async () => {\n this.pendingRequestedCanvasPaths = null;\n this.requestedThisSession.clear();\n await rm(this.requestedCanvasesPath, { force: true });\n });\n }\n\n /**\n * Drop demand registrations inherited from an older host session without\n * erasing canvases already requested through this registry instance.\n *\n * Canvas admission may run while first-time initialization is queued. The\n * registry queue makes both orderings safe: a reset that wins clears the old\n * file before admission writes it, while a reset that follows admission\n * observes the current-session request and preserves the file.\n */\n async resetStaleRequestedCanvases(): Promise<void> {\n await this.runRequestedCanvasesExclusive(async () => {\n const requestedPaths = [...this.requestedThisSession];\n this.pendingRequestedCanvasPaths = requestedPaths;\n if (requestedPaths.length === 0) {\n await rm(this.requestedCanvasesPath, { force: true });\n } else {\n await this.writeRequestedCanvasPaths(requestedPaths);\n }\n this.pendingRequestedCanvasPaths = null;\n });\n }\n\n private requestedPathToAbsolutePath(relativePath: string): string | null {\n if (typeof relativePath !== \"string\" || relativePath.length === 0) {\n return null;\n }\n\n const absolutePath = path.resolve(this.projectRoot, relativePath);\n const normalizedRelative = toPosixPath(\n path.relative(this.projectRoot, absolutePath),\n );\n if (\n normalizedRelative === \"..\" ||\n normalizedRelative.startsWith(\"../\") ||\n path.isAbsolute(normalizedRelative)\n ) {\n return null;\n }\n return absolutePath;\n }\n\n private toRequestedPath(absolutePath: string): string {\n return toPosixPath(path.relative(this.projectRoot, absolutePath));\n }\n\n private async readRequestedCanvasPaths(): Promise<string[]> {\n const source = await readTextFileOptional(this.requestedCanvasesPath);\n if (!source) return [];\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(source);\n } catch {\n return [];\n }\n\n const rawCanvases = (parsed as { canvases?: unknown }).canvases;\n if (!Array.isArray(rawCanvases)) return [];\n\n const requestedPaths: string[] = [];\n const seen = new Set<string>();\n for (const value of rawCanvases) {\n if (typeof value !== \"string\") continue;\n const absolutePath = this.requestedPathToAbsolutePath(value);\n if (!absolutePath) continue;\n const requestedPath = this.toRequestedPath(absolutePath);\n if (seen.has(requestedPath)) continue;\n seen.add(requestedPath);\n requestedPaths.push(requestedPath);\n }\n if (!this.pendingRequestedCanvasPaths) {\n return requestedPaths;\n }\n\n const mergedPaths = [...requestedPaths];\n const mergedSeen = new Set(mergedPaths);\n for (const requestedPath of this.pendingRequestedCanvasPaths) {\n if (mergedSeen.has(requestedPath)) continue;\n mergedSeen.add(requestedPath);\n mergedPaths.push(requestedPath);\n }\n return mergedPaths;\n }\n\n private async writeRequestedCanvasPaths(\n requestedPaths: string[],\n ): Promise<void> {\n await mkdir(path.dirname(this.requestedCanvasesPath), { recursive: true });\n await atomicWriteText(\n this.requestedCanvasesPath,\n JSON.stringify({ version: 1, canvases: requestedPaths }, null, 2),\n );\n }\n\n private async pruneDeletedRequestedPaths(\n requestedPaths: string[],\n ): Promise<{ requestedPaths: string[]; changed: boolean }> {\n const existingPaths: string[] = [];\n for (const requestedPath of requestedPaths) {\n const absolutePath = this.requestedPathToAbsolutePath(requestedPath);\n if (!absolutePath) continue;\n if (await this.canvasFileExistsWithRecheck(absolutePath)) {\n existingPaths.push(requestedPath);\n }\n }\n\n return {\n requestedPaths: existingPaths,\n changed: existingPaths.length !== requestedPaths.length,\n };\n }\n\n // A prune drop is PERMANENT — nothing re-adds the entry until the next\n // explicit canvas open — so a single ENOENT must not be trusted. Editors and\n // AI tools rewrite canvas files via write-temp + rename, and git operations\n // (checkout, stash) briefly remove them; a prune racing that window used to\n // silently unregister the canvas, leaving the generated tempo-host.tsx\n // serving without it and every storyboard stuck \"waiting for iframe content\"\n // until the 180s watchdog (TEM-919). Re-check once after a short delay and\n // only drop when the file is still gone.\n private async canvasFileExistsWithRecheck(\n absolutePath: string,\n ): Promise<boolean> {\n for (let attempt = 0; ; attempt += 1) {\n try {\n await readFile(absolutePath, \"utf-8\");\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw error;\n }\n if (attempt >= 1) return false;\n await new Promise((resolve) =>\n setTimeout(resolve, this.pruneRecheckDelayMs),\n );\n }\n }\n }\n\n /**\n * The requested-canvas set as persisted (merged with any pending in-memory\n * write), WITHOUT the prune side effect. Diagnostics-only read: surfaced in\n * the bundle's `projectState` so \"the active canvas isn't registered in the\n * host\" is directly visible instead of inferred from reconcile diffs\n * (TEM-919).\n */\n async getRequestedCanvasPaths(): Promise<string[]> {\n return this.runRequestedCanvasesExclusive(() =>\n this.readRequestedCanvasPaths(),\n );\n }\n\n async ensureReactCanvasHostEntry(\n canvasFile: string,\n ): Promise<{ entry: ReactCanvasHostEntry; changed: boolean } | null> {\n const absolutePath = path.resolve(canvasFile);\n let source: string;\n try {\n source = await readFile(absolutePath, \"utf-8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return null;\n }\n throw error;\n }\n\n const parsed = parsePageSource(absolutePath, source);\n if (parsed.canvasFormat !== \"react\") {\n return null;\n }\n\n return this.runRequestedCanvasesExclusive(async () => {\n const requestedPath = this.toRequestedPath(absolutePath);\n this.requestedThisSession.add(requestedPath);\n const pruned = await this.pruneDeletedRequestedPaths(\n await this.readRequestedCanvasPaths(),\n );\n const existingIndex = pruned.requestedPaths.indexOf(requestedPath);\n const requestedPaths =\n existingIndex === -1\n ? [...pruned.requestedPaths, requestedPath]\n : pruned.requestedPaths;\n const changed = pruned.changed || existingIndex === -1;\n if (changed) {\n this.pendingRequestedCanvasPaths = requestedPaths;\n try {\n await this.writeRequestedCanvasPaths(requestedPaths);\n this.pendingRequestedCanvasPaths = null;\n } catch {\n // The host rewrite below is the authoritative operation. If this\n // session file is briefly unwritable, keep the requested set in memory\n // so the devserver's recovery reconcile can persist it and regenerate\n // the host after the transient lock clears.\n }\n }\n\n const entry = this.buildEntry(absolutePath);\n return { entry, changed };\n });\n }\n\n async collectReactCanvasHostEntries(): Promise<ReactCanvasHostEntry[]> {\n return this.runRequestedCanvasesExclusive(async () => {\n const pruned = await this.pruneDeletedRequestedPaths(\n await this.readRequestedCanvasPaths(),\n );\n if (pruned.changed || this.pendingRequestedCanvasPaths) {\n this.pendingRequestedCanvasPaths = pruned.requestedPaths;\n await this.writeRequestedCanvasPaths(pruned.requestedPaths);\n this.pendingRequestedCanvasPaths = null;\n }\n\n return pruned.requestedPaths.flatMap((requestedPath) => {\n const absolutePath = this.requestedPathToAbsolutePath(requestedPath);\n return absolutePath ? [this.buildEntry(absolutePath)] : [];\n });\n });\n }\n}\n","/**\n * Typed error surface for TempoDevServer.\n *\n * Every public method either succeeds, or rejects with a TempoDevServerError\n * carrying one of the documented ErrorCodes. The renderer (and any other\n * caller) can branch on `error.code` rather than parsing strings.\n *\n * Section 11.5 of specs/tempo-devserver-spec.md is the source of truth for\n * the enumeration.\n */\n\nexport type ErrorCode =\n | \"config_missing\"\n | \"config_conflict\"\n | \"config_invalid\"\n | \"package_json_invalid\"\n | \"install_failed\"\n | \"install_network_error\"\n | \"install_timeout\"\n | \"build_failed\"\n | \"start_failed\"\n | \"daemonized_start_requires_stop\"\n | \"start_timeout\"\n | \"probe_blocked\"\n | \"wrong_responder\"\n | \"not_reachable\"\n | \"process_crashed\"\n | \"io_failed\"\n | \"disposed\"\n | \"validation_error\"\n | \"root_deps_missing\"\n | \"tool_missing\"\n | \"node_version_unsupported\"\n | \"install_permission_denied\"\n | \"install_file_locked\"\n | \"install_dependency_conflict\"\n | \"install_interrupted\"\n | \"install_shell_dialect_mismatch\"\n | \"start_shell_dialect_mismatch\"\n | \"canvas_host_sdk_incompatible\";\n\nexport class TempoDevServerError extends Error {\n readonly code: ErrorCode;\n readonly recoveryHint?: string;\n\n constructor(code: ErrorCode, message: string, recoveryHint?: string) {\n super(message);\n this.code = code;\n this.name = \"TempoDevServerError\";\n if (recoveryHint) this.recoveryHint = recoveryHint;\n }\n}\n\nexport class ConfigMissingError extends TempoDevServerError {\n constructor(projectRoot: string) {\n super(\"config_missing\", `Missing tempo.config.json in ${projectRoot}`);\n this.name = \"ConfigMissingError\";\n }\n}\n\nexport class ConfigInvalidError extends TempoDevServerError {\n constructor(configPath: string, reason: string) {\n super(\n \"config_invalid\",\n `Invalid tempo.config.json at ${configPath}: ${reason}`,\n );\n this.name = \"ConfigInvalidError\";\n }\n}\n\n/**\n * A workspace has tempo.config.json files at BOTH the legacy project-root\n * path AND the canonical `<projectRoot>/tempo/` path. The legacy root file\n * is normally moved into `tempo/` by the migration step in\n * `TempoProject.ensureInitialized` — if both still exist, the migration\n * was either interrupted or someone re-created the legacy file by hand.\n * The right fix is to delete whichever file is no longer the source of\n * truth (typically the legacy root one); the AI's diagnostic context\n * shows both paths so it can clean up automatically.\n */\nexport class ConfigConflictError extends TempoDevServerError {\n readonly legacyRootConfigPath: string;\n readonly canonicalConfigPath: string;\n\n constructor(legacyRootConfigPath: string, canonicalConfigPath: string) {\n super(\n \"config_conflict\",\n `Found tempo.config.json files at BOTH ${legacyRootConfigPath} (legacy root location) AND ${canonicalConfigPath} (canonical location). Delete whichever one is stale — typically the legacy root one.`,\n );\n this.legacyRootConfigPath = legacyRootConfigPath;\n this.canonicalConfigPath = canonicalConfigPath;\n this.name = \"ConfigConflictError\";\n }\n}\n\nexport class PackageJsonInvalidError extends TempoDevServerError {\n constructor(filePath: string, reason: string) {\n super(\n \"package_json_invalid\",\n `Invalid package.json at ${filePath}: ${reason}`,\n );\n this.name = \"PackageJsonInvalidError\";\n }\n}\n\nexport class InstallFailedError extends TempoDevServerError {\n constructor(command: string, exitCode: number | null, output: string) {\n const detail = output.trim().length > 0 ? `\\n${output}` : \"\";\n super(\n \"install_failed\",\n `Install command failed (${exitCode ?? \"unknown\"}): ${command}${detail}`,\n );\n this.name = \"InstallFailedError\";\n }\n}\n\nexport class InstallNetworkError extends TempoDevServerError {\n readonly recoveryHint: string;\n\n constructor(\n command: string,\n output: string,\n problem: { code: string; hint: string },\n ) {\n super(\n \"install_network_error\",\n `${problem.hint}\\n\\nPackage download failed (${problem.code}): ${command}\\n${output.trim()}`,\n );\n this.name = \"InstallNetworkError\";\n this.recoveryHint = problem.hint;\n }\n}\n\nexport class InstallTimeoutError extends TempoDevServerError {\n readonly recoveryHint =\n \"No install output for 5 minutes. Check the install logs and your connection, then retry.\";\n\n constructor(command: string, output: string) {\n super(\n \"install_timeout\",\n `Dependency installation produced no output for 5 minutes and was stopped. Check the install logs, your connection, or a waiting install script before retrying.\\n\\nCommand: ${command}\\n${output.trim()}`,\n );\n this.name = \"InstallTimeoutError\";\n }\n}\n\nexport class ToolMissingError extends TempoDevServerError {\n constructor(toolName: string, cwd: string) {\n super(\n \"tool_missing\",\n `Required package manager \"${toolName}\" was not found while starting the Tempo canvas devserver from ${cwd}. Install ${toolName} or enable it with Corepack, then retry.`,\n );\n this.name = \"ToolMissingError\";\n }\n}\n\nexport class NodeVersionUnsupportedError extends TempoDevServerError {\n constructor(currentVersion: string, requirement: string) {\n super(\n \"node_version_unsupported\",\n `The Tempo canvas devserver resolved Node.js ${currentVersion}, but this project's devserver requires ${requirement}. Install a compatible Node version or update PATH so the devserver uses it.`,\n );\n this.name = \"NodeVersionUnsupportedError\";\n }\n}\n\nexport class InstallPermissionDeniedError extends TempoDevServerError {\n constructor(command: string, output: string) {\n super(\n \"install_permission_denied\",\n `Install command hit a filesystem permission/locked-file error: ${command}\\n${output.trim()}`,\n );\n this.name = \"InstallPermissionDeniedError\";\n }\n}\n\n/**\n * A package-manager install aborted because the OS refused to replace a file\n * another process still had open — classically Windows `EBUSY … copyfile` on a\n * native addon under `tempo/node_modules` that a running canvas dev server has\n * mapped.\n *\n * Deliberately NOT folded into {@link InstallPermissionDeniedError}: the two\n * describe different user situations and different remedies. A permission\n * error means the tree is unwritable and the user has to fix access; a locked\n * file means access is fine and something just needs to let go of the handle.\n * Reusing the permission code sent Windows users off auditing folder\n * permissions for a problem that was never about permissions. See TEM-1349.\n */\nexport class InstallFileLockedError extends TempoDevServerError {\n constructor(command: string, output: string) {\n super(\n \"install_file_locked\",\n `Install command was blocked by a file another program had open: ${command}\\n${output.trim()}`,\n );\n this.name = \"InstallFileLockedError\";\n }\n}\n\nexport class InstallDependencyConflictError extends TempoDevServerError {\n constructor(command: string, output: string) {\n super(\n \"install_dependency_conflict\",\n `Install command hit a dependency resolution conflict: ${command}\\n${output.trim()}`,\n );\n this.name = \"InstallDependencyConflictError\";\n }\n}\n\n/**\n * A package-manager install died because the `node_modules` tree changed\n * under it: npm's `TAR_ENTRY_ERROR` / `ENOTEMPTY` (rename over a directory\n * something else is still filling) signature. The classic cause is TWO\n * installs running in the same folder at once — Tempo's own canvas\n * dependency preparation racing an `npm install` the user or an agent\n * started in `tempo/` (diagnostic t175n51yhjbemx3dxvf2a7n4j18dq9dy).\n *\n * Kept distinct from {@link InstallFailedError}: nothing is wrong with the\n * manifest, the lockfile, or the network, so the generic \"Failed to install\n * dependencies\" headline (plus 4k of raw tar errors) sent the user hunting\n * for a package problem that never existed. The remedy is to let the other\n * install finish and retry — a retry against a settled tree succeeds.\n */\n/**\n * What the install output proves about WHY the tree was inconsistent:\n *\n * - `concurrent-install`: entries vanished while npm was still extracting\n * them (`TAR_ENTRY_ERROR`) — something else was rewriting the tree at the\n * same time. Waiting for it and retrying is the whole remedy.\n * - `inconsistent-tree`: only the rename collision (`ENOTEMPTY`) is present.\n * That also happens with NO other install running: npm moves a package\n * aside to a deterministic `.<name>-<hash>` path before replacing it, and\n * an earlier run cut short mid-reify leaves that path populated, so every\n * later install trips on it. Retrying alone does not help; the leftover has\n * to be deleted.\n */\nexport type InstallInterruptedEvidence =\n | \"concurrent-install\"\n | \"inconsistent-tree\";\n\nexport class InstallInterruptedError extends TempoDevServerError {\n readonly evidence: InstallInterruptedEvidence;\n\n constructor(\n command: string,\n output: string,\n evidence: InstallInterruptedEvidence,\n ) {\n const explanation =\n evidence === \"concurrent-install\"\n ? \"Install command was interrupted — node_modules changed while it ran, because another install was running in the same folder at the same time\"\n : \"Install command could not move a package aside because node_modules is in an inconsistent state (npm ENOTEMPTY) — left behind when an earlier install was cut short, or by another install running in the same folder\";\n const remedy =\n evidence === \"concurrent-install\"\n ? \"Wait for any other install in this folder to finish, then retry the canvas.\"\n : \"If no other install is running, delete that folder's node_modules and retry the canvas.\";\n super(\n \"install_interrupted\",\n `${explanation}: ${command}\\n${output.trim()}\\n\\n${remedy}`,\n );\n this.name = \"InstallInterruptedError\";\n this.evidence = evidence;\n }\n}\n\n/**\n * The rewrite guidance shared by the shell-dialect errors for the two\n * `tempo.config.json` scripts the supervisor spawns, so the install and\n * canvas-host surfaces describe the identical remedy. `field` names the\n * config field the user must edit.\n */\nfunction shellDialectRewriteHint(field: string): string {\n return `Hint: Tempo runs tempo.config.json's \"${field}\" through the platform shell, which is cmd.exe on Windows — it cannot parse PowerShell. Rewrite the command as a plain, cross-platform invocation with no shell conditionals (e.g. \"npm install\", or \"npm install --prefix .. && npm install\" — cmd supports \\`&&\\`). Avoid PowerShell-only syntax: \\`if (...) { ... } else { ... }\\`, \\`Test-Path\\`, \\`$env:FOO\\`. If you need cmd's own conditional, it is \\`if exist package.json (...) else (...)\\`.`;\n}\n\n/**\n * The install command never ran: it is not valid for the shell Tempo spawns\n * commands with.\n *\n * Tempo spawns `scripts.install` through the platform shell, which on Windows\n * is cmd.exe (Node's `shell: true` resolves to `%ComSpec%`). A command authored\n * in PowerShell — `if (Test-Path …) { … } else { … }`, `Test-Path`, `&&` under\n * Windows PowerShell 5.1 — is not valid cmd grammar, so cmd gives up with\n * \"<token> was unexpected at this time.\" before executing a single step.\n * Raised on TWO paths: the pre-spawn guard (`findPowerShellSyntax` matched the\n * configured command, so Tempo refused to spawn it at all — `detail` says so),\n * and the runtime classifier (the shell's own parse-abort output, carried in\n * `detail`) as the safety net for shapes the static guard can't see.\n *\n * Kept distinct from {@link InstallFailedError} because the remedy is the\n * opposite: nothing is wrong with the dependency tree or the network, and the\n * generic \"Failed to install dependencies\" headline sent Windows users (and the\n * AI agent reading `start_canvas_devserver`'s error) chasing an npm failure that\n * never happened. See TEM-436.\n */\nexport class InstallShellDialectMismatchError extends TempoDevServerError {\n constructor(command: string, detail: string) {\n super(\n \"install_shell_dialect_mismatch\",\n `The install command isn't valid for the shell Tempo runs commands with — nothing was installed, because nothing ran: ${command}\\n${detail.trim()}\\n\\n${shellDialectRewriteHint(\"scripts.install\")}`,\n );\n this.name = \"InstallShellDialectMismatchError\";\n }\n}\n\n/**\n * The canvas-host start command (`scripts.start`) contains PowerShell-only\n * syntax, so Tempo refused to spawn it: cmd.exe would abort while parsing it\n * (\"X was unexpected at this time.\") and no shell Tempo spawns with can run\n * it. Raised by the pre-spawn guard in `startDevServer` — the runtime\n * equivalent for output that only becomes recognizable after a spawn stays on\n * the `ProcessCrashedError`/`StartFailedError` path with the streaming hint\n * appended. See TEM-436.\n */\nexport class StartShellDialectMismatchError extends TempoDevServerError {\n constructor(command: string, detail: string) {\n super(\n \"start_shell_dialect_mismatch\",\n `The canvas devserver start command isn't valid for the shell Tempo runs commands with — the canvas host was not started: ${command}\\n${detail.trim()}\\n\\n${shellDialectRewriteHint(\"scripts.start\")}`,\n );\n this.name = \"StartShellDialectMismatchError\";\n }\n}\n\nexport class StartFailedError extends TempoDevServerError {\n constructor(message: string) {\n super(\"start_failed\", message);\n this.name = \"StartFailedError\";\n }\n}\n\nexport class DaemonizedStartRequiresStopError extends TempoDevServerError {\n constructor() {\n super(\n \"daemonized_start_requires_stop\",\n 'The canvas devserver start command exited successfully before the host became ready, which means it launched the host in the background. Add a matching non-empty \"scripts.stop\" command to tempo.config.json so Tempo can stop and restart that external host safely.',\n );\n this.name = \"DaemonizedStartRequiresStopError\";\n }\n}\n\nexport class StartTimeoutError extends TempoDevServerError {\n constructor(message: string) {\n super(\"start_timeout\", message);\n this.name = \"StartTimeoutError\";\n }\n}\n\nexport class NotReachableError extends TempoDevServerError {\n constructor(message: string) {\n super(\"not_reachable\", message);\n this.name = \"NotReachableError\";\n }\n}\n\n/**\n * The dev server came up healthy (printed its URL, still running) but every\n * readiness probe failed at the network level — the supervisor's loopback\n * HTTP is being blocked on this machine (Windows firewall / antivirus\n * loopback filtering — TEM-873). Distinct from `start_timeout` so the UI can\n * say \"check your firewall\" instead of a generic timeout.\n */\nexport class ProbeBlockedError extends TempoDevServerError {\n constructor(message: string) {\n super(\"probe_blocked\", message);\n this.name = \"ProbeBlockedError\";\n }\n}\n\n/**\n * The dev server came up and something answers on the host URL, but it never\n * identified as this workspace's tempo-host (TEM-1167 — e.g. a Next.js app\n * serving its own 404 for `/tempo-host` because the scaffolded host wiring is\n * missing, stale, or intercepted by the app's own middleware/proxy). A\n * deterministic failure a restart can never fix; distinct from `start_timeout`\n * so the UI can point at the host wiring instead of a generic timeout.\n */\nexport class WrongResponderError extends TempoDevServerError {\n constructor(message: string) {\n super(\"wrong_responder\", message);\n this.name = \"WrongResponderError\";\n }\n}\n\nexport class ProcessCrashedError extends TempoDevServerError {\n constructor(message: string) {\n super(\"process_crashed\", message);\n this.name = \"ProcessCrashedError\";\n }\n}\n\nexport class IoFailedError extends TempoDevServerError {\n constructor(filePath: string, reason: string) {\n super(\"io_failed\", `Failed to write ${filePath}: ${reason}`);\n this.name = \"IoFailedError\";\n }\n}\n\nexport class DisposedError extends TempoDevServerError {\n constructor() {\n super(\"disposed\", \"TempoDevServer has been disposed\");\n this.name = \"DisposedError\";\n }\n}\n\nexport class ValidationError extends TempoDevServerError {\n constructor(message: string) {\n super(\"validation_error\", message);\n this.name = \"ValidationError\";\n }\n}\n\n/** Actionable detail for a running host that failed the served identity gate. */\nexport class CanvasHostIdentityError extends TempoDevServerError {\n readonly managedUpgradeAttempted: boolean;\n readonly remediationCommand: string;\n readonly remediationDirectory: string;\n readonly upgradeError: string | null;\n readonly projectNodeHint: string | null;\n\n constructor(args: {\n managedUpgradeAttempted: boolean;\n remediationCommand: string;\n remediationDirectory: string;\n upgradeError?: string | null;\n projectNodeHint?: string | null;\n }) {\n const upgradeError = args.upgradeError ?? null;\n const upgradeNote = args.managedUpgradeAttempted\n ? upgradeError\n ? `Tempo's managed SDK update failed:\\n${upgradeErrorExcerpt(upgradeError)}\\n`\n : \"Tempo's managed SDK update completed, but the running host still serves an incompatible identity. \"\n : upgradeError\n ? `Tempo's managed SDK update did not run:\\n${upgradeErrorExcerpt(upgradeError)}\\n`\n : \"Tempo's managed SDK update did not run for this workspace. \";\n const projectNodeHint = args.projectNodeHint ?? null;\n const projectNodeNote = projectNodeHint ? `${projectNodeHint}\\n` : \"\";\n super(\n \"canvas_host_sdk_incompatible\",\n \"The Tempo host answered, but it did not prove the current served host contract. \" +\n \"The Tempo plugin may be inactive (ensure TEMPO=true is forwarded across the launch boundary) or the running tempo-sdk may be stale. \" +\n `${upgradeNote}${projectNodeNote}Run \\`${args.remediationCommand}\\` in \\`${args.remediationDirectory}\\`, restart the configured host if it is externally managed, then choose Retry.`,\n );\n this.managedUpgradeAttempted = args.managedUpgradeAttempted;\n this.remediationCommand = args.remediationCommand;\n this.remediationDirectory = args.remediationDirectory;\n this.upgradeError = upgradeError;\n this.projectNodeHint = projectNodeHint;\n this.name = \"CanvasHostIdentityError\";\n }\n}\n\nconst UPGRADE_ERROR_EXCERPT_MAX_LINES = 6;\nconst UPGRADE_ERROR_EXCERPT_MAX_CHARS = 700;\n\n/**\n * Package-manager failures arrive as multi-line dumps. The diagnosis can sit\n * on any of the first few lines (pnpm prints it AFTER the failed-command\n * header), so the excerpt keeps a bounded block rather than one line — while\n * still dropping pure boilerplate (npm's trailing debug-log pointer, stack\n * frames) so the remediation command that follows isn't buried.\n */\nfunction upgradeErrorExcerpt(text: string): string {\n const lines = text\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .filter(\n (line) => !/complete log of this run/i.test(line) && !/^at\\s/.test(line),\n )\n .slice(0, UPGRADE_ERROR_EXCERPT_MAX_LINES);\n const joined = lines.join(\"\\n\");\n if (joined.length === 0) return text.trim();\n return joined.length > UPGRADE_ERROR_EXCERPT_MAX_CHARS\n ? `${joined.slice(0, UPGRADE_ERROR_EXCERPT_MAX_CHARS)}…`\n : joined;\n}\n\n/**\n * The project root has a `package.json` declaring deps the canvas will\n * need (storyboards import from `../src/...`), but the host-side dependency\n * layout does not contain one or more of them. This never blocks a custom or\n * containerized start pre-emptively; it enriches an observed module-resolution\n * failure with the concrete host-side install remedy.\n *\n * Normally prevented by the install planner's \"project root install\" step\n * (`buildInstallSteps` plans it whenever `scripts.install` is a\n * Tempo-generated shape and root node_modules is missing); reachable when a\n * CUSTOM `scripts.install` doesn't cover the project root, or the planner's\n * root install itself failed. The message names the durable custom-script\n * fix because a custom value runs verbatim and is never rewritten by Tempo\n * (TEM-1324) — an agent editing the config can trust the edit to stick.\n */\nexport class MissingRootDepsError extends TempoDevServerError {\n readonly projectRoot: string;\n\n constructor(projectRoot: string) {\n super(\n \"root_deps_missing\",\n `The project root at ${projectRoot} has a package.json but no installed dependencies. Run the project's install command (e.g. \\`bun install\\`, \\`pnpm install\\`, \\`npm install\\`) at ${projectRoot}, then retry. For a durable fix, set scripts.install in tempo/tempo.config.json to a command that installs BOTH the project root and the tempo root (e.g. \\`cd .. && npm install && cd tempo && npm install\\`) — a custom value runs verbatim and Tempo never rewrites it.`,\n );\n this.projectRoot = projectRoot;\n this.name = \"MissingRootDepsError\";\n }\n}\n","import { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { sourceHasHomeMarker } from \"./home-marker\";\nimport type {\n LayoutSpans,\n ParseError,\n ParsedPage,\n ParsedStoryboard,\n Rect,\n StoryboardLayout,\n} from \"./types\";\nimport {\n parseCanvasFile,\n type ParsedCanvasFile,\n type ParsedCanvasStoryboard,\n} from \"../tempo-sdk/canvas/parser\";\n\ntype ParseSyncFn = (\n fileName: string,\n sourceText: string,\n) => { program: { body?: any[] } };\n\nconst currentModulePath =\n typeof __filename !== \"undefined\"\n ? __filename\n : fileURLToPath(import.meta.url);\nconst currentDirectory = path.dirname(currentModulePath);\nconst require = createRequire(currentModulePath);\n\nfunction loadParseSync(): ParseSyncFn {\n try {\n const parserModule = require(\"oxc-parser\") as {\n parseSync: ParseSyncFn;\n };\n return parserModule.parseSync;\n } catch {\n const fallbackPaths = [\n path.resolve(\n currentDirectory,\n \"../../modules/file-parser/node_modules/oxc-parser\",\n ),\n path.resolve(\n currentDirectory,\n \"../../../../node_modules/.pnpm/node_modules/oxc-parser\",\n ),\n path.resolve(\n process.cwd(),\n \"../../node_modules/.pnpm/node_modules/oxc-parser\",\n ),\n ];\n\n for (const fallbackPath of fallbackPaths) {\n try {\n const parserModule = require(fallbackPath) as {\n parseSync: ParseSyncFn;\n };\n return parserModule.parseSync;\n } catch {\n continue;\n }\n }\n\n throw new Error(\"Unable to resolve oxc-parser for canvas page parsing\");\n }\n}\n\nconst parseSync = loadParseSync();\n\nconst DEFAULT_LAYOUT: StoryboardLayout = {\n x: 0,\n y: 0,\n width: 600,\n height: 400,\n};\n\nfunction hasReactCanvasDeclaration(parsed: ParsedCanvasFile): boolean {\n return parsed.canvasElementSpan !== undefined;\n}\n\nfunction toLegacyLayoutSpans(\n spans: ParsedCanvasStoryboard[\"layoutSpans\"],\n): LayoutSpans | undefined {\n if (\n !spans?.x ||\n !spans.y ||\n !spans.width ||\n !spans.height ||\n typeof spans.layoutClose !== \"number\"\n ) {\n return undefined;\n }\n return {\n x: spans.x,\n y: spans.y,\n width: spans.width,\n height: spans.height,\n ...(spans.zIndex ? { zIndex: spans.zIndex } : {}),\n ...(spans.intrinsicSizing\n ? { intrinsicSizing: spans.intrinsicSizing }\n : {}),\n layoutClose: spans.layoutClose,\n };\n}\n\nfunction adaptReactCanvasPage(\n filePath: string,\n source: string,\n): ParsedPage | null {\n const parsed = parseCanvasFile(filePath, source);\n if (!hasReactCanvasDeclaration(parsed)) {\n return null;\n }\n\n return {\n filePath,\n canvasFormat: \"react\",\n ...(parsed.canvasPath ? { canvasPath: parsed.canvasPath } : {}),\n pageName: parsed.pageName,\n // The home marker is a comment (see ./home-marker.ts) — detect it from the\n // raw source in both formats, or React-format home canvases would lose\n // Run-button detection.\n home: sourceHasHomeMarker(source),\n ...(parsed.backgroundColor !== undefined\n ? { backgroundColor: parsed.backgroundColor }\n : {}),\n storyboards: parsed.storyboards.map((storyboard, index) => {\n const exportName =\n storyboard.storyboardKey || `__invalid_react_storyboard_${index}`;\n return {\n exportName,\n canvasFormat: \"react\",\n storyboardKey: storyboard.storyboardKey,\n ...(storyboard.uiFramework\n ? { uiFramework: storyboard.uiFramework }\n : {}),\n displayName: storyboard.displayName,\n layout: storyboard.layout,\n isRoute: storyboard.kind === \"route\",\n ...(storyboard.kind === \"html\" ? { isHtml: true } : {}),\n ...(storyboard.route ? { route: storyboard.route } : {}),\n ...(storyboard.filepath ? { filepath: storyboard.filepath } : {}),\n ...(storyboard.src ? { src: storyboard.src } : {}),\n ...(storyboard.vrt ? { vrt: true, vrtConfig: storyboard.vrt } : {}),\n layoutSpans: toLegacyLayoutSpans(storyboard.layoutSpans),\n ...(storyboard.errors.length > 0\n ? {\n error: storyboard.errors.map((error) => error.message).join(\"; \"),\n }\n : {}),\n exportSpan: storyboard.jsxElementSpan,\n // Surface the component reference for BOTH imported and same-file\n // components (`component.import` is undefined for same-file, which is a\n // valid v2 shape) so downstream consumers can tell \"no component\" from\n // \"same-file component\". `componentImport` stays import-only.\n ...(storyboard.component?.expression\n ? { componentExpression: storyboard.component.expression }\n : {}),\n ...(storyboard.component?.import\n ? {\n componentImport: {\n localName: storyboard.component.import.localName,\n specifier: storyboard.component.import.source,\n span: storyboard.component.import.declarationSpan,\n },\n }\n : {}),\n };\n }),\n errors: parsed.errors.map((error) => ({\n message: error.message,\n kind: error.kind,\n })),\n };\n}\n\ntype VariableBinding = {\n init: any;\n typeAnnotation: any;\n};\n\nfunction isObjectExpression(node: any): boolean {\n return (\n node && node.type === \"ObjectExpression\" && Array.isArray(node.properties)\n );\n}\n\nfunction toIdentifierName(node: any): string | null {\n if (!node) {\n return null;\n }\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (node.type === \"Literal\" && typeof node.value === \"string\") {\n return node.value;\n }\n return null;\n}\n\nfunction getObjectProperty(node: any, propertyName: string): any | null {\n if (!isObjectExpression(node)) {\n return null;\n }\n for (const property of node.properties) {\n if (!property || property.type !== \"Property\") {\n continue;\n }\n const keyName = toIdentifierName(property.key);\n if (keyName === propertyName) {\n return property;\n }\n }\n return null;\n}\n\nfunction readStringLiteral(\n valueNode: any,\n fieldName: string,\n exportName: string,\n): string {\n if (valueNode?.type === \"Literal\" && typeof valueNode.value === \"string\") {\n return valueNode.value;\n }\n throw new Error(\n `Non-literal \\`${fieldName}\\` property in \"${exportName}\" — ${fieldName} must be a string literal`,\n );\n}\n\nfunction readNumberLiteral(\n valueNode: any,\n fieldName: string,\n exportName: string,\n): { value: number; start: number; end: number } {\n if (\n valueNode?.type === \"Literal\" &&\n typeof valueNode.value === \"number\" &&\n Number.isFinite(valueNode.value)\n ) {\n return {\n value: valueNode.value,\n start: valueNode.start,\n end: valueNode.end,\n };\n }\n if (\n valueNode?.type === \"UnaryExpression\" &&\n valueNode.operator === \"-\" &&\n valueNode.argument?.type === \"Literal\" &&\n typeof valueNode.argument.value === \"number\" &&\n Number.isFinite(valueNode.argument.value)\n ) {\n return {\n value: -valueNode.argument.value,\n start: valueNode.start,\n end: valueNode.end,\n };\n }\n throw new Error(\n `Layout values must be number literals, found non-literal value for \\`${fieldName}\\` in \"${exportName}\"`,\n );\n}\n\nfunction readLayout(\n objectNode: any,\n exportName: string,\n): { layout: StoryboardLayout; spans: LayoutSpans } {\n const layoutProperty = getObjectProperty(objectNode, \"layout\");\n if (!layoutProperty) {\n throw new Error(`Missing required \\`layout\\` property in \"${exportName}\"`);\n }\n if (!isObjectExpression(layoutProperty.value)) {\n throw new Error(`Storyboard \"${exportName}\" has invalid \\`layout\\` shape`);\n }\n\n const xProperty = getObjectProperty(layoutProperty.value, \"x\");\n const yProperty = getObjectProperty(layoutProperty.value, \"y\");\n const widthProperty = getObjectProperty(layoutProperty.value, \"width\");\n const heightProperty = getObjectProperty(layoutProperty.value, \"height\");\n if (!xProperty || !yProperty || !widthProperty || !heightProperty) {\n throw new Error(\n `Storyboard \"${exportName}\" layout must include x, y, width, and height`,\n );\n }\n\n const x = readNumberLiteral(xProperty.value, \"x\", exportName);\n const y = readNumberLiteral(yProperty.value, \"y\", exportName);\n const width = readNumberLiteral(widthProperty.value, \"width\", exportName);\n const height = readNumberLiteral(heightProperty.value, \"height\", exportName);\n const zIndexProperty = getObjectProperty(layoutProperty.value, \"zIndex\");\n const zIndex = zIndexProperty\n ? readNumberLiteral(zIndexProperty.value, \"zIndex\", exportName)\n : null;\n const intrinsicSizingProperty = getObjectProperty(\n layoutProperty.value,\n \"intrinsicSizing\",\n );\n const intrinsicSizing = intrinsicSizingProperty\n ? readStringLiteral(\n intrinsicSizingProperty.value,\n \"layout.intrinsicSizing\",\n exportName,\n )\n : null;\n if (intrinsicSizing !== null && intrinsicSizing !== \"root-element\") {\n throw new Error(\n `Storyboard \"${exportName}\" \\`layout.intrinsicSizing\\` must be \"root-element\" when set`,\n );\n }\n\n const layoutEnd = readNumber(layoutProperty.value.end);\n if (layoutEnd === null) {\n throw new Error(\n `Storyboard \"${exportName}\" layout literal missing position info`,\n );\n }\n\n const spans: LayoutSpans = {\n x: { start: x.start, end: x.end },\n y: { start: y.start, end: y.end },\n width: { start: width.start, end: width.end },\n height: { start: height.start, end: height.end },\n ...(zIndex ? { zIndex: { start: zIndex.start, end: zIndex.end } } : {}),\n ...(intrinsicSizingProperty &&\n intrinsicSizingProperty.value &&\n typeof intrinsicSizingProperty.value.start === \"number\" &&\n typeof intrinsicSizingProperty.value.end === \"number\"\n ? {\n intrinsicSizing: {\n start: intrinsicSizingProperty.value.start,\n end: intrinsicSizingProperty.value.end,\n },\n }\n : {}),\n // Position of the `}` that closes the layout literal (exclusive end).\n layoutClose: layoutEnd - 1,\n };\n\n return {\n layout: {\n x: x.value,\n y: y.value,\n width: width.value,\n height: height.value,\n ...(zIndex ? { zIndex: zIndex.value } : {}),\n ...(intrinsicSizing !== null ? { intrinsicSizing } : {}),\n },\n spans,\n };\n}\n\nfunction readNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction extractTypeName(typeAnnotation: any): string | null {\n if (!typeAnnotation || typeAnnotation.type !== \"TSTypeReference\") {\n return null;\n }\n const typeNameNode = typeAnnotation.typeName;\n if (!typeNameNode) {\n return null;\n }\n if (typeNameNode.type === \"Identifier\") {\n return typeNameNode.name;\n }\n if (\n typeNameNode.type === \"TSQualifiedName\" &&\n typeNameNode.right?.type === \"Identifier\"\n ) {\n return typeNameNode.right.name;\n }\n return null;\n}\n\ntype ComponentImportRef = {\n localName: string;\n specifier: string;\n span: { start: number; end: number };\n};\n\n/**\n * Walk top-level ImportDeclaration nodes to build a local-binding → import map.\n * Each import statement span is recorded once and shared by all its specifiers\n * so deletion later operates on the statement as a whole.\n */\nfunction collectComponentImports(\n programBody: any[],\n): Map<string, ComponentImportRef> {\n const imports = new Map<string, ComponentImportRef>();\n\n for (const statement of programBody) {\n if (statement?.type !== \"ImportDeclaration\") {\n continue;\n }\n const specifier = statement.source?.value;\n if (typeof specifier !== \"string\") {\n continue;\n }\n const span = { start: statement.start, end: statement.end };\n for (const spec of statement.specifiers ?? []) {\n const localName = spec?.local?.name;\n if (typeof localName !== \"string\") {\n continue;\n }\n imports.set(localName, { localName, specifier, span });\n }\n }\n\n return imports;\n}\n\n/**\n * Extract the identifier of the single JSX element produced by an arrow\n * expression of the form `() => <X />`. Returns null for anything else\n * (block bodies, member-expression tags, fragments) — the caller treats a\n * null result as \"no component import association\".\n */\nfunction extractRenderIdentifier(renderValue: any): string | null {\n if (!renderValue || renderValue.type !== \"ArrowFunctionExpression\") {\n return null;\n }\n const body = renderValue.body;\n if (!body || body.type !== \"JSXElement\") {\n return null;\n }\n const name = body.openingElement?.name;\n if (!name || name.type !== \"JSXIdentifier\") {\n return null;\n }\n return typeof name.name === \"string\" ? name.name : null;\n}\n\nfunction collectVariableBindings(\n programBody: any[],\n): Map<string, VariableBinding> {\n const bindings = new Map<string, VariableBinding>();\n\n const collectFromVariableDeclaration = (node: any): void => {\n if (!node || node.type !== \"VariableDeclaration\") {\n return;\n }\n for (const declaration of node.declarations ?? []) {\n if (declaration?.id?.type !== \"Identifier\") {\n continue;\n }\n const bindingName = declaration.id.name;\n bindings.set(bindingName, {\n init: declaration.init,\n typeAnnotation: declaration.id.typeAnnotation?.typeAnnotation ?? null,\n });\n }\n };\n\n for (const statement of programBody) {\n collectFromVariableDeclaration(statement);\n if (\n statement?.type === \"ExportNamedDeclaration\" &&\n statement.declaration?.type === \"VariableDeclaration\"\n ) {\n collectFromVariableDeclaration(statement.declaration);\n }\n }\n\n return bindings;\n}\n\nfunction resolvePageNameFromObject(node: any): string | null {\n if (!isObjectExpression(node)) {\n return null;\n }\n const nameProperty = getObjectProperty(node, \"name\");\n if (!nameProperty) {\n return null;\n }\n if (\n nameProperty.value?.type === \"Literal\" &&\n typeof nameProperty.value.value === \"string\"\n ) {\n return nameProperty.value.value;\n }\n return null;\n}\n\n/**\n * Read the page-config `backgroundColor` property plus the byte spans needed to\n * edit it in place. Returns null when the property is absent. When the property\n * exists but its value is NOT a string literal (e.g. a `var`/expression), only\n * `propertySpan` is returned (no `value`/`valueSpan`) — recording its presence\n * so the writer won't insert a duplicate, while never clobbering the user's\n * expression.\n */\nfunction resolvePageBackgroundColorFromObject(node: any): {\n value?: string;\n valueSpan?: { start: number; end: number };\n propertySpan: { start: number; end: number };\n} | null {\n const property = getObjectProperty(node, \"backgroundColor\");\n if (\n !property ||\n typeof property.start !== \"number\" ||\n typeof property.end !== \"number\"\n ) {\n return null;\n }\n const propertySpan = { start: property.start, end: property.end };\n if (\n property.value?.type === \"Literal\" &&\n typeof property.value.value === \"string\" &&\n typeof property.value.start === \"number\" &&\n typeof property.value.end === \"number\"\n ) {\n return {\n value: property.value.value,\n valueSpan: { start: property.value.start, end: property.value.end },\n propertySpan,\n };\n }\n return { propertySpan };\n}\n\nfunction parseStoryboardFromBinding(options: {\n exportName: string;\n binding: VariableBinding;\n componentImports: Map<string, ComponentImportRef>;\n}): ParsedStoryboard | null {\n const typeName = extractTypeName(options.binding.typeAnnotation);\n const isComponentStoryboard = typeName === \"TempoStoryboard\";\n const isRouteStoryboard = typeName === \"TempoRouteStoryboard\";\n const isHtmlStoryboard = typeName === \"TempoHtmlStoryboard\";\n if (!isComponentStoryboard && !isRouteStoryboard && !isHtmlStoryboard) {\n return null;\n }\n\n try {\n if (!isObjectExpression(options.binding.init)) {\n throw new Error(\n `Could not parse storyboard export \"${options.exportName}\" — expected object literal`,\n );\n }\n\n const parsedLayout = readLayout(options.binding.init, options.exportName);\n\n const nameProperty = getObjectProperty(options.binding.init, \"name\");\n const displayName = nameProperty\n ? readStringLiteral(nameProperty.value, \"name\", options.exportName)\n : undefined;\n\n let route: string | undefined;\n if (isRouteStoryboard) {\n const routeProperty = getObjectProperty(options.binding.init, \"route\");\n if (!routeProperty) {\n throw new Error(\n `Route storyboard \"${options.exportName}\" is missing required \\`route\\` property`,\n );\n }\n route = readStringLiteral(\n routeProperty.value,\n \"route\",\n options.exportName,\n );\n }\n\n let componentExpression: string | undefined;\n let componentImport: ComponentImportRef | undefined;\n let filepath: string | undefined;\n if (isHtmlStoryboard) {\n const filepathProperty = getObjectProperty(\n options.binding.init,\n \"filepath\",\n );\n if (!filepathProperty) {\n throw new Error(\n `Html storyboard \"${options.exportName}\" is missing required \\`filepath\\` property`,\n );\n }\n filepath = readStringLiteral(\n filepathProperty.value,\n \"filepath\",\n options.exportName,\n );\n }\n\n if (isComponentStoryboard) {\n const renderProperty = getObjectProperty(options.binding.init, \"render\");\n if (!renderProperty) {\n throw new Error(\n `Component storyboard \"${options.exportName}\" is missing required \\`render\\` property`,\n );\n }\n const identifier = extractRenderIdentifier(renderProperty.value);\n if (identifier) {\n componentImport = options.componentImports.get(identifier);\n // A capitalized render root (`() => <Foo />`) is a component\n // reference — surface it whether or not it resolves to an import, so\n // same-file components are visible. A lowercase root (`() => <div>…`)\n // is an intrinsic element, not a component reference.\n if (componentImport || /^[A-Z]/.test(identifier)) {\n componentExpression = identifier;\n }\n }\n }\n\n return {\n exportName: options.exportName,\n displayName,\n layout: parsedLayout.layout,\n isRoute: isRouteStoryboard,\n ...(isHtmlStoryboard ? { isHtml: true } : {}),\n ...(route ? { route } : {}),\n ...(filepath ? { filepath } : {}),\n layoutSpans: parsedLayout.spans,\n ...(componentExpression ? { componentExpression } : {}),\n ...(componentImport ? { componentImport } : {}),\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n exportName: options.exportName,\n displayName: undefined,\n layout: DEFAULT_LAYOUT,\n isRoute: false,\n error: message,\n };\n }\n}\n\nfunction collectStoryboardExports(\n programBody: any[],\n bindings: Map<string, VariableBinding>,\n componentImports: Map<string, ComponentImportRef>,\n): ParsedStoryboard[] {\n const storyboards: ParsedStoryboard[] = [];\n const seenExports = new Set<string>();\n\n const maybePushStoryboard = (\n exportName: string,\n binding: VariableBinding,\n exportSpan?: { start: number; end: number },\n ): void => {\n if (seenExports.has(exportName)) {\n return;\n }\n const parsed = parseStoryboardFromBinding({\n exportName,\n binding,\n componentImports,\n });\n if (!parsed) {\n return;\n }\n if (exportSpan) {\n parsed.exportSpan = exportSpan;\n }\n seenExports.add(exportName);\n storyboards.push(parsed);\n };\n\n for (const statement of programBody) {\n if (statement?.type !== \"ExportNamedDeclaration\") {\n continue;\n }\n\n const statementSpan = { start: statement.start, end: statement.end };\n\n if (statement.declaration?.type === \"VariableDeclaration\") {\n for (const declaration of statement.declaration.declarations ?? []) {\n if (declaration?.id?.type !== \"Identifier\") {\n continue;\n }\n const binding = bindings.get(declaration.id.name);\n if (!binding) {\n continue;\n }\n maybePushStoryboard(declaration.id.name, binding, statementSpan);\n }\n continue;\n }\n\n if (!Array.isArray(statement.specifiers) || statement.source) {\n continue;\n }\n for (const specifier of statement.specifiers) {\n const localName = toIdentifierName(specifier.local);\n const exportedName = toIdentifierName(\n specifier.exported ?? specifier.local,\n );\n if (!localName || !exportedName) {\n continue;\n }\n const binding = bindings.get(localName);\n if (!binding) {\n continue;\n }\n maybePushStoryboard(exportedName, binding, statementSpan);\n }\n }\n\n return storyboards;\n}\n\nexport function parsePageSource(filePath: string, source: string): ParsedPage {\n const reactCanvasPage = adaptReactCanvasPage(filePath, source);\n if (reactCanvasPage) {\n return reactCanvasPage;\n }\n\n const fallbackPageName = path.basename(path.dirname(filePath));\n const errors: ParseError[] = [];\n const result = parseSync(filePath, source);\n const programBody = Array.isArray(result.program?.body)\n ? result.program.body\n : [];\n const bindings = collectVariableBindings(programBody);\n const componentImports = collectComponentImports(programBody);\n\n let pageName = fallbackPageName;\n let pageNameResolved = false;\n // The home marker is a comment (see ./home-marker.ts) — detect it from the\n // raw source, not the AST, so it can't type-error and the agent won't strip it.\n const home = sourceHasHomeMarker(source);\n\n // Resolve the default-export object literal once — inline (`export default\n // { ... }`) or via an identifier binding (`const config = { ... }; export\n // default config`) — so we can read every page-level field (name,\n // backgroundColor) and capture spans for in-place writes from the same node.\n let pageObjectNode: any = null;\n for (const statement of programBody) {\n if (statement?.type !== \"ExportDefaultDeclaration\") {\n continue;\n }\n const declaration = statement.declaration;\n if (isObjectExpression(declaration)) {\n pageObjectNode = declaration;\n } else if (declaration?.type === \"Identifier\") {\n const binding = bindings.get(declaration.name);\n if (binding && isObjectExpression(binding.init)) {\n pageObjectNode = binding.init;\n }\n }\n break;\n }\n\n let backgroundColor: string | undefined;\n let pageConfigSpans: ParsedPage[\"pageConfigSpans\"];\n if (pageObjectNode) {\n const extractedName = resolvePageNameFromObject(pageObjectNode);\n if (extractedName) {\n pageName = extractedName;\n pageNameResolved = true;\n }\n const bg = resolvePageBackgroundColorFromObject(pageObjectNode);\n if (bg?.value !== undefined) {\n backgroundColor = bg.value;\n }\n const pageObjectEnd = readNumber(pageObjectNode.end);\n if (pageObjectEnd !== null) {\n pageConfigSpans = {\n pageObjectClose: pageObjectEnd - 1,\n ...(bg?.valueSpan ? { backgroundColor: bg.valueSpan } : {}),\n ...(bg?.propertySpan\n ? { backgroundColorProperty: bg.propertySpan }\n : {}),\n };\n }\n }\n\n if (!pageNameResolved) {\n errors.push({\n message:\n \"Canvas default export name could not be parsed; falling back to canvas folder name\",\n kind: \"degraded\",\n });\n }\n\n const storyboards = collectStoryboardExports(\n programBody,\n bindings,\n componentImports,\n );\n for (const storyboard of storyboards) {\n if (storyboard.error) {\n errors.push({\n exportName: storyboard.exportName,\n message: storyboard.error,\n kind: \"degraded\",\n });\n }\n }\n\n return {\n filePath,\n pageName,\n home,\n ...(backgroundColor !== undefined ? { backgroundColor } : {}),\n ...(pageConfigSpans ? { pageConfigSpans } : {}),\n storyboards,\n errors,\n };\n}\n\nexport function toStoryboardRects(page: ParsedPage): Rect[] {\n return page.storyboards\n .filter((storyboard) => !storyboard.error)\n .map((storyboard) => ({\n x: storyboard.layout.x,\n y: storyboard.layout.y,\n width: storyboard.layout.width,\n height: storyboard.layout.height,\n ...(storyboard.layout.zIndex != null\n ? { zIndex: storyboard.layout.zIndex }\n : {}),\n }));\n}\n","/**\n * The \"home canvas\" marker.\n *\n * It is stored as a **comment**, not a typed field on `TempoPage`. A typed\n * `home: true` property type-errors against the `tempo-sdk` that real user\n * projects install (the published package's `TempoPage` has no `home` field),\n * and the agent then \"fixes\" the type error by deleting the marker — which\n * silently breaks home-canvas detection (diagnostic\n * 2026-06-18T03-14-45: the AI removed `home: true` to clear a TS error). A\n * comment is invisible to the type checker, so it survives across every\n * tempo-sdk version and the agent has no reason to remove it.\n */\n\n/** Distinctive token used to detect the marker in a canvas's source. */\nexport const HOME_CANVAS_MARKER = \"@tempo-home\";\n\n/** The full comment line emitted at the top of a home canvas's file. */\nexport const HOME_CANVAS_MARKER_LINE = `// ${HOME_CANVAS_MARKER} — Tempo home canvas (the workspace Run button opens this). Managed marker; do not remove.`;\n\n/** True when a canvas source carries the home marker comment. */\nexport function sourceHasHomeMarker(source: string): boolean {\n return source.includes(HOME_CANVAS_MARKER);\n}\n","import { parseSync } from \"oxc-parser\";\n\nexport type CanvasSourceSpan = { start: number; end: number };\n\nexport type CanvasParseError = {\n message: string;\n span?: CanvasSourceSpan;\n /** \"structural\": the file's storyboard identity/structure cannot be\n * trusted — readers must fail closed. \"degraded\": a single storyboard\n * (or the canvas display name) is not statically readable/editable but\n * identity is intact — readers keep working with fallback values. */\n kind: \"structural\" | \"degraded\";\n};\n\nexport type ParsedCanvasImport = {\n localName: string;\n source: string;\n importedName: string;\n kind: \"default\" | \"named\" | \"namespace\";\n importKind?: \"type\" | \"value\";\n span: CanvasSourceSpan;\n declarationSpan: CanvasSourceSpan;\n specifierCount: number;\n namedSpecifierCount: number;\n hasDefaultSpecifier: boolean;\n hasNamespaceSpecifier: boolean;\n};\n\n/** An import statement that names Tempo SDK canvas components but uses the\n * wrong tempo-sdk specifier (e.g. `import { Canvas } from \"tempo-sdk\"`).\n * Neither the main entry nor the other subpaths export the components, so\n * the file both fails at runtime and is invisible to this parser's <Canvas>\n * detection — migrate-on-read uses these records to repair the specifier. */\nexport type MisimportedSdkCanvasImport = {\n /** Import source exactly as written, e.g. \"tempo-sdk\". */\n source: string;\n /** Span of the source string literal INCLUDING its quote characters. */\n sourceSpan: CanvasSourceSpan;\n /** Imported names of every value specifier on the statement. */\n importedNames: string[];\n /** True when every value specifier on the statement is a named import of a\n * value actually exported by \"tempo-sdk/canvas\" — rewriting the source\n * string to \"tempo-sdk/canvas\" provably fixes the statement. */\n rewritable: boolean;\n};\n\nexport type ParsedCanvasLayout = {\n x: number;\n y: number;\n width: number;\n height: number;\n zIndex?: number;\n intrinsicSizing?: \"root-element\";\n};\n\nexport type ParsedCanvasLayoutSpans = {\n x?: CanvasSourceSpan;\n y?: CanvasSourceSpan;\n width?: CanvasSourceSpan;\n height?: CanvasSourceSpan;\n zIndex?: CanvasSourceSpan;\n intrinsicSizing?: CanvasSourceSpan;\n layoutClose?: number;\n};\n\nexport type ParsedCanvasComponentReference = {\n expression: string;\n span: CanvasSourceSpan;\n attributeSpan?: CanvasSourceSpan;\n localName?: string;\n import?: ParsedCanvasImport;\n};\n\nexport type ParsedStoryboardVrtIgnoreRegion = {\n x: number;\n y: number;\n w: number;\n h: number;\n};\n\n/** Opt-in visual-regression tracking parsed from the `vrt` prop on\n * `<Storyboard>` (TEM-1775). Presence of the parsed field means the\n * storyboard is tracked; `skip: true` keeps the config but pauses gating.\n * `vrt` / `vrt={true}` parse to `{}`; `vrt={false}` parses to absent. */\nexport type ParsedStoryboardVrt = {\n /** Per-storyboard pixelmatch threshold override (0..0.5). */\n threshold?: number;\n /** Temporarily untracked without losing config. */\n skip?: boolean;\n /** Regions (storyboard-local px) excluded from comparison. */\n ignore?: ParsedStoryboardVrtIgnoreRegion[];\n};\n\nexport type ParsedCanvasExpressionReference = {\n expression: string;\n span: CanvasSourceSpan;\n attributeSpan?: CanvasSourceSpan;\n};\n\nexport type ParsedCanvasStoryboard = {\n storyboardKey: string;\n displayName?: string;\n kind: \"component\" | \"route\" | \"html\";\n /** Authored component runtime selected by the storyboard declaration. */\n uiFramework?: \"svelte\";\n layout: ParsedCanvasLayout;\n editable: boolean;\n jsxElementSpan: CanvasSourceSpan;\n openingElementSpan?: CanvasSourceSpan;\n idSpan?: CanvasSourceSpan;\n idAttributeSpan?: CanvasSourceSpan;\n nameSpan?: CanvasSourceSpan;\n nameAttributeSpan?: CanvasSourceSpan;\n layoutAttributeSpan?: CanvasSourceSpan;\n layoutSpans?: ParsedCanvasLayoutSpans;\n component?: ParsedCanvasComponentReference;\n props?: ParsedCanvasExpressionReference;\n vrt?: ParsedStoryboardVrt;\n /** Span of the whole `vrt` attribute. Present whenever the attribute\n * exists in source — including `vrt={false}` and degraded (dynamic /\n * malformed) forms whose `vrt` value stays absent — so an editor can\n * replace or remove the declaration without ever inserting a duplicate. */\n vrtAttributeSpan?: CanvasSourceSpan;\n route?: string;\n routeSpan?: CanvasSourceSpan;\n routeAttributeSpan?: CanvasSourceSpan;\n filepath?: string;\n filepathSpan?: CanvasSourceSpan;\n filepathAttributeSpan?: CanvasSourceSpan;\n src?: string;\n srcSpan?: CanvasSourceSpan;\n srcAttributeSpan?: CanvasSourceSpan;\n legacyV1?: {\n storyboard: ParsedCanvasExpressionReference;\n objectName?: string;\n objectSpan?: CanvasSourceSpan;\n objectClose?: number;\n namePropertySpan?: CanvasSourceSpan;\n layoutPropertySpan?: CanvasSourceSpan;\n };\n errors: CanvasParseError[];\n};\n\nexport type ParsedCanvasFile = {\n filePath: string;\n canvasPath?: string;\n pageName: string;\n backgroundColor?: string;\n canvasElementSpan?: CanvasSourceSpan;\n canvasOpeningElementSpan?: CanvasSourceSpan;\n canvasChildrenInsertionOffset?: number;\n nameSpan?: CanvasSourceSpan;\n nameAttributeSpan?: CanvasSourceSpan;\n pathSpan?: CanvasSourceSpan;\n pathAttributeSpan?: CanvasSourceSpan;\n backgroundColorSpan?: CanvasSourceSpan;\n backgroundColorAttributeSpan?: CanvasSourceSpan;\n imports: ParsedCanvasImport[];\n misimportedSdkCanvasImports: MisimportedSdkCanvasImport[];\n storyboards: ParsedCanvasStoryboard[];\n errors: CanvasParseError[];\n};\n\ntype SdkComponent =\n | \"Canvas\"\n | \"Storyboard\"\n | \"SvelteStoryboard\"\n | \"LegacyStoryboardV1\"\n | \"RouteStoryboard\"\n | \"HtmlStoryboard\";\n\ntype StoryboardSdkComponent = Exclude<SdkComponent, \"Canvas\">;\n\ntype StoryboardElement = {\n element: any;\n component: StoryboardSdkComponent;\n};\n\ntype ParsedImports = {\n all: ParsedCanvasImport[];\n byLocalName: Map<string, ParsedCanvasImport>;\n sdkLocalNames: Map<string, SdkComponent>;\n sdkNamespaces: Set<string>;\n};\n\ntype ParsedLegacyStoryboardObject = {\n bindingName: string;\n displayName?: string;\n component?: ParsedCanvasComponentReference;\n objectSpan?: CanvasSourceSpan;\n objectClose?: number;\n nameSpan?: CanvasSourceSpan;\n namePropertySpan?: CanvasSourceSpan;\n layoutPropertySpan?: CanvasSourceSpan;\n layout: ParsedCanvasLayout;\n layoutSpans?: ParsedCanvasLayoutSpans;\n errors: CanvasParseError[];\n};\n\ntype ObjectStringBindings = Map<\n string,\n Map<string, { value: string; span: CanvasSourceSpan }>\n>;\n\nconst SDK_IMPORT_SOURCES = new Set([\n \"tempo-sdk/canvas\",\n \"tempo-sdk/canvas/svelte\",\n]);\n\nconst SDK_COMPONENTS = new Set<SdkComponent>([\n \"Canvas\",\n \"Storyboard\",\n \"SvelteStoryboard\",\n \"LegacyStoryboardV1\",\n \"RouteStoryboard\",\n \"HtmlStoryboard\",\n]);\n\n// Must stay in sync with runtime value exports from tempo-sdk/canvas/index.ts.\nconst SDK_CANVAS_VALUE_EXPORTS = new Set([\n \"Canvas\",\n \"CanvasContext\",\n \"getCanvasStoryboardRenderId\",\n \"CanvasHost\",\n \"buildRenderContentFiberSelector\",\n \"buildRenderContentSelector\",\n \"buildRenderSelector\",\n \"captureStoryboardCombined\",\n \"captureStoryboardHtml\",\n \"captureStoryboardImage\",\n \"stripRenderContentWrapperAttributes\",\n \"stripRenderContentWrapperFromCombinedCapture\",\n \"watchStoryboardCombined\",\n \"getCanvasHostRuntime\",\n \"getOptionalCanvasHostRuntime\",\n \"useActiveState\",\n \"HtmlStoryboard\",\n \"LegacyStoryboardV1\",\n \"RouteStoryboard\",\n \"Storyboard\",\n \"StoryboardSlot\",\n]);\n\nconst DEFAULT_LAYOUT: ParsedCanvasLayout = {\n x: 0,\n y: 0,\n width: 600,\n height: 400,\n};\n\n/** Emitted when a file parses cleanly but declares no `<Canvas>` — for\n * readers that need to distinguish \"legacy page format\" from \"broken\". */\nexport const NO_CANVAS_DECLARATION_ERROR =\n \"No <Canvas> declaration found in canvas file\";\n\nexport function parseCanvasFile(\n filePath: string,\n source: string,\n): ParsedCanvasFile {\n const errors: CanvasParseError[] = [];\n let program: any;\n\n try {\n const result = parseSync(filePath, source) as any;\n program = result.program;\n for (const error of result.errors ?? []) {\n errors.push({\n message: error.message ?? \"Unable to parse canvas source\",\n span: toSpan(error) ?? toSpan(error?.labels?.[0]),\n kind: \"structural\",\n });\n }\n } catch (error) {\n return {\n filePath,\n pageName: fallbackPageName(filePath),\n imports: [],\n misimportedSdkCanvasImports: [],\n storyboards: [],\n errors: [\n {\n message:\n error instanceof Error\n ? error.message\n : \"Unable to parse canvas source\",\n kind: \"structural\",\n },\n ],\n };\n }\n\n const imports = readImports(program);\n const misimportedSdkCanvasImports = readMisimportedSdkCanvasImports(program);\n const { legacyObjects, stringBindings: objectStringBindings } =\n readObjectExpressionBindings(program, imports, source);\n let canvasElement: any | null = null;\n const allStoryboardElements: StoryboardElement[] = [];\n\n walk(program, (node) => {\n if (node.type !== \"JSXElement\") return;\n\n const component = resolveSdkComponent(node.openingElement?.name, imports);\n const namespaceMisimport = resolveMisimportedNamespaceSdkComponent(\n node.openingElement?.name,\n imports,\n misimportedSdkCanvasImports,\n );\n if (namespaceMisimport) {\n misimportedSdkCanvasImports.add(namespaceMisimport);\n }\n\n if (component === \"Canvas\") {\n if (!canvasElement) {\n canvasElement = node;\n } else {\n errors.push({\n message: \"Canvas file must declare exactly one <Canvas> element\",\n span: toSpan(node),\n kind: \"structural\",\n });\n }\n return;\n }\n\n if (isStoryboardSdkComponent(component)) {\n allStoryboardElements.push({ element: node, component });\n }\n });\n\n const directStoryboardElements = canvasElement\n ? readDirectCanvasStoryboardElements(canvasElement, imports)\n : [];\n const directStoryboardElementSet = new Set(\n directStoryboardElements.map((item) => item.element),\n );\n\n const storyboards = directStoryboardElements.map((item) =>\n readStoryboard(\n item.element,\n item.component,\n imports,\n legacyObjects,\n source,\n errors,\n ),\n );\n\n // Storyboard identity and layout only map faithfully when declarations are\n // direct Canvas children; anything else has no stable page-stack position.\n for (const item of allStoryboardElements) {\n if (directStoryboardElementSet.has(item.element)) continue;\n errors.push({\n message: `<${item.component}> declarations must be direct children of the <Canvas> element`,\n span: toSpan(item.element),\n kind: \"structural\",\n });\n }\n\n reportDuplicateStoryboardIds(storyboards, errors);\n\n if (!canvasElement) {\n if (misimportedSdkCanvasImports.all.length > 0) {\n for (const entry of misimportedSdkCanvasImports.all) {\n errors.push({\n message: misimportedSdkCanvasImportMessage(entry),\n span: entry.sourceSpan,\n kind: \"structural\",\n });\n }\n } else {\n errors.push({\n message: NO_CANVAS_DECLARATION_ERROR,\n kind: \"structural\",\n });\n }\n } else {\n errors.push(...validateCanvasChildren(canvasElement, imports));\n }\n\n const openingElement = canvasElement?.openingElement;\n const name = readResolvableStringAttribute(\n openingElement,\n \"name\",\n objectStringBindings,\n );\n const path = readStringAttribute(openingElement, \"path\");\n const backgroundColor =\n readResolvableStringAttribute(\n openingElement,\n \"backgroundColor\",\n objectStringBindings,\n ) ??\n readResolvableStringAttribute(\n openingElement,\n \"background\",\n objectStringBindings,\n );\n\n if (openingElement && !name) {\n errors.push({\n message: '<Canvas> must include a literal \"name\" prop',\n span: toSpan(openingElement),\n kind: \"degraded\",\n });\n }\n\n return {\n filePath,\n canvasPath: path?.value,\n pageName: name?.value ?? fallbackPageName(filePath),\n backgroundColor: backgroundColor?.value,\n canvasElementSpan: toSpan(canvasElement),\n canvasOpeningElementSpan: toSpan(openingElement),\n canvasChildrenInsertionOffset:\n typeof canvasElement?.closingElement?.start === \"number\"\n ? canvasElement.closingElement.start\n : undefined,\n nameSpan: name?.valueSpan,\n nameAttributeSpan: name?.attributeSpan,\n pathSpan: path?.valueSpan,\n pathAttributeSpan: path?.attributeSpan,\n backgroundColorSpan: backgroundColor?.valueSpan,\n backgroundColorAttributeSpan: backgroundColor?.attributeSpan,\n imports: imports.all,\n misimportedSdkCanvasImports: misimportedSdkCanvasImports.all,\n storyboards,\n errors,\n };\n}\n\ntype MutableMisimportedSdkCanvasImports = {\n all: MisimportedSdkCanvasImport[];\n byDeclarationKey: Map<string, MisimportedSdkCanvasImport>;\n namespaceCandidatesByLocalName: Map<string, MisimportedSdkCanvasImport>;\n add(entry: MisimportedSdkCanvasImport): void;\n};\n\nexport function misimportedSdkCanvasImportMessage(\n entry: MisimportedSdkCanvasImport,\n): string {\n return `Tempo canvas components must be imported from \"tempo-sdk/canvas\" — found import of ${entry.importedNames.join(\", \")} from \"${entry.source}\"`;\n}\n\nfunction readMisimportedSdkCanvasImports(\n program: any,\n): MutableMisimportedSdkCanvasImports {\n const all: MisimportedSdkCanvasImport[] = [];\n const byDeclarationKey = new Map<string, MisimportedSdkCanvasImport>();\n const namespaceCandidatesByLocalName = new Map<\n string,\n MisimportedSdkCanvasImport\n >();\n const add = (entry: MisimportedSdkCanvasImport) => {\n const key = spanKey(entry.sourceSpan);\n if (byDeclarationKey.has(key)) return;\n byDeclarationKey.set(key, entry);\n all.push(entry);\n };\n\n for (const statement of program?.body ?? []) {\n if (\n statement?.type !== \"ImportDeclaration\" ||\n typeof statement.source?.value !== \"string\" ||\n !isWrongTempoSdkCanvasImportSource(statement.source.value) ||\n statement.importKind === \"type\"\n ) {\n continue;\n }\n\n const source = statement.source.value;\n const sourceSpan = toSpan(statement.source);\n if (!sourceSpan) continue;\n\n const valueSpecifiers = (statement.specifiers ?? []).filter(\n (specifier: any) => specifier?.importKind !== \"type\",\n );\n const importedNames = valueSpecifiers.map(readImportedName);\n const entry: MisimportedSdkCanvasImport = {\n source,\n sourceSpan,\n importedNames,\n rewritable:\n valueSpecifiers.length > 0 &&\n valueSpecifiers.every(\n (specifier: any) =>\n specifier?.type === \"ImportSpecifier\" &&\n SDK_CANVAS_VALUE_EXPORTS.has(readImportedName(specifier)),\n ),\n };\n\n const hasMisimportedNamedSdkComponent = valueSpecifiers.some(\n (specifier: any) =>\n specifier?.type === \"ImportSpecifier\" &&\n SDK_COMPONENTS.has(readImportedName(specifier) as SdkComponent),\n );\n if (hasMisimportedNamedSdkComponent) {\n add(entry);\n }\n\n for (const specifier of valueSpecifiers) {\n if (specifier?.type !== \"ImportNamespaceSpecifier\") continue;\n const localName = specifier.local?.name;\n if (typeof localName === \"string\") {\n namespaceCandidatesByLocalName.set(localName, entry);\n }\n }\n }\n\n return { all, byDeclarationKey, namespaceCandidatesByLocalName, add };\n}\n\nfunction readImports(program: any): ParsedImports {\n const all: ParsedCanvasImport[] = [];\n const byLocalName = new Map<string, ParsedCanvasImport>();\n const sdkLocalNames = new Map<string, SdkComponent>();\n const sdkNamespaces = new Set<string>();\n\n for (const statement of program?.body ?? []) {\n if (\n statement?.type !== \"ImportDeclaration\" ||\n typeof statement.source?.value !== \"string\"\n ) {\n continue;\n }\n\n const source = statement.source.value;\n const isSdkImport = isSdkImportSource(source);\n\n for (const specifier of statement.specifiers ?? []) {\n const localName = specifier.local?.name;\n if (typeof localName !== \"string\") continue;\n\n const importedName = readImportedName(specifier);\n const imported: ParsedCanvasImport = {\n localName,\n source,\n importedName,\n kind: readImportKind(specifier),\n importKind: specifier.importKind === \"type\" ? \"type\" : \"value\",\n span: toSpan(specifier) ?? toSpan(statement) ?? { start: 0, end: 0 },\n declarationSpan: toSpan(statement) ?? { start: 0, end: 0 },\n specifierCount: statement.specifiers?.length ?? 0,\n namedSpecifierCount: (statement.specifiers ?? []).filter(\n (item: any) => item?.type === \"ImportSpecifier\",\n ).length,\n hasDefaultSpecifier: (statement.specifiers ?? []).some(\n (item: any) => item?.type === \"ImportDefaultSpecifier\",\n ),\n hasNamespaceSpecifier: (statement.specifiers ?? []).some(\n (item: any) => item?.type === \"ImportNamespaceSpecifier\",\n ),\n };\n\n all.push(imported);\n byLocalName.set(localName, imported);\n\n if (!isSdkImport) continue;\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n sdkNamespaces.add(localName);\n continue;\n }\n\n if (SDK_COMPONENTS.has(importedName as SdkComponent)) {\n sdkLocalNames.set(localName, importedName as SdkComponent);\n }\n }\n }\n\n return { all, byLocalName, sdkLocalNames, sdkNamespaces };\n}\n\nfunction readObjectExpressionBindings(\n program: any,\n imports: ParsedImports,\n source: string,\n): {\n legacyObjects: Map<string, ParsedLegacyStoryboardObject>;\n stringBindings: ObjectStringBindings;\n} {\n const legacyObjects = new Map<string, ParsedLegacyStoryboardObject>();\n const stringBindings: ObjectStringBindings = new Map();\n\n // Canvas metadata and legacy storyboard objects are module-level authoring\n // contracts; function-local shadowing must not change parsed identities.\n for (const node of readTopLevelVariableDeclarators(program)) {\n if (node.id?.type !== \"Identifier\") {\n continue;\n }\n const objectExpression = unwrapTsExpression(node.init);\n if (objectExpression?.type !== \"ObjectExpression\") continue;\n\n const bindingName = node.id.name;\n legacyObjects.set(\n bindingName,\n readLegacyStoryboardObject(\n bindingName,\n objectExpression,\n imports,\n source,\n ),\n );\n\n const stringProperties = readObjectStringProperties(objectExpression);\n if (stringProperties.size > 0) {\n stringBindings.set(bindingName, stringProperties);\n }\n }\n\n return { legacyObjects, stringBindings };\n}\n\nfunction readTopLevelVariableDeclarators(program: any): any[] {\n const declarators: any[] = [];\n\n for (const statement of program?.body ?? []) {\n const declaration =\n statement?.type === \"VariableDeclaration\"\n ? statement\n : statement?.type === \"ExportNamedDeclaration\" &&\n statement.declaration?.type === \"VariableDeclaration\"\n ? statement.declaration\n : null;\n if (!declaration) continue;\n declarators.push(...(declaration.declarations ?? []));\n }\n\n return declarators;\n}\n\nfunction readLegacyStoryboardObject(\n bindingName: string,\n objectExpression: any,\n imports: ParsedImports,\n source: string,\n): ParsedLegacyStoryboardObject {\n const errors: CanvasParseError[] = [];\n const nameProperty = getObjectProperty(objectExpression, \"name\");\n const name = nameProperty ? readStringLiteralValue(nameProperty.value) : null;\n const layoutProperty = getObjectProperty(objectExpression, \"layout\");\n const layout: {\n value: ParsedCanvasLayout;\n spans?: ParsedCanvasLayoutSpans;\n errors: CanvasParseError[];\n } = layoutProperty\n ? readLayoutExpression(layoutProperty.value, \"LegacyStoryboardV1\")\n : {\n value: DEFAULT_LAYOUT,\n errors: [\n {\n message:\n '<LegacyStoryboardV1> storyboard object must include a literal \"layout\" object',\n span: toSpan(objectExpression),\n kind: \"degraded\",\n },\n ],\n };\n const renderProperty = getObjectProperty(objectExpression, \"render\");\n const component = readRenderComponentReference(\n renderProperty?.value,\n imports,\n source,\n );\n\n if (nameProperty && !name) {\n errors.push({\n message:\n \"<LegacyStoryboardV1> storyboard.name must be a string literal when set\",\n span: toSpan(nameProperty),\n kind: \"degraded\",\n });\n }\n errors.push(...layout.errors);\n\n return {\n bindingName,\n displayName: name?.value,\n component: component ?? undefined,\n objectSpan: toSpan(objectExpression),\n objectClose: readLayoutClose(objectExpression),\n nameSpan: name?.span,\n namePropertySpan: toSpan(nameProperty),\n layoutPropertySpan: toSpan(layoutProperty),\n layout: layout.value,\n layoutSpans: layout.spans,\n errors,\n };\n}\n\nfunction readObjectStringProperties(\n objectExpression: any,\n): Map<string, { value: string; span: CanvasSourceSpan }> {\n const properties = new Map<\n string,\n { value: string; span: CanvasSourceSpan }\n >();\n for (const property of objectExpression.properties ?? []) {\n if (property?.type !== \"Property\" || property.computed) continue;\n const propertyName = readPropertyKeyName(property.key);\n const value = readStringLiteralValue(property.value);\n if (!propertyName || !value) continue;\n properties.set(propertyName, value);\n }\n return properties;\n}\n\nfunction readStoryboard(\n element: any,\n component: Exclude<SdkComponent, \"Canvas\">,\n imports: ParsedImports,\n legacyObjects: Map<string, ParsedLegacyStoryboardObject>,\n source: string,\n globalErrors: CanvasParseError[],\n): ParsedCanvasStoryboard {\n const openingElement = element.openingElement;\n const errors: CanvasParseError[] = [];\n const id = readStringAttribute(openingElement, \"id\");\n const name = readStringAttribute(openingElement, \"name\");\n const legacyReference =\n component === \"LegacyStoryboardV1\"\n ? readExpressionReference(openingElement, \"storyboard\", source)\n : undefined;\n const legacyObject =\n legacyReference?.expression &&\n isIdentifierExpression(legacyReference.expression)\n ? legacyObjects.get(legacyReference.expression)\n : undefined;\n const layout =\n component === \"LegacyStoryboardV1\"\n ? {\n value: legacyObject?.layout ?? DEFAULT_LAYOUT,\n spans: legacyObject?.layoutSpans,\n attributeSpan: undefined,\n errors:\n legacyObject?.errors ??\n (legacyReference\n ? [\n {\n message:\n \"<LegacyStoryboardV1> storyboard prop must reference a same-file object literal\",\n span: legacyReference.span,\n kind: \"degraded\",\n },\n ]\n : [\n {\n message:\n '<LegacyStoryboardV1> must include a \"storyboard\" expression prop',\n span: toSpan(openingElement),\n kind: \"degraded\",\n },\n ]),\n }\n : readLayout(openingElement, component);\n const kind =\n component === \"Storyboard\" ||\n component === \"SvelteStoryboard\" ||\n component === \"LegacyStoryboardV1\"\n ? \"component\"\n : component === \"RouteStoryboard\"\n ? \"route\"\n : \"html\";\n\n if (!id) {\n errors.push({\n message: `<${component}> must include a literal \"id\" prop`,\n span: toSpan(openingElement),\n kind: \"structural\",\n });\n }\n\n if (layout.errors.length > 0) {\n errors.push(...layout.errors);\n }\n\n const storyboard: ParsedCanvasStoryboard = {\n storyboardKey: id?.value ?? \"\",\n displayName: name?.value ?? legacyObject?.displayName,\n kind,\n ...(component === \"SvelteStoryboard\"\n ? { uiFramework: \"svelte\" as const }\n : {}),\n layout: layout.value,\n editable: Boolean(id) && layout.errors.length === 0,\n jsxElementSpan: toSpan(element) ?? { start: 0, end: 0 },\n openingElementSpan: toSpan(openingElement),\n idSpan: id?.valueSpan,\n idAttributeSpan: id?.attributeSpan,\n nameSpan: name?.valueSpan ?? legacyObject?.nameSpan,\n nameAttributeSpan: name?.attributeSpan ?? legacyObject?.namePropertySpan,\n layoutAttributeSpan: layout.attributeSpan,\n layoutSpans: layout.spans,\n props: readExpressionReference(openingElement, \"props\", source),\n component: legacyObject?.component,\n legacyV1: legacyReference\n ? {\n storyboard: legacyReference,\n objectName: legacyObject?.bindingName,\n objectSpan: legacyObject?.objectSpan,\n objectClose: legacyObject?.objectClose,\n namePropertySpan: legacyObject?.namePropertySpan,\n layoutPropertySpan: legacyObject?.layoutPropertySpan,\n }\n : undefined,\n errors,\n };\n\n if (component === \"Storyboard\" || component === \"SvelteStoryboard\") {\n const componentReference = readComponentReference(\n openingElement,\n imports,\n source,\n );\n if (componentReference) {\n storyboard.component = componentReference;\n } else {\n errors.push({\n message: `<${component}> must include a \"component\" expression prop`,\n span: toSpan(openingElement),\n kind: \"degraded\",\n });\n }\n\n const vrt = readVrt(openingElement, errors);\n if (vrt) {\n if (vrt.value) {\n storyboard.vrt = vrt.value;\n }\n storyboard.vrtAttributeSpan = vrt.attributeSpan;\n }\n }\n\n if (component === \"RouteStoryboard\") {\n const route = readStringAttribute(openingElement, \"route\");\n if (route) {\n storyboard.route = route.value;\n storyboard.routeSpan = route.valueSpan;\n storyboard.routeAttributeSpan = route.attributeSpan;\n } else {\n errors.push({\n message: '<RouteStoryboard> must include a literal \"route\" prop',\n span: toSpan(openingElement),\n kind: \"degraded\",\n });\n }\n }\n\n if (component === \"HtmlStoryboard\") {\n const filepath = readStringAttribute(openingElement, \"filepath\");\n const src = readStringAttribute(openingElement, \"src\");\n storyboard.filepath = filepath?.value;\n storyboard.filepathSpan = filepath?.valueSpan;\n storyboard.filepathAttributeSpan = filepath?.attributeSpan;\n storyboard.src = src?.value;\n storyboard.srcSpan = src?.valueSpan;\n storyboard.srcAttributeSpan = src?.attributeSpan;\n if (!filepath && !src) {\n errors.push({\n message:\n '<HtmlStoryboard> must include a literal \"filepath\" or \"src\" prop',\n span: toSpan(openingElement),\n kind: \"degraded\",\n });\n }\n }\n\n globalErrors.push(...errors);\n return storyboard;\n}\n\nfunction readDirectCanvasStoryboardElements(\n canvasElement: any,\n imports: ParsedImports,\n): StoryboardElement[] {\n const elements: StoryboardElement[] = [];\n\n for (const child of canvasElement.children ?? []) {\n if (child?.type !== \"JSXElement\") continue;\n const component = resolveSdkComponent(child.openingElement?.name, imports);\n if (isStoryboardSdkComponent(component)) {\n elements.push({ element: child, component });\n }\n }\n\n return elements;\n}\n\nfunction reportDuplicateStoryboardIds(\n storyboards: ParsedCanvasStoryboard[],\n errors: CanvasParseError[],\n) {\n const byId = new Map<string, ParsedCanvasStoryboard[]>();\n\n for (const storyboard of storyboards) {\n if (!storyboard.storyboardKey) continue;\n const matching = byId.get(storyboard.storyboardKey);\n if (matching) {\n matching.push(storyboard);\n } else {\n byId.set(storyboard.storyboardKey, [storyboard]);\n }\n }\n\n for (const [storyboardKey, matching] of byId) {\n if (matching.length < 2) continue;\n errors.push({\n message: `Duplicate storyboard id \"${storyboardKey}\" (${matching.length} declarations) — storyboard ids must be unique`,\n span: matching[1]?.jsxElementSpan,\n kind: \"structural\",\n });\n }\n}\n\nfunction validateCanvasChildren(\n canvasElement: any,\n imports: ParsedImports,\n): CanvasParseError[] {\n const errors: CanvasParseError[] = [];\n\n for (const child of canvasElement.children ?? []) {\n if (!child) continue;\n if (child.type === \"JSXText\") {\n if (typeof child.value === \"string\" && child.value.trim() === \"\") {\n continue;\n }\n }\n if (child.type === \"JSXExpressionContainer\") {\n if (child.expression?.type === \"JSXEmptyExpression\") {\n continue;\n }\n errors.push({\n message:\n \"<Canvas> children must be static storyboard declarations, not expressions\",\n span: toSpan(child),\n kind: \"structural\",\n });\n continue;\n }\n if (child.type === \"JSXElement\") {\n const component = resolveSdkComponent(\n child.openingElement?.name,\n imports,\n );\n if (\n component === \"Storyboard\" ||\n component === \"SvelteStoryboard\" ||\n component === \"LegacyStoryboardV1\" ||\n component === \"RouteStoryboard\" ||\n component === \"HtmlStoryboard\"\n ) {\n continue;\n }\n }\n errors.push({\n message:\n \"<Canvas> children must be <Storyboard>, <SvelteStoryboard>, <RouteStoryboard>, <HtmlStoryboard>, or <LegacyStoryboardV1> declarations\",\n span: toSpan(child),\n kind: \"structural\",\n });\n }\n\n return errors;\n}\n\n/**\n * Reads the opt-in `vrt` prop on a `<Storyboard>` (TEM-1775). Only statically\n * analyzable literal shapes are tracked — the mark is a build-time manifest,\n * so a dynamic expression is a degraded parse error, never a guess:\n * `vrt` / `vrt={true}` → tracked with defaults ({})\n * `vrt={false}` → untracked (absent)\n * `vrt={{ threshold, skip, ignore }}` → tracked with overrides\n */\nfunction readVrt(\n openingElement: any,\n errors: CanvasParseError[],\n): {\n /** Null when the attribute exists but reads as untracked (`vrt={false}`,\n * dynamic / malformed forms). The span stays so editors can target the\n * declaration without inserting a duplicate. */\n value: ParsedStoryboardVrt | null;\n attributeSpan?: CanvasSourceSpan;\n} | null {\n const attribute = getAttribute(openingElement, \"vrt\");\n if (!attribute) return null;\n const attributeSpan = toSpan(attribute);\n\n // Bare `vrt` (no value) — tracked with defaults.\n if (attribute.value == null) {\n return { value: {}, attributeSpan };\n }\n\n const expression = unwrapTsExpression(\n unwrapAttributeExpression(attribute.value),\n );\n if (expression?.type === \"Literal\" && expression.value === true) {\n return { value: {}, attributeSpan };\n }\n if (expression?.type === \"Literal\" && expression.value === false) {\n return { value: null, attributeSpan };\n }\n if (expression?.type !== \"ObjectExpression\") {\n errors.push({\n message:\n '<Storyboard> vrt must be a boolean literal or an object literal ({ threshold?, skip?, ignore? })',\n span: toSpan(expression) ?? attributeSpan,\n kind: \"degraded\",\n });\n return { value: null, attributeSpan };\n }\n\n const value: ParsedStoryboardVrt = {};\n for (const property of expression.properties ?? []) {\n if (property?.type !== \"Property\" || property.computed) {\n errors.push({\n message: \"<Storyboard> vrt must contain only literal properties\",\n span: toSpan(property ?? expression),\n kind: \"degraded\",\n });\n continue;\n }\n const key = readPropertyKeyName(property.key);\n if (key === \"threshold\") {\n // Capped at 0.5: beyond that pixelmatch is effectively blind and the\n // \"tracked\" mark would be a lie — black→white passes at threshold 1.\n const threshold = readNumberLiteral(property.value);\n if (threshold && threshold.value >= 0 && threshold.value <= 0.5) {\n value.threshold = threshold.value;\n } else {\n errors.push({\n message:\n \"<Storyboard> vrt.threshold must be a number literal between 0 and 0.5\",\n span: toSpan(property),\n kind: \"degraded\",\n });\n }\n continue;\n }\n if (key === \"skip\") {\n if (\n property.value?.type === \"Literal\" &&\n typeof property.value.value === \"boolean\"\n ) {\n if (property.value.value) value.skip = true;\n } else {\n errors.push({\n message: \"<Storyboard> vrt.skip must be a boolean literal\",\n span: toSpan(property),\n kind: \"degraded\",\n });\n }\n continue;\n }\n if (key === \"ignore\") {\n const regions = readVrtIgnoreRegions(property.value, errors);\n if (regions) value.ignore = regions;\n continue;\n }\n errors.push({\n message: `<Storyboard> vrt has unknown property \"${key ?? \"?\"}\" — expected threshold, skip, or ignore`,\n span: toSpan(property),\n kind: \"degraded\",\n });\n }\n\n return { value, attributeSpan };\n}\n\nfunction readVrtIgnoreRegions(\n node: any,\n errors: CanvasParseError[],\n): ParsedStoryboardVrtIgnoreRegion[] | null {\n if (node?.type !== \"ArrayExpression\") {\n errors.push({\n message:\n \"<Storyboard> vrt.ignore must be an array of { x, y, w, h } literals\",\n span: toSpan(node),\n kind: \"degraded\",\n });\n return null;\n }\n const regions: ParsedStoryboardVrtIgnoreRegion[] = [];\n for (const element of node.elements ?? []) {\n const region: Partial<ParsedStoryboardVrtIgnoreRegion> = {};\n for (const key of [\"x\", \"y\", \"w\", \"h\"] as const) {\n const property = getObjectProperty(element, key);\n const number = property ? readNumberLiteral(property.value) : null;\n if (number) region[key] = number.value;\n }\n if (\n region.x === undefined ||\n region.y === undefined ||\n region.w === undefined ||\n region.h === undefined\n ) {\n errors.push({\n message:\n \"<Storyboard> vrt.ignore regions must be { x, y, w, h } number literals\",\n span: toSpan(element) ?? toSpan(node),\n kind: \"degraded\",\n });\n return null;\n }\n regions.push(region as ParsedStoryboardVrtIgnoreRegion);\n }\n return regions;\n}\n\nfunction readComponentReference(\n openingElement: any,\n imports: ParsedImports,\n source: string,\n): ParsedCanvasComponentReference | null {\n const attribute = getAttribute(openingElement, \"component\");\n const expression = unwrapAttributeExpression(attribute?.value);\n const span = toSpan(expression);\n if (!expression || !span) return null;\n\n const localName =\n expression.type === \"Identifier\"\n ? expression.name\n : expression.type === \"MemberExpression\" &&\n expression.object?.type === \"Identifier\"\n ? expression.object.name\n : undefined;\n\n return {\n expression: source.slice(span.start, span.end),\n span,\n attributeSpan: toSpan(attribute),\n localName,\n import: localName ? imports.byLocalName.get(localName) : undefined,\n };\n}\n\nfunction readRenderComponentReference(\n renderValue: any,\n imports: ParsedImports,\n source: string,\n): ParsedCanvasComponentReference | null {\n if (renderValue?.type !== \"ArrowFunctionExpression\") return null;\n const body = unwrapTsExpression(renderValue.body);\n if (body?.type !== \"JSXElement\") return null;\n const name = body.openingElement?.name;\n if (name?.type !== \"JSXIdentifier\") return null;\n\n const span = toSpan(name);\n if (!span || typeof name.name !== \"string\") return null;\n\n return {\n expression: source.slice(span.start, span.end),\n span,\n localName: name.name,\n import: imports.byLocalName.get(name.name),\n };\n}\n\nfunction readExpressionReference(\n openingElement: any,\n name: string,\n source: string,\n): ParsedCanvasExpressionReference | undefined {\n const attribute = getAttribute(openingElement, name);\n const expression = unwrapAttributeExpression(attribute?.value);\n const span = toSpan(expression);\n if (!expression || !span) return undefined;\n return {\n expression: source.slice(span.start, span.end),\n span,\n attributeSpan: toSpan(attribute),\n };\n}\n\nfunction isIdentifierExpression(expression: string): boolean {\n return /^[A-Za-z_$][\\w$]*$/.test(expression);\n}\n\nfunction readLayout(\n openingElement: any,\n componentName: string,\n): {\n value: ParsedCanvasLayout;\n attributeSpan?: CanvasSourceSpan;\n spans?: ParsedCanvasLayoutSpans;\n errors: CanvasParseError[];\n} {\n const errors: CanvasParseError[] = [];\n const attribute = getAttribute(openingElement, \"layout\");\n const expression = unwrapAttributeExpression(attribute?.value);\n\n if (!expression) {\n return {\n value: DEFAULT_LAYOUT,\n attributeSpan: toSpan(attribute),\n errors: [\n {\n message: `<${componentName}> must include a literal \"layout\" object prop`,\n span: toSpan(openingElement),\n kind: \"degraded\",\n },\n ],\n };\n }\n\n const layout = readLayoutExpression(expression, componentName);\n return {\n ...layout,\n attributeSpan: toSpan(attribute),\n };\n}\n\nfunction readLayoutExpression(\n expression: any,\n componentName: string,\n): {\n value: ParsedCanvasLayout;\n spans?: ParsedCanvasLayoutSpans;\n errors: CanvasParseError[];\n} {\n const errors: CanvasParseError[] = [];\n\n if (expression.type !== \"ObjectExpression\") {\n return {\n value: DEFAULT_LAYOUT,\n spans: { layoutClose: readLayoutClose(expression) },\n errors: [\n {\n message: `<${componentName}> layout must be an object literal`,\n span: toSpan(expression),\n kind: \"degraded\",\n },\n ],\n };\n }\n\n const spans: ParsedCanvasLayoutSpans = {\n layoutClose: readLayoutClose(expression),\n };\n const layout: ParsedCanvasLayout = { ...DEFAULT_LAYOUT };\n\n for (const key of [\"x\", \"y\", \"width\", \"height\"] as const) {\n const property = getObjectProperty(expression, key);\n const value = property ? readNumberLiteral(property.value) : null;\n if (value) {\n layout[key] = value.value;\n spans[key] = value.span;\n } else {\n errors.push({\n message: `<${componentName}> layout.${key} must be a number literal`,\n span: toSpan(property ?? expression),\n kind: \"degraded\",\n });\n }\n }\n\n const zIndex = getObjectProperty(expression, \"zIndex\");\n if (zIndex) {\n const value = readNumberLiteral(zIndex.value);\n if (value) {\n layout.zIndex = value.value;\n spans.zIndex = value.span;\n } else {\n errors.push({\n message: `<${componentName}> layout.zIndex must be a number literal`,\n span: toSpan(zIndex),\n kind: \"degraded\",\n });\n }\n }\n\n const intrinsicSizing = getObjectProperty(expression, \"intrinsicSizing\");\n if (intrinsicSizing) {\n if (\n intrinsicSizing.value?.type === \"Literal\" &&\n intrinsicSizing.value.value === \"root-element\"\n ) {\n layout.intrinsicSizing = \"root-element\";\n spans.intrinsicSizing = toSpan(intrinsicSizing.value);\n } else {\n errors.push({\n message: `<${componentName}> layout.intrinsicSizing must be \"root-element\" when set`,\n span: toSpan(intrinsicSizing),\n kind: \"degraded\",\n });\n }\n }\n\n return { value: layout, spans, errors };\n}\n\nfunction readStringLiteralValue(\n node: any,\n): { value: string; span: CanvasSourceSpan } | null {\n return node?.type === \"Literal\" && typeof node.value === \"string\"\n ? { value: node.value, span: toSpan(node) ?? { start: 0, end: 0 } }\n : null;\n}\n\nexport function unwrapTsExpression(node: any): any {\n let current = node;\n while (\n current?.type === \"TSSatisfiesExpression\" ||\n current?.type === \"TSAsExpression\" ||\n current?.type === \"TSNonNullExpression\" ||\n current?.type === \"ParenthesizedExpression\"\n ) {\n current = current.expression;\n }\n return current;\n}\n\nfunction readResolvableStringAttribute(\n openingElement: any,\n name: string,\n bindings: ObjectStringBindings,\n): {\n value: string;\n valueSpan: CanvasSourceSpan;\n attributeSpan: CanvasSourceSpan;\n} | null {\n const literal = readStringAttribute(openingElement, name);\n if (literal) return literal;\n\n const attribute = getAttribute(openingElement, name);\n const expression = unwrapAttributeExpression(attribute?.value);\n const expressionSpan = toSpan(expression);\n const attributeSpan = toSpan(attribute);\n if (!expression || !expressionSpan || !attributeSpan) return null;\n\n const resolved = resolveStringMemberExpression(expression, bindings);\n return resolved\n ? {\n value: resolved.value,\n valueSpan: expressionSpan,\n attributeSpan,\n }\n : null;\n}\n\nfunction resolveStringMemberExpression(\n expression: any,\n bindings: ObjectStringBindings,\n): { value: string; span: CanvasSourceSpan } | null {\n if (expression?.type !== \"MemberExpression\") return null;\n if (expression.object?.type !== \"Identifier\") return null;\n const propertyName = readPropertyKeyName(expression.property);\n if (!propertyName) return null;\n return bindings.get(expression.object.name)?.get(propertyName) ?? null;\n}\n\nfunction readStringAttribute(\n openingElement: any,\n name: string,\n): {\n value: string;\n valueSpan: CanvasSourceSpan;\n attributeSpan: CanvasSourceSpan;\n} | null {\n const attribute = getAttribute(openingElement, name);\n if (!attribute) return null;\n\n const value = attribute.value;\n if (value?.type === \"Literal\" && typeof value.value === \"string\") {\n return {\n value: value.value,\n valueSpan: toSpan(value) ?? toSpan(attribute) ?? { start: 0, end: 0 },\n attributeSpan: toSpan(attribute) ?? { start: 0, end: 0 },\n };\n }\n\n const expression = unwrapAttributeExpression(value);\n if (expression?.type === \"Literal\" && typeof expression.value === \"string\") {\n return {\n value: expression.value,\n valueSpan: toSpan(expression) ??\n toSpan(attribute) ?? { start: 0, end: 0 },\n attributeSpan: toSpan(attribute) ?? { start: 0, end: 0 },\n };\n }\n return null;\n}\n\nfunction getAttribute(openingElement: any, name: string): any | null {\n for (const attribute of openingElement?.attributes ?? []) {\n if (\n attribute?.type === \"JSXAttribute\" &&\n attribute.name?.type === \"JSXIdentifier\" &&\n attribute.name.name === name\n ) {\n return attribute;\n }\n }\n return null;\n}\n\nfunction unwrapAttributeExpression(value: any): any | null {\n if (!value) return null;\n if (value.type === \"JSXExpressionContainer\") {\n return value.expression;\n }\n return value;\n}\n\nfunction readPropertyKeyName(key: any): string | null {\n if (key?.type === \"Identifier\") return key.name;\n if (key?.type === \"Literal\" && typeof key.value === \"string\") {\n return key.value;\n }\n return null;\n}\n\nfunction getObjectProperty(objectExpression: any, name: string): any | null {\n if (objectExpression?.type !== \"ObjectExpression\") return null;\n for (const property of objectExpression.properties ?? []) {\n if (property?.type !== \"Property\" || property.computed) continue;\n const propertyName = readPropertyKeyName(property.key);\n if (propertyName === name) return property;\n }\n return null;\n}\n\nfunction readNumberLiteral(\n node: any,\n): { value: number; span: CanvasSourceSpan } | null {\n if (\n node?.type === \"Literal\" &&\n typeof node.value === \"number\" &&\n Number.isFinite(node.value)\n ) {\n return { value: node.value, span: toSpan(node) ?? { start: 0, end: 0 } };\n }\n\n if (\n node?.type === \"UnaryExpression\" &&\n node.operator === \"-\" &&\n node.argument?.type === \"Literal\" &&\n typeof node.argument.value === \"number\" &&\n Number.isFinite(node.argument.value)\n ) {\n return {\n value: -node.argument.value,\n span: toSpan(node) ?? { start: 0, end: 0 },\n };\n }\n\n return null;\n}\n\nfunction resolveSdkComponent(\n nameNode: any,\n imports: ParsedImports,\n): SdkComponent | null {\n if (nameNode?.type === \"JSXIdentifier\") {\n return imports.sdkLocalNames.get(nameNode.name) ?? null;\n }\n\n if (\n nameNode?.type === \"JSXMemberExpression\" &&\n nameNode.object?.type === \"JSXIdentifier\" &&\n nameNode.property?.type === \"JSXIdentifier\" &&\n imports.sdkNamespaces.has(nameNode.object.name) &&\n SDK_COMPONENTS.has(nameNode.property.name as SdkComponent)\n ) {\n return nameNode.property.name as SdkComponent;\n }\n\n return null;\n}\n\nfunction resolveMisimportedNamespaceSdkComponent(\n nameNode: any,\n imports: ParsedImports,\n misimportedSdkCanvasImports: MutableMisimportedSdkCanvasImports,\n): MisimportedSdkCanvasImport | null {\n if (\n nameNode?.type !== \"JSXMemberExpression\" ||\n nameNode.object?.type !== \"JSXIdentifier\" ||\n nameNode.property?.type !== \"JSXIdentifier\" ||\n !SDK_COMPONENTS.has(nameNode.property.name as SdkComponent)\n ) {\n return null;\n }\n\n const imported = imports.byLocalName.get(nameNode.object.name);\n if (\n imported?.kind !== \"namespace\" ||\n !isWrongTempoSdkCanvasImportSource(imported.source)\n ) {\n return null;\n }\n\n return (\n misimportedSdkCanvasImports.namespaceCandidatesByLocalName.get(\n nameNode.object.name,\n ) ?? null\n );\n}\n\nfunction isStoryboardSdkComponent(\n component: SdkComponent | null,\n): component is StoryboardSdkComponent {\n return (\n component === \"Storyboard\" ||\n component === \"SvelteStoryboard\" ||\n component === \"LegacyStoryboardV1\" ||\n component === \"RouteStoryboard\" ||\n component === \"HtmlStoryboard\"\n );\n}\n\nfunction readImportedName(specifier: any): string {\n if (specifier.type === \"ImportDefaultSpecifier\") return \"default\";\n if (specifier.type === \"ImportNamespaceSpecifier\") return \"*\";\n return specifier.imported?.name ?? specifier.local?.name ?? \"unknown\";\n}\n\nfunction readImportKind(specifier: any): ParsedCanvasImport[\"kind\"] {\n if (specifier.type === \"ImportDefaultSpecifier\") return \"default\";\n if (specifier.type === \"ImportNamespaceSpecifier\") return \"namespace\";\n return \"named\";\n}\n\nfunction isSdkImportSource(source: string): boolean {\n return (\n SDK_IMPORT_SOURCES.has(source) ||\n source.endsWith(\"/tempo-sdk/canvas\") ||\n source.endsWith(\"/tempo-sdk/canvas/svelte\")\n );\n}\n\nfunction isWrongTempoSdkCanvasImportSource(source: string): boolean {\n return (\n (source === \"tempo-sdk\" || source.startsWith(\"tempo-sdk/\")) &&\n !isSdkImportSource(source)\n );\n}\n\nfunction spanKey(span: CanvasSourceSpan): string {\n return `${span.start}:${span.end}`;\n}\n\nfunction readLayoutClose(node: any): number | undefined {\n return typeof node?.end === \"number\" && Number.isFinite(node.end)\n ? node.end - 1\n : undefined;\n}\n\nexport function fallbackPageName(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, \"/\");\n const segments = normalized.split(\"/\").filter(Boolean);\n const fileName =\n segments.length > 0 ? segments[segments.length - 1] : filePath;\n const parentName =\n segments.length > 1 ? segments[segments.length - 2] : fileName;\n const baseName = /^index\\.canvas\\.[tj]sx?$/.test(fileName)\n ? parentName\n : fileName.replace(/\\.canvas\\.[tj]sx?$/, \"\");\n\n return baseName\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \");\n}\n\nfunction toSpan(node: any): CanvasSourceSpan | undefined {\n return typeof node?.start === \"number\" &&\n Number.isFinite(node.start) &&\n typeof node?.end === \"number\" &&\n Number.isFinite(node.end)\n ? { start: node.start, end: node.end }\n : undefined;\n}\n\nfunction walk(node: any, visitor: (node: any) => void, seen = new Set<any>()) {\n if (!node || typeof node !== \"object\" || seen.has(node)) return;\n seen.add(node);\n visitor(node);\n\n for (const value of Object.values(node)) {\n if (!value) continue;\n if (Array.isArray(value)) {\n for (const item of value) {\n walk(item, visitor, seen);\n }\n } else if (typeof value === \"object\") {\n walk(value, visitor, seen);\n }\n }\n}\n","import type { DevServerHint, DevServerHintClass } from \"./types\";\n\n/**\n * The hints a dev-server process currently carries, kept in two slots because\n * they have different lifetimes:\n *\n * - An OUTPUT hint is classified from a stdout/stderr line (a compile error,\n * an unresolved import). Nothing in the stream reliably says the problem was\n * fixed, so it stays until the process is replaced.\n * - A SUPERVISOR hint is observed from outside the process (a live host that\n * stopped answering URL probes). It has an observed \"all clear\" — a probe\n * succeeding again — and is withdrawn then.\n *\n * `current()` is what consumers see: the supervisor hint while it applies,\n * otherwise the output hint. Keeping one slot per source is what lets a\n * transient probe hint come and go WITHOUT erasing a compile diagnostic that\n * is still true (review of PR #3267).\n */\nexport class ProcessHintSlots {\n private outputHint: DevServerHint | null = null;\n private supervisorHint: DevServerHint | null = null;\n\n current(): DevServerHint | null {\n return this.supervisorHint ?? this.outputHint;\n }\n\n recordOutput(hint: DevServerHint): void {\n this.outputHint = hint;\n }\n\n recordSupervisor(hint: DevServerHint): void {\n this.supervisorHint = hint;\n }\n\n /** Withdraw the supervisor hint when it is of `hintClass`; false if it was not. */\n clearSupervisor(hintClass: DevServerHintClass): boolean {\n if (this.supervisorHint?.class !== hintClass) return false;\n this.supervisorHint = null;\n return true;\n }\n}\n","import { access, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Which package manager the user's project root is configured for, detected\n * from its lockfile. This drives the `scripts.install` value Tempo writes\n * into `tempo.config.json` so the supervisor's install pass also installs\n * the user-side deps that the canvas's components import from `../src/`.\n *\n * Without this, workspaces sit on shimmer forever the first time they're\n * opened because Vite's `Failed to resolve import` for every user-side dep\n * keeps the storyboards in `hostProblem` state.\n */\n\nexport type DetectedPackageManager = {\n pm: \"bun\" | \"pnpm\" | \"yarn\" | \"npm\";\n /**\n * The install command to run from the project root. These are local\n * recovery installs, so they intentionally avoid strict CI-only flags that\n * fail when a user's lockfile is stale.\n */\n installCmd: string;\n /**\n * Exact version pinned by the `packageManager` field when detection came\n * from one — `\"10.32.1\"` for `packageManager: \"pnpm@10.32.1+sha512…\"`.\n * Absent for lockfile/marker-based detection. Lets callers with a specific\n * PM build on hand (e.g. Electron's staged tutorial pnpm) decide whether it\n * satisfies the project's pin.\n */\n pmVersion?: string;\n};\n\nconst LOCKFILE_PRIORITY: ReadonlyArray<{\n file: string;\n pm: DetectedPackageManager[\"pm\"];\n installCmd: string;\n}> = [\n // Order matters: a project with both bun.lock and package-lock.json is\n // a bun project (the package-lock.json is stale). We bias to whichever\n // lockfile the user actively maintains.\n { file: \"bun.lock\", pm: \"bun\", installCmd: \"bun install\" },\n { file: \"bun.lockb\", pm: \"bun\", installCmd: \"bun install\" },\n {\n file: \"pnpm-lock.yaml\",\n pm: \"pnpm\",\n installCmd: \"pnpm install --no-frozen-lockfile\",\n },\n { file: \"yarn.lock\", pm: \"yarn\", installCmd: \"yarn install\" },\n { file: \"package-lock.json\", pm: \"npm\", installCmd: \"npm install\" },\n];\n\nconst INSTALL_COMMAND_BY_PM: Record<DetectedPackageManager[\"pm\"], string> = {\n bun: \"bun install\",\n pnpm: \"pnpm install --no-frozen-lockfile\",\n yarn: \"yarn install\",\n npm: \"npm install\",\n};\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\ntype PackageManagerPin = {\n pm: DetectedPackageManager[\"pm\"];\n pmVersion?: string;\n};\n\nfunction parsePackageManagerField(value: unknown): PackageManagerPin | null {\n if (typeof value !== \"string\") return null;\n const [name, versionWithHash] = value.split(\"@\");\n const pm = name?.trim();\n if (pm !== \"bun\" && pm !== \"pnpm\" && pm !== \"yarn\" && pm !== \"npm\") {\n return null;\n }\n // The field may carry an integrity suffix: `pnpm@10.32.1+sha512.…`.\n const pmVersion = versionWithHash?.split(\"+\")[0]?.trim();\n return pmVersion ? { pm, pmVersion } : { pm };\n}\n\nasync function readPackageManagerFromPackageJson(\n packageJsonPath: string,\n): Promise<PackageManagerPin | null> {\n try {\n const parsed = JSON.parse(await readFile(packageJsonPath, \"utf-8\")) as {\n packageManager?: unknown;\n };\n return parsePackageManagerField(parsed.packageManager);\n } catch {\n return null;\n }\n}\n\n/**\n * Detect the package manager for a project root. Returns null when there's\n * no `package.json` (caller treats this as \"no root install needed\").\n *\n * When a `package.json` exists but no lockfile, falls back to `npm install`\n * — npm is the only PM guaranteed to be present on a Node install, and it\n * will generate a `package-lock.json` on first run.\n */\nexport async function detectRootPackageManager(\n projectRoot: string,\n): Promise<DetectedPackageManager | null> {\n const resolvedRoot = path.resolve(projectRoot);\n const packageJsonPath = path.join(resolvedRoot, \"package.json\");\n if (!(await fileExists(packageJsonPath))) {\n return null;\n }\n\n const pin = await readPackageManagerFromPackageJson(packageJsonPath);\n if (pin) {\n return { ...pin, installCmd: INSTALL_COMMAND_BY_PM[pin.pm] };\n }\n\n for (const candidate of LOCKFILE_PRIORITY) {\n if (await fileExists(path.join(resolvedRoot, candidate.file))) {\n return { pm: candidate.pm, installCmd: candidate.installCmd };\n }\n }\n\n // package.json with no lockfile — most likely a freshly-scaffolded\n // workspace where install has never run. npm is the safest fallback\n // (always available) and will generate the lockfile on first run.\n return { pm: \"npm\", installCmd: \"npm install\" };\n}\n\n/**\n * Ownership markers for the ancestor walk, strongest first. Same ordering\n * rationale as LOCKFILE_PRIORITY (bias to the actively-maintained tool), with\n * one addition: `pnpm-workspace.yaml` OUTRANKS `package-lock.json`. A pnpm\n * workspace root can carry a stale npm lockfile (migrated repo) or no pnpm\n * lockfile yet (first install pending, lockfile gitignored) — and picking npm\n * there is the exact EUNSUPPORTEDPROTOCOL failure this detection exists to\n * prevent, because npm cannot parse `workspace:`/`catalog:` specs at all.\n */\nconst OWNERSHIP_MARKER_PRIORITY: ReadonlyArray<{\n file: string;\n pm: DetectedPackageManager[\"pm\"];\n}> = [\n { file: \"bun.lock\", pm: \"bun\" },\n { file: \"bun.lockb\", pm: \"bun\" },\n { file: \"pnpm-lock.yaml\", pm: \"pnpm\" },\n { file: \"yarn.lock\", pm: \"yarn\" },\n { file: \"pnpm-workspace.yaml\", pm: \"pnpm\" },\n { file: \"package-lock.json\", pm: \"npm\" },\n];\n\nasync function detectPackageManagerMarkerInDir(\n dir: string,\n): Promise<PackageManagerPin | null> {\n const fromPackageJson = await readPackageManagerFromPackageJson(\n path.join(dir, \"package.json\"),\n );\n if (fromPackageJson) return fromPackageJson;\n for (const candidate of OWNERSHIP_MARKER_PRIORITY) {\n if (await fileExists(path.join(dir, candidate.file))) {\n return { pm: candidate.pm };\n }\n }\n return null;\n}\n\n/**\n * Which package manager owns `packageRoot`'s `node_modules` — the same\n * question as {@link detectRootPackageManager}, but for roots that may be\n * workspace MEMBERS rather than standalone projects.\n *\n * A workspace member has no lockfile of its own: its install is governed by\n * the nearest ancestor carrying one. So after checking the root itself\n * (explicit `packageManager` field, then its own lockfile), this walks UP\n * until the first directory with a package-manager marker and lets THAT\n * directory decide. The walk stops at the first hit — walking past it would\n * let an unrelated outer repo's lockfile decide.\n *\n * The ancestor walk is the TEM-1338 fix: the canvas dependency install ran\n * the per-root detection on pnpm-workspace members (`tempo/`,\n * `tempo-modules/*` — package.json full of `catalog:` / `workspace:*` specs,\n * lockfile only at the workspace root), fell back to `npm install`, and npm\n * exits with `EUNSUPPORTEDPROTOCOL: Unsupported URL Type \"catalog:\"` before\n * ever contacting the registry.\n */\nexport async function detectOwningPackageManager(\n packageRoot: string,\n): Promise<DetectedPackageManager | null> {\n const resolvedRoot = path.resolve(packageRoot);\n if (!(await fileExists(path.join(resolvedRoot, \"package.json\")))) {\n return null;\n }\n\n for (let dir = resolvedRoot; ; dir = path.dirname(dir)) {\n const pin = await detectPackageManagerMarkerInDir(dir);\n if (pin) return { ...pin, installCmd: INSTALL_COMMAND_BY_PM[pin.pm] };\n if (path.dirname(dir) === dir) break;\n }\n\n return { pm: \"npm\", installCmd: \"npm install\" };\n}\n","/**\n * Project Node-version reconciliation for managed commands (TEM-1536).\n *\n * The managed devserver shells out package-manager commands (the `tempo/`\n * install, the tempo-sdk floor upgrade) with whatever `node` the user's PATH\n * happens to resolve. A repo that pins a newer Node than that default —\n * `package.json` `engines.node: \">=24\"` with `engine-strict=true` — makes pnpm\n * refuse outright (`ERR_PNPM_UNSUPPORTED_ENGINE`), and the canvas hard-blocks\n * on the SDK floor with no in-app recovery: Retry re-runs the same command\n * under the same Node, and the remediation command Tempo prints fails the same\n * way in the user's terminal. The user's own project scripts don't hit this\n * because they pin Node themselves (nvm/nodenv shell hooks); Tempo's managed\n * commands bypassed all of that.\n *\n * This module answers \"which installed Node should Tempo's managed commands\n * for this project run under?\" by reconciling three inputs:\n *\n * - the project's declared requirement — every `engines.node` range from the\n * tempo root up through the repo boundary (the lockfile owner / `.git` dir,\n * the same scope a package manager's engine check evaluates), plus the\n * nearest `.nvmrc` / `.node-version` as a steering hint;\n * - the Node versions the user actually has installed via a version manager —\n * nvm (`$NVM_DIR/versions/node/v*`), nodenv (`$NODENV_ROOT/versions/*`),\n * nvm-windows (`%NVM_HOME%\\v*`);\n * - the Node the managed env currently resolves.\n *\n * FALLBACK-ONLY, same policy as bundled-node.ts: a current Node that satisfies\n * every declared range is never shadowed. Only when it violates the project's\n * requirement do we pick a satisfying installed version (preferring the one\n * the version file names, then the highest) — and the caller may fall back to\n * the host's bundled Node when no version-manager install satisfies. Every\n * chosen override must also pass the canvas-host floor check the caller\n * provides, so honoring an old project pin can never break the Vite host.\n */\nimport { access, readFile, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/** A Node install discovered under a version manager's versions directory. */\nexport type NodeCandidate = {\n /** Full `major.minor.patch`, no leading `v`. */\n version: string;\n /** Directory to prepend to PATH so `node` (and its npm/npx) resolve to this install. */\n binDir: string;\n source: \"nvm\" | \"nodenv\" | \"nvm-windows\";\n};\n\n/** The project's declared Node requirement, read from disk. */\nexport type ProjectNodeRequirement = {\n /** Every `engines.node` range found on the walk, tempo root upward. */\n engines: Array<{ range: string; dir: string }>;\n /** Nearest `.nvmrc` / `.node-version` content (trimmed), or null. */\n versionFileSpec: string | null;\n};\n\nexport type NodeOverrideDecision =\n | { kind: \"none\"; reason: \"no-constraint\" | \"current-satisfies\" }\n | { kind: \"override\"; candidate: NodeCandidate }\n | {\n kind: \"unsatisfied\";\n /** Human-readable joined requirement, e.g. `>=24`. */\n requirement: string;\n currentVersion: string | null;\n };\n\ntype VersionTriple = { major: number; minor: number; patch: number };\n\nfunction parseTriple(value: string): (VersionTriple & { precision: 1 | 2 | 3 }) | null {\n const match = value\n .trim()\n .replace(/^v/i, \"\")\n .match(/^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?$/);\n if (!match?.[1]) return null;\n const major = Number.parseInt(match[1], 10);\n const minor = match[2] !== undefined ? Number.parseInt(match[2], 10) : 0;\n const patch = match[3] !== undefined ? Number.parseInt(match[3], 10) : 0;\n if (![major, minor, patch].every(Number.isFinite)) return null;\n const precision = match[3] !== undefined ? 3 : match[2] !== undefined ? 2 : 1;\n return { major, minor, patch, precision };\n}\n\nfunction compareTriples(a: VersionTriple, b: VersionTriple): number {\n if (a.major !== b.major) return a.major - b.major;\n if (a.minor !== b.minor) return a.minor - b.minor;\n return a.patch - b.patch;\n}\n\n/**\n * Whether `version` satisfies an `engines.node`-style range. Supports the\n * shapes that actually appear in the field: comparators (`>=24`, `>20 <=22.14`,\n * with or without a space after the operator), OR alternatives\n * (`^20.19.0 || >=22.12.0`), caret/tilde, x-ranges (`24.x`), bare\n * major/major.minor prefixes (`24`, `24.1`), and `*`.\n *\n * Returns `null` when the range can't be parsed — the caller must treat that\n * as NO constraint (never block a project on a spec we don't understand).\n */\nexport function nodeRangeSatisfied(\n version: string,\n range: string,\n): boolean | null {\n const versionTriple = parseTriple(version);\n if (!versionTriple) return null;\n\n const clauses = range\n .split(\"||\")\n .map((clause) => clause.trim())\n .filter((clause) => clause.length > 0);\n if (clauses.length === 0) return null;\n\n let anyClauseParsed = false;\n for (const clause of clauses) {\n const result = clauseSatisfied(versionTriple, clause);\n if (result === null) continue;\n anyClauseParsed = true;\n if (result) return true;\n }\n return anyClauseParsed ? false : null;\n}\n\nfunction clauseSatisfied(\n version: VersionTriple & { precision: 1 | 2 | 3 },\n clause: string,\n): boolean | null {\n // Merge operator-only tokens with the version that follows (`>= 20.19`).\n const rawTokens = clause.split(/\\s+/).filter((token) => token.length > 0);\n const tokens: string[] = [];\n for (let i = 0; i < rawTokens.length; i += 1) {\n const token = rawTokens[i];\n if (/^(>=|<=|>|<|=|\\^|~)$/.test(token) && i + 1 < rawTokens.length) {\n tokens.push(token + rawTokens[i + 1]);\n i += 1;\n } else {\n tokens.push(token);\n }\n }\n if (tokens.length === 0) return null;\n\n for (const token of tokens) {\n const result = comparatorSatisfied(version, token);\n if (result === null) return null;\n if (!result) return false;\n }\n return true;\n}\n\nfunction comparatorSatisfied(\n version: VersionTriple & { precision: 1 | 2 | 3 },\n token: string,\n): boolean | null {\n if (token === \"*\" || token.toLowerCase() === \"x\") return true;\n\n const operatorMatch = token.match(/^(>=|<=|>|<|=|\\^|~)(.+)$/);\n const operator = operatorMatch?.[1] ?? \"\";\n const rest = (operatorMatch?.[2] ?? token).trim();\n\n // X-ranges (`24.x`, `24.1.x`, `24.*`) — prefix match at the given precision.\n const xRange = rest.match(/^v?(\\d+)(?:\\.(\\d+))?\\.(?:x|X|\\*)$/);\n if (xRange && (operator === \"\" || operator === \"=\")) {\n const major = Number.parseInt(xRange[1], 10);\n if (xRange[2] !== undefined) {\n return (\n version.major === major &&\n version.minor === Number.parseInt(xRange[2], 10)\n );\n }\n return version.major === major;\n }\n\n const bound = parseTriple(rest);\n if (!bound) return null;\n\n switch (operator) {\n case \">=\":\n return compareTriples(version, bound) >= 0;\n case \">\": {\n // npm semver desugars a partial upper operand: `>24` means \">=25.0.0\"\n // and `>24.1` means \">=24.2.0\" — NOT \">24.0.0\". pnpm evaluates engines\n // with npm semver, so matching its reading here is what keeps a chosen\n // override from being rejected right back by the package manager.\n const floor: VersionTriple =\n bound.precision === 1\n ? { major: bound.major + 1, minor: 0, patch: 0 }\n : bound.precision === 2\n ? { major: bound.major, minor: bound.minor + 1, patch: 0 }\n : { major: bound.major, minor: bound.minor, patch: bound.patch };\n return bound.precision === 3\n ? compareTriples(version, floor) > 0\n : compareTriples(version, floor) >= 0;\n }\n case \"<=\":\n // `<=24` (precision 1) allows all of 24.x, so compare at the bound's\n // precision: truncate the version to the same precision first.\n return compareTriples(truncateTo(version, bound.precision), bound) <= 0;\n case \"<\":\n return compareTriples(version, bound) < 0;\n case \"^\":\n if (compareTriples(version, bound) < 0) return false;\n if (bound.major > 0) return version.major === bound.major;\n if (bound.minor > 0)\n return version.major === 0 && version.minor === bound.minor;\n return (\n version.major === 0 && version.minor === 0 && version.patch === bound.patch\n );\n case \"~\":\n if (compareTriples(version, bound) < 0) return false;\n if (bound.precision >= 2)\n return version.major === bound.major && version.minor === bound.minor;\n return version.major === bound.major;\n case \"=\":\n case \"\": {\n // Bare/`=` versions match at their stated precision: `24` is any 24.x.\n if (bound.precision === 1) return version.major === bound.major;\n if (bound.precision === 2)\n return version.major === bound.major && version.minor === bound.minor;\n return compareTriples(version, bound) === 0;\n }\n default:\n return null;\n }\n}\n\nfunction truncateTo(\n version: VersionTriple,\n precision: 1 | 2 | 3,\n): VersionTriple {\n if (precision === 1) return { major: version.major, minor: 0, patch: 0 };\n if (precision === 2)\n return { major: version.major, minor: version.minor, patch: 0 };\n return version;\n}\n\n/**\n * Whether `version` matches a `.nvmrc` / `.node-version` spec, using the\n * version managers' prefix semantics: `24` matches any 24.x.y, `24.1` any\n * 24.1.y, `v24.1.0` exactly. Alias specs (`lts/*`, `node`) return null —\n * resolving them needs the manager's own alias database, so they neither\n * match nor exclude.\n */\nexport function matchesVersionFileSpec(\n version: string,\n spec: string,\n): boolean | null {\n const parsedSpec = parseTriple(spec);\n const parsedVersion = parseTriple(version);\n if (!parsedSpec || !parsedVersion) return null;\n if (parsedSpec.precision === 1) return parsedVersion.major === parsedSpec.major;\n if (parsedSpec.precision === 2)\n return (\n parsedVersion.major === parsedSpec.major &&\n parsedVersion.minor === parsedSpec.minor\n );\n return compareTriples(parsedVersion, parsedSpec) === 0;\n}\n\n// Same boundary set as sdk-upgrade-plan's lockfile-owner walk: the first\n// directory carrying any of these owns the repo, and a package manager's\n// engine check never looks above it.\nconst BOUNDARY_LOCKFILES = [\n \"bun.lock\",\n \"bun.lockb\",\n \"pnpm-lock.yaml\",\n \"yarn.lock\",\n \"package-lock.json\",\n] as const;\n\nasync function pathExists(candidate: string): Promise<boolean> {\n try {\n await access(candidate);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Read the project's declared Node requirement, walking from `startDir` up to\n * and including the repo boundary — the first directory carrying a lockfile or\n * `.git` (a plain file in worktrees, which is why this checks existence, not\n * directory-ness). Every `engines.node` on the walk is collected (an override\n * must satisfy all of them, exactly like a package manager evaluating both the\n * project and its workspace root); the NEAREST `.nvmrc` / `.node-version`\n * wins as the steering hint.\n */\nexport async function readProjectNodeRequirement(\n startDir: string,\n): Promise<ProjectNodeRequirement> {\n const engines: Array<{ range: string; dir: string }> = [];\n let versionFileSpec: string | null = null;\n\n for (let dir = path.resolve(startDir); ; dir = path.dirname(dir)) {\n const packageJson = await readFile(path.join(dir, \"package.json\"), \"utf-8\")\n .then((raw) => JSON.parse(raw) as { engines?: { node?: unknown } })\n .catch(() => null);\n const range = packageJson?.engines?.node;\n if (typeof range === \"string\" && range.trim() && range.trim() !== \"*\") {\n engines.push({ range: range.trim(), dir });\n }\n\n if (versionFileSpec === null) {\n for (const file of [\".nvmrc\", \".node-version\"]) {\n const raw = await readFile(path.join(dir, file), \"utf-8\").catch(\n () => null,\n );\n const trimmed = raw?.trim();\n if (trimmed) {\n versionFileSpec = trimmed;\n break;\n }\n }\n }\n\n const atBoundary =\n (await pathExists(path.join(dir, \".git\"))) ||\n (\n await Promise.all(\n BOUNDARY_LOCKFILES.map((file) => pathExists(path.join(dir, file))),\n )\n ).some(Boolean);\n if (atBoundary || path.dirname(dir) === dir) break;\n }\n\n return { engines, versionFileSpec };\n}\n\n/**\n * Enumerate Node installs from the version managers' on-disk layouts. No\n * shell-out: nvm is a shell function (unreachable from a GUI-spawned process\n * anyway), but its installs are plain directories. Skips entries without a\n * runnable `node` binary. Sorted newest-first.\n */\nexport async function discoverVersionManagerNodes(options: {\n env: NodeJS.ProcessEnv;\n homeDir: string;\n platform: NodeJS.Platform;\n}): Promise<NodeCandidate[]> {\n const { env, homeDir, platform } = options;\n const candidates: NodeCandidate[] = [];\n\n const collect = async (\n versionsDir: string,\n source: NodeCandidate[\"source\"],\n layout: \"bin-subdir\" | \"flat\",\n ) => {\n const entries = await readdir(versionsDir).catch(() => []);\n for (const entry of entries) {\n const parsed = parseTriple(entry);\n if (!parsed || parsed.precision !== 3) continue;\n const binDir =\n layout === \"bin-subdir\"\n ? path.join(versionsDir, entry, \"bin\")\n : path.join(versionsDir, entry);\n const nodeBinary = path.join(\n binDir,\n platform === \"win32\" ? \"node.exe\" : \"node\",\n );\n if (!(await pathExists(nodeBinary))) continue;\n candidates.push({\n version: `${parsed.major}.${parsed.minor}.${parsed.patch}`,\n binDir,\n source,\n });\n }\n };\n\n if (platform !== \"win32\") {\n const nvmDir = env.NVM_DIR?.trim() || path.join(homeDir, \".nvm\");\n await collect(path.join(nvmDir, \"versions\", \"node\"), \"nvm\", \"bin-subdir\");\n const nodenvRoot =\n env.NODENV_ROOT?.trim() || path.join(homeDir, \".nodenv\");\n await collect(path.join(nodenvRoot, \"versions\"), \"nodenv\", \"bin-subdir\");\n }\n if (env.NVM_HOME?.trim()) {\n // nvm-windows keeps `v24.1.0\\node.exe` directly in the version dir.\n await collect(env.NVM_HOME.trim(), \"nvm-windows\", \"flat\");\n }\n\n return candidates.sort((a, b) => {\n const parsedA = parseTriple(a.version);\n const parsedB = parseTriple(b.version);\n if (!parsedA || !parsedB) return 0;\n return compareTriples(parsedB, parsedA);\n });\n}\n\n/** Ranges from `requirement` that parse; unparseable ones impose nothing. */\nfunction usableRanges(requirement: ProjectNodeRequirement): string[] {\n return requirement.engines\n .map((entry) => entry.range)\n .filter((range) => nodeRangeSatisfied(\"0.0.0\", range) !== null);\n}\n\n/** Whether `version` satisfies every usable declared range. */\nexport function versionSatisfiesRequirement(\n version: string,\n requirement: ProjectNodeRequirement,\n): boolean {\n return usableRanges(requirement).every(\n (range) => nodeRangeSatisfied(version, range) === true,\n );\n}\n\n/**\n * The fallback-only override decision. `floorOk` is the canvas-host floor\n * (Vite 7's 20.19+/22.12+): a candidate that would satisfy the project but\n * break the host itself is never chosen — better to fail with the current\n * behavior than to trade one dead canvas for another.\n */\nexport function selectNodeOverride(args: {\n currentVersion: string | null;\n requirement: ProjectNodeRequirement;\n candidates: NodeCandidate[];\n floorOk: (version: string) => boolean;\n}): NodeOverrideDecision {\n const ranges = usableRanges(args.requirement);\n if (ranges.length === 0) return { kind: \"none\", reason: \"no-constraint\" };\n\n if (\n args.currentVersion !== null &&\n versionSatisfiesRequirement(args.currentVersion, args.requirement)\n ) {\n return { kind: \"none\", reason: \"current-satisfies\" };\n }\n\n const viable = args.candidates.filter(\n (candidate) =>\n versionSatisfiesRequirement(candidate.version, args.requirement) &&\n args.floorOk(candidate.version),\n );\n const hint = args.requirement.versionFileSpec;\n const hinted = hint\n ? viable.filter(\n (candidate) => matchesVersionFileSpec(candidate.version, hint) === true,\n )\n : [];\n const chosen = hinted[0] ?? viable[0];\n if (chosen) return { kind: \"override\", candidate: chosen };\n\n return {\n kind: \"unsatisfied\",\n requirement: ranges.join(\" and \"),\n currentVersion: args.currentVersion,\n };\n}\n\n/**\n * One actionable sentence for the hard-blocked case: the project pins a Node\n * the machine simply doesn't have. Names the requirement, what Tempo checked,\n * and the exact install command (major extracted from the requirement).\n */\nexport function buildUnsatisfiedNodeHint(args: {\n requirement: string;\n currentVersion: string | null;\n}): string {\n const major = args.requirement.match(/(\\d+)/)?.[1];\n const installExample = major ? ` (e.g. \\`nvm install ${major}\\`)` : \"\";\n const current = args.currentVersion\n ? `your default is ${args.currentVersion}`\n : \"no usable Node was found on PATH\";\n return (\n `This project requires Node ${args.requirement} (package.json engines.node) and ${current}; ` +\n `Tempo checked nvm and nodenv and found no installed version that satisfies it. ` +\n `Install one${installExample}, then choose Retry.`\n );\n}\n","import { access, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * How to move the managed `tempo-sdk` under `tempo/` to the latest published\n * version, expressed in the package manager that actually owns that tree.\n *\n * Bulk-install detection lives in `package-manager.ts`. SDK maintenance also\n * needs update-specific verbs, metadata-cache flags and workspace scoping to\n * upgrade one dependency inside `tempo/` with its owning package manager.\n *\n * The distinction is the whole bug. `npm update` was hardcoded here, and in a\n * pnpm workspace whose members use `catalog:` / `workspace:*` specs npm exits\n * immediately with `EUNSUPPORTEDPROTOCOL: Unsupported URL Type \"catalog:\"` —\n * before it ever contacts the registry. Every canvas in the tempo-monorepo\n * checkout was stranded on whatever SDK the ancestor lockfile pinned, and the\n * remediation the error printed (`npm install tempo-sdk@latest`) failed the\n * exact same way.\n */\nexport type TempoSdkUpgradePlan = {\n packageManager: \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n /**\n * Directory to run both commands in. Always the tempo root: pnpm, bun and\n * yarn berry each resolve their workspace root from the cwd and scope the\n * update to the containing package, so the command the user is shown is the\n * same one Tempo ran.\n */\n cwd: string;\n /** What Tempo runs to move the managed SDK to latest. */\n updateCommand: string;\n /** What the user is told to run when the managed upgrade fails. */\n remediationCommand: string;\n /**\n * An ancestor workspace owns this install, so the upgrade also writes that\n * workspace's lockfile.\n */\n governedByAncestorWorkspace: boolean;\n};\n\ntype PackageManager = TempoSdkUpgradePlan[\"packageManager\"];\n\nconst INSTALL_MARKERS: ReadonlyArray<{ file: string; pm: PackageManager }> = [\n // Order matters, same rationale as pm-detect: a tree carrying both bun.lock\n // and package-lock.json is a bun tree with a stale npm lockfile.\n { file: \"bun.lock\", pm: \"bun\" },\n { file: \"bun.lockb\", pm: \"bun\" },\n { file: \"pnpm-lock.yaml\", pm: \"pnpm\" },\n { file: \"yarn.lock\", pm: \"yarn\" },\n // A pnpm workspace owns its install even before its first lockfile exists.\n { file: \"pnpm-workspace.yaml\", pm: \"pnpm\" },\n { file: \"package-lock.json\", pm: \"npm\" },\n];\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction parsePackageManagerField(value: unknown): PackageManager | null {\n if (typeof value !== \"string\") return null;\n const name = value.split(\"@\")[0]?.trim();\n if (name === \"bun\" || name === \"pnpm\" || name === \"yarn\" || name === \"npm\") {\n return name;\n }\n return null;\n}\n\n/**\n * Yarn berry (`yarn up`) and yarn classic (`yarn upgrade --latest`) have\n * incompatible upgrade verbs. Berry's lockfile always carries a `__metadata:`\n * block; classic's never does.\n */\nfunction isYarnBerryLockfile(contents: string): boolean {\n return /^__metadata:/m.test(contents);\n}\n\nfunction buildCommands(\n pm: PackageManager,\n yarnBerry: boolean,\n metadataCacheDir: string | null,\n ignorePnpmWorkspace: boolean,\n): { updateCommand: string; remediationCommand: string } {\n switch (pm) {\n case \"pnpm\": {\n // `--latest` is required: without it pnpm keeps the resolution inside\n // the existing range. The `latest` spec in package.json survives.\n //\n // `--config.cacheDir` is the ONLY thing that defeats a stale metadata\n // cache here: pnpm has no `--prefer-online`, and `--force` does not\n // revalidate registry metadata. Pointing it at an empty directory makes\n // the `latest` dist-tag resolve against the registry. This is cheap —\n // cacheDir holds metadata only; tarballs live in the separate store, so\n // nothing is re-downloaded.\n const cacheFlag = metadataCacheDir\n ? ` --config.cacheDir=${metadataCacheDir}`\n : \"\";\n // `--ignore-workspace` when tempo/ owns its install but has no workspace\n // file of its own: without it pnpm walks up to an ancestor\n // pnpm-workspace.yaml that tempo/ is NOT a member of,\n // runs the update as importer `..`, and rewrites the CUSTOMER's\n // workspace-root lockfile and node_modules — resolving their entire\n // dependency graph, which fails outright in repos whose root install\n // needs custom steps (TEM-1490: 11x installs tempo/ with\n // `pnpm install --ignore-workspace`, and the bare managed update\n // escaped into their monorepo root and exited 1). When an ancestor\n // lockfile genuinely governs tempo/'s node_modules the flag must stay\n // absent — that lockfile is exactly what the upgrade has to rewrite.\n // A local pnpm-workspace.yaml already confines discovery to tempo/.\n // Ignoring it also drops its policies (including approved fresh SDK\n // versions), so pnpm can silently downgrade the SDK (TEM-2356).\n const workspaceFlag = ignorePnpmWorkspace ? \" --ignore-workspace\" : \"\";\n return {\n updateCommand: `pnpm update tempo-sdk --latest${workspaceFlag}${cacheFlag}`,\n remediationCommand: `pnpm update tempo-sdk --latest${workspaceFlag}${cacheFlag}`,\n };\n }\n case \"bun\":\n // `--no-cache` ignores the manifest cache — bun's equivalent of the\n // pnpm cacheDir swap above.\n return {\n updateCommand: \"bun update tempo-sdk --latest --no-cache\",\n remediationCommand: \"bun update tempo-sdk --latest --no-cache\",\n };\n case \"yarn\":\n // Neither yarn berry nor yarn classic exposes a metadata-cache bypass\n // for a single dependency. The post-upgrade version assertion in\n // `ensureTempoSdkLatestSpec` is what catches a stale resolution here.\n return yarnBerry\n ? {\n updateCommand: \"yarn up tempo-sdk\",\n remediationCommand: \"yarn up tempo-sdk\",\n }\n : {\n updateCommand: \"yarn upgrade tempo-sdk --latest\",\n remediationCommand: \"yarn upgrade tempo-sdk --latest\",\n };\n case \"npm\":\n // npm resolves a `latest` dist-tag spec on update, so the plain verb is\n // enough — but only if its cached packument is fresh. `--prefer-online`\n // forces the staleness check that makes `latest` mean latest.\n return {\n updateCommand: \"npm update tempo-sdk --prefer-online\",\n remediationCommand: \"npm install tempo-sdk@latest --prefer-online\",\n };\n }\n}\n\n/**\n * Resolves the upgrade plan for the `tempo-sdk` installed under `tempoRoot`.\n *\n * Precedence: an explicit `packageManager` field on `tempo/package.json`, then\n * `tempo/`'s own install marker, then the nearest ancestor install marker (the\n * workspace that actually owns `tempo/node_modules`), then npm — the only\n * manager guaranteed to exist alongside Node.\n */\nexport async function resolveTempoSdkUpgradePlan(\n tempoRoot: string,\n options: {\n /**\n * Empty directory to use as the package manager's registry-metadata cache\n * for this one upgrade, so a stale `latest` dist-tag cannot pin the SDK.\n * Only pnpm needs it (see buildCommands); omit to get the plain commands.\n */\n metadataCacheDir?: string;\n } = {},\n): Promise<TempoSdkUpgradePlan> {\n const resolvedRoot = path.resolve(tempoRoot);\n\n const declared = await readFile(\n path.join(resolvedRoot, \"package.json\"),\n \"utf-8\",\n )\n .then((raw) => {\n const parsed = JSON.parse(raw) as { packageManager?: unknown };\n return parsePackageManagerField(parsed.packageManager);\n })\n .catch(() => null);\n\n // The first directory carrying an install marker owns the install.\n // Walking past it would let an unrelated outer repo's lockfile decide.\n const owner = await findInstallOwner(resolvedRoot);\n\n const resolvedPm = declared ?? owner?.pm ?? \"npm\";\n const governedByAncestorWorkspace =\n owner !== null && owner.dir !== resolvedRoot;\n const hasOwnPnpmWorkspace =\n resolvedPm === \"pnpm\" &&\n (await fileExists(path.join(resolvedRoot, \"pnpm-workspace.yaml\")));\n return {\n packageManager: resolvedPm,\n cwd: resolvedRoot,\n governedByAncestorWorkspace,\n ...buildCommands(\n resolvedPm,\n owner?.yarnLockContents !== undefined &&\n isYarnBerryLockfile(owner.yarnLockContents),\n options.metadataCacheDir ?? null,\n !governedByAncestorWorkspace && !hasOwnPnpmWorkspace,\n ),\n };\n}\n\nasync function findInstallOwner(startDir: string): Promise<{\n dir: string;\n pm: PackageManager;\n yarnLockContents?: string;\n} | null> {\n for (let dir = startDir; ; dir = path.dirname(dir)) {\n for (const candidate of INSTALL_MARKERS) {\n const markerPath = path.join(dir, candidate.file);\n if (!(await fileExists(markerPath))) continue;\n return {\n dir,\n pm: candidate.pm,\n ...(candidate.pm === \"yarn\"\n ? {\n yarnLockContents: await readFile(markerPath, \"utf-8\").catch(\n () => \"\",\n ),\n }\n : {}),\n };\n }\n if (path.dirname(dir) === dir) return null;\n }\n}\n","import {\n lstat,\n mkdir,\n readFile,\n realpath,\n rm,\n symlink,\n writeFile,\n} from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const TEMPO_SDK_SOURCE_ENV = \"TEMPO_MONOREPO_SDK_SOURCE\";\nconst LOCAL_SDK_MARKER_FILE = \"local-tempo-sdk.json\";\ntype TempoSdkSourceMode = \"local-prebuilt\" | \"published\";\n\nexport type TempoSdkDevSourceResult =\n | { source: \"local\"; localSdkRoot: string }\n | { source: \"published\"; removedLocalLink: boolean };\n\ntype PrepareTempoSdkDevSourceOptions = {\n mode?: TempoSdkSourceMode;\n localSdkRoot?: string;\n discoveryStart?: string;\n};\n\nasync function realpathIfPresent(filePath: string): Promise<string | null> {\n try {\n return await realpath(filePath);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return null;\n }\n throw error;\n }\n}\n\nasync function isTempoSdkRoot(candidate: string): Promise<boolean> {\n try {\n const manifest = JSON.parse(\n await readFile(path.join(candidate, \"package.json\"), \"utf8\"),\n ) as { name?: unknown };\n return manifest.name === \"tempo-sdk\";\n } catch {\n return false;\n }\n}\n\nasync function discoverLocalSdkRoot(start: string): Promise<string | null> {\n for (let current = path.resolve(start); ; current = path.dirname(current)) {\n const candidate = path.join(current, \"tempo-modules\", \"tempo-sdk\");\n if (await isTempoSdkRoot(candidate)) return candidate;\n if (path.dirname(current) === current) return null;\n }\n}\n\nasync function assertLocalSdkPrebuilt(localSdkRoot: string): Promise<void> {\n const requiredOutputs = [\"dist/index.js\", \"dist/canvas/index.js\"];\n const missingOutputs = (\n await Promise.all(\n requiredOutputs.map(async (relativePath) => ({\n relativePath,\n present:\n (await realpathIfPresent(path.join(localSdkRoot, relativePath))) !==\n null,\n })),\n )\n )\n .filter(({ present }) => !present)\n .map(({ relativePath }) => relativePath);\n if (missingOutputs.length === 0) return;\n\n throw new Error(\n `Local prebuilt tempo-sdk is missing ${missingOutputs.join(\", \")} under ${localSdkRoot}. ` +\n \"Build tempo-sdk before starting the Canvas host.\",\n );\n}\n\n/**\n * Select the SDK source for one Canvas host.\n *\n * Tempo client development and E2E build the SDK once before launching\n * Electron, then set TEMPO_MONOREPO_SDK_SOURCE=local-prebuilt. Rebuilding the\n * shared package while the renderer is serving it can invalidate Vite's\n * loaded chunks and create a reload/restart loop. Local-prebuilt mode links\n * every project's tempo/node_modules/tempo-sdk to the client worktree package.\n * Published-mode clients remove only links previously recorded by this module\n * and let the normal package-manager-aware SDK updater restore the registry\n * package.\n */\nexport async function prepareTempoSdkDevSource(\n tempoRoot: string,\n options: PrepareTempoSdkDevSourceOptions = {},\n): Promise<TempoSdkDevSourceResult> {\n const configuredMode = process.env[TEMPO_SDK_SOURCE_ENV];\n if (configuredMode === \"local\") {\n throw new Error(\n `${TEMPO_SDK_SOURCE_ENV}=local is no longer supported because Canvas host startup must not rebuild the SDK used by the running renderer. ` +\n \"Build it before Electron starts and use local-prebuilt.\",\n );\n }\n const mode =\n options.mode ??\n (configuredMode === \"local-prebuilt\" ? configuredMode : \"published\");\n const installedSdkRoot = path.join(tempoRoot, \"node_modules\", \"tempo-sdk\");\n const markerPath = path.join(tempoRoot, \".tempo\", LOCAL_SDK_MARKER_FILE);\n\n if (mode === \"local-prebuilt\") {\n const localSdkRoot =\n options.localSdkRoot ??\n (await discoverLocalSdkRoot(options.discoveryStart ?? process.cwd()));\n if (!localSdkRoot || !(await isTempoSdkRoot(localSdkRoot))) {\n throw new Error(\n `Local tempo-sdk development is enabled via ${TEMPO_SDK_SOURCE_ENV}=local-prebuilt, ` +\n \"but tempo-modules/tempo-sdk could not be found from the Tempo client checkout.\",\n );\n }\n\n await assertLocalSdkPrebuilt(localSdkRoot);\n const [resolvedLocalSdk, resolvedInstalledSdk] = await Promise.all([\n realpath(localSdkRoot),\n realpathIfPresent(installedSdkRoot),\n ]);\n if (resolvedInstalledSdk !== resolvedLocalSdk) {\n await rm(installedSdkRoot, { force: true, recursive: true });\n await mkdir(path.dirname(installedSdkRoot), { recursive: true });\n await symlink(\n resolvedLocalSdk,\n installedSdkRoot,\n process.platform === \"win32\" ? \"junction\" : \"dir\",\n );\n }\n await mkdir(path.dirname(markerPath), { recursive: true });\n await writeFile(\n markerPath,\n `${JSON.stringify({ localSdkRoot: resolvedLocalSdk }, null, 2)}\\n`,\n );\n return { source: \"local\", localSdkRoot: resolvedLocalSdk };\n }\n\n const marker = await readFile(markerPath, \"utf8\")\n .then((raw) => JSON.parse(raw) as { localSdkRoot?: unknown })\n .catch(() => null);\n if (!marker) return { source: \"published\", removedLocalLink: false };\n\n let removedLocalLink = false;\n if (typeof marker.localSdkRoot === \"string\") {\n const [resolvedInstalledSdk, resolvedLocalSdk] = await Promise.all([\n realpathIfPresent(installedSdkRoot),\n realpathIfPresent(marker.localSdkRoot),\n ]);\n const installedStat = await lstat(installedSdkRoot).catch(() => null);\n if (\n resolvedInstalledSdk === resolvedLocalSdk ||\n (resolvedInstalledSdk === null && installedStat?.isSymbolicLink())\n ) {\n await rm(installedSdkRoot, { force: true, recursive: true });\n removedLocalLink = true;\n }\n }\n await rm(markerPath, { force: true });\n return { source: \"published\", removedLocalLink };\n}\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport interface DevServerStatusOnDisk {\n dependenciesInstalled: boolean;\n devserverUrl: string | null;\n}\n\n/**\n * Read the persisted devserver status from `.tempo/status.json`.\n *\n * This is a standalone function that doesn't require a TempoDevServer instance,\n * making it safe to use from stateless consumers like MCP tools.\n */\nexport async function readDevServerStatus(\n tempoRoot: string,\n): Promise<DevServerStatusOnDisk> {\n const statusPath = path.join(tempoRoot, \".tempo\", \"status.json\");\n let raw: string;\n try {\n raw = await readFile(statusPath, \"utf-8\");\n } catch {\n return { dependenciesInstalled: false, devserverUrl: null };\n }\n\n try {\n const parsed = JSON.parse(raw) as {\n dependenciesInstalled?: unknown;\n devserverUrl?: unknown;\n };\n return {\n dependenciesInstalled: parsed.dependenciesInstalled === true,\n devserverUrl:\n typeof parsed.devserverUrl === \"string\" ? parsed.devserverUrl : null,\n };\n } catch {\n return { dependenciesInstalled: false, devserverUrl: null };\n }\n}\n","import { access, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { scanCanvasFiles } from \"@tempo-modules/tempo-devserver\";\nimport {\n NO_CANVAS_DECLARATION_ERROR,\n parseCanvasFile,\n} from \"../../tempo-sdk/canvas/parser\";\n\nimport { projectRootForTempoDir } from \"./devserver\";\nimport type {\n CanvasEnumeration,\n EnumeratedStoryboard,\n TrackedEnumeration,\n TrackedStoryboard,\n} from \"./types\";\n\nfunction toPosixPath(value: string): string {\n return value.split(path.sep).join(\"/\");\n}\n\n/**\n * Enumerates every renderable component storyboard under the canvases root,\n * from the canvas AST (no devserver needed). Route and html storyboards are\n * counted but not returned — they need the user's app dev server rather\n * than the canvas host's direct-render URL.\n *\n * Error semantics (AST truth — the caller decides severity):\n * - A structurally unreadable canvas contributes no storyboards and is\n * reported in `structuralErrors` (its storyboards can't be trusted).\n * - Legacy page-format canvases (the pre-react default-export config shape)\n * parse cleanly but declare no `<Canvas>` and import nothing from\n * tempo-sdk/canvas — they have no storyboard JSX, so they are skipped as\n * empty, never errors.\n * - A malformed `vrt` declaration (dynamic expression, bad threshold,\n * broken ignore region) is reported in `vrtConfigErrors` and the file\n * contributes no storyboards: the author expressed tracking intent that\n * cannot be honored, and proceeding would drop or misconfigure it.\n */\nexport async function enumerateCanvasStoryboards(options: {\n /** The tempo dir (holding tempo.config.json), e.g. `<repo>/tempo`. */\n tempoDir: string;\n /** The canvases root. Default `<tempoDir>/designs`. */\n pagesDir?: string;\n}): Promise<CanvasEnumeration> {\n const pagesDir = options.pagesDir ?? path.join(options.tempoDir, \"designs\");\n // `hostCanvasPath` is the devserver's module key — relative to the root\n // TempoDevServer itself derives for this tempo dir.\n const projectRoot = projectRootForTempoDir(options.tempoDir);\n const canvasFiles = await scanCanvasFiles(pagesDir);\n\n const storyboards: EnumeratedStoryboard[] = [];\n const structuralErrors: CanvasEnumeration[\"structuralErrors\"] = [];\n const vrtConfigErrors: CanvasEnumeration[\"vrtConfigErrors\"] = [];\n let totalStoryboards = 0;\n\n for (const filePath of canvasFiles) {\n const source = await readFile(filePath, \"utf-8\");\n const parsed = parseCanvasFile(filePath, source);\n\n const structural = parsed.errors.filter(\n (error) => error.kind === \"structural\",\n );\n const isReactCanvas =\n parsed.imports.some(\n (imported) =>\n imported.source === \"tempo-sdk/canvas\" ||\n imported.source.endsWith(\"/tempo-sdk/canvas\"),\n ) || parsed.misimportedSdkCanvasImports.length > 0;\n const isLegacyPage =\n !isReactCanvas &&\n structural.length > 0 &&\n structural.every(\n (error) => error.message === NO_CANVAS_DECLARATION_ERROR,\n );\n if (isLegacyPage) continue;\n if (structural.length > 0) {\n structuralErrors.push(\n ...structural.map((error) => ({ filePath, message: error.message })),\n );\n continue;\n }\n\n const vrtErrors = parsed.errors.filter(\n (error) => error.kind === \"degraded\" && error.message.includes(\" vrt\"),\n );\n if (vrtErrors.length > 0) {\n vrtConfigErrors.push(\n ...vrtErrors.map((error) => ({ filePath, message: error.message })),\n );\n continue;\n }\n\n const canvasPath = toPosixPath(\n path.relative(options.tempoDir, path.dirname(filePath)),\n );\n const hostCanvasPath = toPosixPath(path.relative(projectRoot, filePath));\n\n for (const storyboard of parsed.storyboards) {\n totalStoryboards += 1;\n if (storyboard.kind !== \"component\") continue;\n storyboards.push({\n filePath,\n canvasPath,\n hostCanvasPath,\n storyboardId: storyboard.storyboardKey,\n name: storyboard.displayName,\n layout: {\n x: storyboard.layout.x,\n y: storyboard.layout.y,\n width: storyboard.layout.width,\n height: storyboard.layout.height,\n },\n vrt: storyboard.vrt,\n });\n }\n }\n\n return {\n storyboards,\n totalStoryboards,\n totalCanvases: canvasFiles.length,\n structuralErrors,\n vrtConfigErrors,\n };\n}\n\n/**\n * The tracked set (TEM-1775 §3.2) — the visual-review POLICY layered on\n * `enumerateCanvasStoryboards`, shared by the CI capture (`@tempo-ai/visual-\n * review`) and the setup doctor (`visual_review_doctor`) so both see exactly\n * the same manifest:\n * - every `<Storyboard>` under the canvases root carrying a literal `vrt`\n * prop is tracked, minus `skip: true` (paused);\n * - structural parse errors FAIL enumeration for that file (its storyboards\n * can't be trusted) and are returned for the caller to report loudly\n * rather than silently shrinking the tracked set;\n * - a malformed `vrt` declaration (dynamic expression, bad threshold, broken\n * ignore region) is fatal for the file: the author expressed tracking\n * intent, and silently proceeding would drop the board from the gate or\n * gate it with different settings than written;\n * - an ignore configuration masking ≥90% of the board is fatal — \"tracked\"\n * must not be a lie.\n * A missing tempo dir is an EMPTY manifest, not an error: an onboarding\n * PR's merge-base is exactly that commit.\n */\nexport async function enumerateTrackedStoryboards(options: {\n /** The tempo dir (holding tempo.config.json), e.g. `<repo>/tempo`. */\n tempoDir: string;\n /** The canvases root. Default `<tempoDir>/designs`. */\n pagesDir?: string;\n}): Promise<TrackedEnumeration> {\n try {\n await access(options.tempoDir);\n } catch {\n return {\n tracked: [],\n paused: [],\n totalStoryboards: 0,\n totalCanvases: 0,\n errors: [],\n };\n }\n const enumeration = await enumerateCanvasStoryboards(options);\n\n const tracked: TrackedStoryboard[] = [];\n const paused: TrackedStoryboard[] = [];\n const errors: TrackedEnumeration[\"errors\"] = [\n ...enumeration.structuralErrors,\n ...enumeration.vrtConfigErrors,\n ];\n\n for (const storyboard of enumeration.storyboards) {\n if (!storyboard.vrt) continue;\n const board: TrackedStoryboard = {\n canvasPath: storyboard.canvasPath,\n hostCanvasPath: storyboard.hostCanvasPath,\n storyboardId: storyboard.storyboardId,\n name: storyboard.name,\n layout: storyboard.layout,\n threshold: storyboard.vrt.threshold,\n ignore: storyboard.vrt.ignore,\n };\n const maskedArea = (storyboard.vrt.ignore ?? []).reduce(\n (total, region) =>\n total +\n Math.max(0, Math.min(region.w, board.layout.width)) *\n Math.max(0, Math.min(region.h, board.layout.height)),\n 0,\n );\n if (maskedArea >= 0.9 * board.layout.width * board.layout.height) {\n errors.push({\n filePath: storyboard.filePath,\n message: `<Storyboard id=\"${storyboard.storyboardId}\"> vrt.ignore masks ≥90% of the board — untrack it instead`,\n });\n continue;\n }\n if (storyboard.vrt.skip) {\n paused.push(board);\n continue;\n }\n tracked.push(board);\n }\n\n return {\n tracked,\n paused,\n totalStoryboards: enumeration.totalStoryboards,\n totalCanvases: enumeration.totalCanvases,\n errors,\n };\n}\n","/**\n * The timing harness (TEM-1775 §3.2a): PR2's acceptance artifact is a\n * per-phase report with p50/p95 per storyboard, validating the pipeline\n * budget before the workflow ships blocking.\n */\n\nexport type PhaseTimings = Record<string, number>;\n\nexport function percentile(sortedValues: number[], p: number): number {\n if (sortedValues.length === 0) return 0;\n const index = Math.min(\n sortedValues.length - 1,\n Math.ceil((p / 100) * sortedValues.length) - 1,\n );\n return sortedValues[Math.max(0, index)]!;\n}\n\nexport function summarize(values: number[]): {\n count: number;\n totalMs: number;\n p50Ms: number;\n p95Ms: number;\n maxMs: number;\n} {\n const sorted = [...values].sort((a, b) => a - b);\n return {\n count: sorted.length,\n totalMs: sorted.reduce((total, value) => total + value, 0),\n p50Ms: percentile(sorted, 50),\n p95Ms: percentile(sorted, 95),\n maxMs: sorted[sorted.length - 1] ?? 0,\n };\n}\n\nexport class TimingReport {\n private readonly phases: Array<{ name: string; ms: number }> = [];\n private readonly perBoard: Record<\n string,\n { unit: \"ms\" | \"count\"; values: number[] }\n > = {};\n\n async phase<T>(name: string, work: () => Promise<T>): Promise<T> {\n const start = Date.now();\n try {\n return await work();\n } finally {\n this.phases.push({ name, ms: Date.now() - start });\n }\n }\n\n recordBoard(\n metric: string,\n value: number,\n unit: \"ms\" | \"count\" = \"ms\",\n ): void {\n const series = (this.perBoard[metric] ??= { unit, values: [] });\n if (series.unit !== unit) {\n throw new Error(\n `Timing metric ${JSON.stringify(metric)} changed unit from ${series.unit} to ${unit}`,\n );\n }\n series.values.push(value);\n }\n\n toJSON() {\n return {\n phases: this.phases,\n perBoard: Object.fromEntries(\n Object.entries(this.perBoard).map(([metric, { unit, values }]) => [\n metric,\n { ...summarize(values), unit },\n ]),\n ),\n };\n }\n\n /** The human-readable acceptance table. */\n format(): string {\n const lines: string[] = [\"── timing ──────────────────────────────\"];\n for (const { name, ms } of this.phases) {\n lines.push(`${name.padEnd(28)} ${(ms / 1000).toFixed(1)}s`);\n }\n const perBoard = Object.entries(this.perBoard);\n if (perBoard.length > 0) {\n lines.push(\"── per storyboard ──────────────────────\");\n lines.push(\n `${\"metric\".padEnd(28)}${\"count\".padStart(6)}${\"total\".padStart(10)}${\"p50\".padStart(8)}${\"p95\".padStart(8)}${\"max\".padStart(8)}`,\n );\n for (const [metric, { unit, values }] of perBoard) {\n const summary = summarize(values);\n const total =\n unit === \"ms\"\n ? `${(summary.totalMs / 1000).toFixed(1)}s`\n : String(summary.totalMs);\n const formatValue = (value: number) =>\n unit === \"ms\" ? `${Math.round(value)}ms` : String(Math.round(value));\n lines.push(\n `${metric.padEnd(28)}${String(summary.count).padStart(6)}${total.padStart(10)}${formatValue(summary.p50Ms).padStart(8)}${formatValue(summary.p95Ms).padStart(8)}${formatValue(summary.maxMs).padStart(8)}`,\n );\n }\n }\n return lines.join(\"\\n\");\n }\n}\n","import { enumerateTrackedStoryboards as enumerateTrackedStoryboardsHeadless } from \"@tempo-modules/canvas-capture-headless\";\n\nimport type { EnumerationResult } from \"./types\";\n\n/**\n * Enumerates the tracked set (TEM-1775 §3.2): every `<Storyboard>` under the\n * canvases root carrying a literal `vrt` prop, minus `skip: true`. Route and\n * html storyboards are out of scope in v1 — the parser only attaches `vrt`\n * to component storyboards, so they can never enter the manifest.\n *\n * The AST walk AND the vrt policy (structural errors and malformed `vrt`\n * declarations are fatal for the file; ≥90%-masked boards are rejected; a\n * missing `tempo/` dir is an empty manifest) live in\n * `@tempo-modules/canvas-capture-headless`, shared with the setup doctor\n * (`visual_review_doctor`) so the doctor's \"tracked\" count is exactly what\n * this capture will honor. This module keeps the CLI's type names.\n */\nexport async function enumerateTrackedStoryboards(options: {\n /** The tempo dir (holding tempo.config.json), e.g. `<repo>/tempo`. */\n tempoDir: string;\n /** The canvases root, e.g. `<repo>/tempo/designs`. */\n pagesDir: string;\n}): Promise<EnumerationResult> {\n return await enumerateTrackedStoryboardsHeadless(options);\n}\n","import { DEVICE_SCALE_FACTOR } from \"@tempo-modules/canvas-capture-headless\";\n\nimport type { VrtIgnoreRegion } from \"./types\";\n\n/**\n * Authors declare `vrt.ignore` regions in storyboard-local CSS px; the\n * compare engine masks the DEVICE-pixel snapshot. The run manifest carries\n * device px — this is the single conversion point.\n */\nexport function scaleIgnoreRegionsToDevicePx(\n regions: VrtIgnoreRegion[] | undefined,\n): VrtIgnoreRegion[] | undefined {\n return regions?.map((region) => ({\n x: region.x * DEVICE_SCALE_FACTOR,\n y: region.y * DEVICE_SCALE_FACTOR,\n w: region.w * DEVICE_SCALE_FACTOR,\n h: region.h * DEVICE_SCALE_FACTOR,\n }));\n}\n","import type { RunManifest } from \"./types\";\n\n/** A 404 from the ingest surface almost always means the deployment simply\n * doesn't have the /visual-review/* routes yet (they ship with the next\n * Convex release deploy) — say so instead of leaving a bare status code. */\nfunction routeMissingHint(status: number): string {\n return status === 404\n ? \" — the target deployment has no /visual-review routes yet (they arrive with the next release's Convex deploy); expected-red until then\"\n : \"\";\n}\n\n/**\n * The producer credential. In production (and any customer repo) this is a\n * GitHub Actions OIDC token minted with `audience: tempo-visual-review`; the\n * global shared secret is accepted only by non-prod deployments (previews,\n * e2e, local capture). See the Convex `visualReviewAuthenticate` gate.\n */\nexport type IngestCredential =\n | { kind: \"oidc\"; value: string }\n | { kind: \"secret\"; value: string };\n\n/**\n * A credential source. GitHub OIDC id-tokens expire 5 minutes after minting,\n * and a capture run spans many minutes between its first authenticated call\n * (the mid-capture baseline fetch) and its last (finalize) — so the client\n * resolves the credential PER REQUEST from a provider that can re-mint,\n * instead of pinning one token at construction.\n */\nexport type IngestCredentialProvider = () => Promise<IngestCredential>;\n\nexport type BaselineClaimResult =\n | { status: \"unsupported\"; reason?: string }\n | { status: \"ready\"; commitSha: string }\n | {\n status: \"capturing\";\n commitSha: string;\n leaseExpiresAt: number;\n }\n | { status: \"claimed\"; commitSha: string; leaseExpiresAt: number };\n\nexport type BaselineClaimIdentity = {\n repoKey: string;\n commitSha: string;\n envFingerprint: string;\n ownerToken: string;\n};\n\nexport class VisualReviewHttpError extends Error {\n readonly status: number;\n\n constructor(message: string, status: number) {\n super(message);\n this.name = \"VisualReviewHttpError\";\n this.status = status;\n }\n}\n\n/**\n * Client for the Convex `/visual-review/*` ingest routes (TEM-1775 §4.3):\n * hash-negotiated, idempotent, resumable. A retried CI job re-uploads\n * nothing (negotiate reports every hash present) and re-finalizes safely\n * (the mutation dedups on repo+sha+env+trigger+branch).\n */\nexport class VisualReviewIngestClient {\n /** Convex site origin, e.g. https://dapper-heron-945.convex.site */\n private readonly baseUrl: string;\n private readonly credential: IngestCredentialProvider;\n private lastCredentialKind: IngestCredential[\"kind\"] = \"secret\";\n\n constructor(\n baseUrl: string,\n credential: IngestCredential | IngestCredentialProvider,\n ) {\n this.baseUrl = baseUrl;\n this.credential =\n typeof credential === \"function\" ? credential : async () => credential;\n }\n\n private async authHeader(): Promise<Record<string, string>> {\n const credential = await this.credential();\n this.lastCredentialKind = credential.kind;\n return credential.kind === \"oidc\"\n ? { \"x-visual-review-oidc\": credential.value }\n : { \"x-visual-review-secret\": credential.value };\n }\n\n private async request(path: string, init: RequestInit): Promise<Response> {\n const response = await fetch(`${this.baseUrl}${path}`, {\n ...init,\n headers: {\n ...(await this.authHeader()),\n ...(init.headers ?? {}),\n },\n });\n if (response.status === 503) {\n // Only an explicit configuration error calls for credentials. Preserve\n // GitHub authorization failures and other upstream outages in CI logs.\n const reason = await this.errorReason(response);\n if (reason === \"jwks_unavailable\") {\n throw new VisualReviewHttpError(\n \"visual-review ingest could not verify the OIDC token: GitHub's JWKS endpoint is unreachable from the deployment (transient — retry the job)\",\n response.status,\n );\n }\n if (reason === \"visual_review_unconfigured\") {\n throw new VisualReviewHttpError(\n \"visual-review ingest is unconfigured on the target deployment (set VISUAL_REVIEW_UPLOAD_SECRET, or its OIDC producer credential)\",\n response.status,\n );\n }\n throw new VisualReviewHttpError(\n `visual-review ingest unavailable: ${reason ?? \"HTTP 503\"}`,\n response.status,\n );\n }\n if (response.status === 401) {\n throw new VisualReviewHttpError(\n this.lastCredentialKind === \"oidc\"\n ? \"visual-review ingest OIDC token rejected (unregistered repo, wrong audience, or expired)\"\n : \"visual-review ingest secret rejected (note: production rejects the shared secret — CI must send an OIDC token)\",\n response.status,\n );\n }\n return response;\n }\n\n /** Best-effort read of the typed `reason`/`error` from an error body. */\n private async errorReason(response: Response): Promise<string | undefined> {\n try {\n const body = (await response.json()) as {\n reason?: string;\n error?: string;\n };\n return body.reason ?? body.error;\n } catch {\n return undefined;\n }\n }\n\n /** → the subset of `hashes` the store lacks. */\n async negotiate(hashes: string[]): Promise<string[]> {\n if (hashes.length === 0) return [];\n const response = await this.request(\"/visual-review/negotiate\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ hashes }),\n });\n if (!response.ok) {\n throw new Error(\n `negotiate failed: ${response.status}${routeMissingHint(response.status)}`,\n );\n }\n const { missing } = (await response.json()) as { missing: string[] };\n return missing;\n }\n\n async uploadBlob(\n hash: string,\n bytes: Uint8Array,\n size: { width: number; height: number },\n ): Promise<void> {\n const response = await this.request(\n `/visual-review/blob?hash=${hash}&w=${size.width}&h=${size.height}`,\n { method: \"POST\", body: bytes as unknown as BodyInit },\n );\n if (!response.ok) {\n throw new Error(\n `blob upload failed for ${hash.slice(0, 12)}: ${response.status} ${await response.text()}`,\n );\n }\n }\n\n async claimBaseline(input: {\n repoKey: string;\n commitSha: string;\n envFingerprint: string;\n ownerToken: string;\n }): Promise<BaselineClaimResult> {\n try {\n const response = await this.request(\"/visual-review/baseline/claim\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(input),\n });\n if (response.status === 404) return { status: \"unsupported\" };\n if (response.status === 429 || response.status >= 500) {\n return {\n status: \"unsupported\",\n reason: `claim service returned ${response.status}`,\n };\n }\n if (!response.ok) {\n throw new VisualReviewHttpError(\n `baseline claim failed: ${response.status} ${await response.text()}${routeMissingHint(response.status)}`,\n response.status,\n );\n }\n return (await response.json()) as BaselineClaimResult;\n } catch (error) {\n if (\n error instanceof VisualReviewHttpError &&\n error.status >= 400 &&\n error.status < 500 &&\n error.status !== 429\n ) {\n throw error;\n }\n return {\n status: \"unsupported\",\n reason: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n async renewBaselineClaim(\n input: BaselineClaimIdentity,\n ): Promise<{ renewed: true; leaseExpiresAt: number }> {\n const response = await this.request(\"/visual-review/baseline/heartbeat\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(input),\n });\n if (!response.ok) {\n throw new VisualReviewHttpError(\n `baseline claim heartbeat failed: ${response.status} ${await response.text()}`,\n response.status,\n );\n }\n return (await response.json()) as {\n renewed: true;\n leaseExpiresAt: number;\n };\n }\n\n async failBaselineClaim(\n input: Omit<BaselineClaimIdentity, \"envFingerprint\"> & {\n envFingerprint?: string;\n error: string;\n },\n ): Promise<{ released: boolean }> {\n const response = await this.request(\"/visual-review/baseline/fail\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(input),\n });\n if (!response.ok) {\n throw new Error(\n `baseline claim release failed: ${response.status} ${await response.text()}`,\n );\n }\n return (await response.json()) as { released: boolean };\n }\n\n async baselineManifest(\n repoKey: string,\n commitSha: string,\n envFingerprint: string,\n ): Promise<Pick<RunManifest, \"expected\" | \"snapshots\">> {\n const params = new URLSearchParams({\n repo: repoKey,\n sha: commitSha,\n env: envFingerprint,\n });\n const response = await this.request(\n `/visual-review/baseline/manifest?${params}`,\n { method: \"GET\" },\n );\n if (!response.ok) {\n throw new Error(\n `baseline manifest fetch failed: ${response.status} ${await response.text()}`,\n );\n }\n return (await response.json()) as Pick<\n RunManifest,\n \"expected\" | \"snapshots\"\n >;\n }\n\n async finalize(manifest: RunManifest): Promise<{\n runId: string;\n reportKey: string;\n state: string;\n deduped: boolean;\n reportUrl: string;\n statusUrl: string;\n }> {\n const response = await this.request(\"/visual-review/finalize\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n repoKey: manifest.repoKey,\n githubRepo: manifest.githubRepo,\n headSha: manifest.headSha,\n branch: manifest.branch,\n trigger: manifest.trigger,\n prNumber: manifest.prNumber,\n baseCandidates: manifest.baseCandidates,\n envFingerprint: manifest.envFingerprint,\n expected: manifest.expected,\n snapshots: manifest.snapshots,\n baselineClaimToken: manifest.baselineClaimToken,\n }),\n });\n if (!response.ok) {\n throw new Error(\n `finalize failed: ${response.status} ${await response.text()}${routeMissingHint(response.status)}`,\n );\n }\n return (await response.json()) as any;\n }\n}\n"],"mappings":"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;;ACAjD,IAAM,qCAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["spawn","randomUUID","mkdir","mkdtemp","rm","writeFile","tmpdir","path","require","resolve","baseHref","buildDirectRenderUrl","window","sleep","resolve","key","PNG","readFile","path","spawn","resolve","mkdir","rename","writeFile","execFile","spawn","path","promisify","sleep","resolve","spawn","path","key","path","resolve","spawn","path","stdout","dirname","maxBytesPerFile","mkdir","rename","rm","writeFile","path","resolve","ANSI_ESCAPE_RE","ANSI_ESCAPE_RE","path","spawn","cleanupError","finalError","mkdir","writeFile","rename","sleep","resolve","key","execFileAsync","promisify","execFile","stdout","path","createHash","readFile","path","spawn","createHash","randomUUID","access","mkdir","readdir","readFile","rm","stat","writeFile","os","path","lstat","stat","createRequire","join","relative","readFile","writeFile","join","path","createRequire","resolve","join","lstat","stat","symlink","mkdir","readdir","readFile","rm","path","createRequire","path","path","key","path","require","createRequire","parseSync","DEFAULT_LAYOUT","getObjectProperty","readNumberLiteral","readLayout","fallbackPageName","path","readFile","atomicWriteText","readdir","resolve","rm","mkdir","readFile","path","readFile","path","access","readFile","readdir","path","access","readFile","path","fileExists","parsePackageManagerField","lstat","mkdir","readFile","realpath","rm","writeFile","path","path","resolve","extractServerUrl","path","mkdir","atomicWriteText","process","pathExists","readFile","access","writeFile","randomUUID","rm","readdir","stat","cacheStat","resolve","markedOurs","createHash","key","spawn","path","key","access","os","resolve","rm","signal","mkdir","atomicWriteText","writeFile","spawn","spawn","path","os","rm","readFile","writeFile","extractServerUrl","resolve","resolve","sleep","resolve","sleep","readFile","path","readFile","path","tempoDir","resolve","access","readFile","path","toPosixPath","path","readFile","access","enumerateTrackedStoryboards","path","path","resolve","relative","mkdtemp","tmpdir","spawn","rm","enumerateTrackedStoryboards","randomUUID","mkdir","writeFile"]}
|