import { spawnSync } from "node:child_process"; import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { createCommanderRequestHandler, type RuntimeConfig } from "../src/components/microservices/host-codex-commander/src/index"; import { commanderHealth, summarizeCommanderTrace } from "../src/components/microservices/host-codex-commander/src/state"; type JsonRecord = Record; function assertCondition(condition: unknown, message: string, detail: unknown = {}): void { if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`); } function asRecord(value: unknown, label: string): JsonRecord { assertCondition(typeof value === "object" && value !== null && !Array.isArray(value), `${label} must be an object`, value); return value as JsonRecord; } function asRecordArray(value: unknown, label: string): JsonRecord[] { assertCondition(Array.isArray(value) && value.every((item) => typeof item === "object" && item !== null && !Array.isArray(item)), `${label} must be object array`, value); return value as JsonRecord[]; } function asStringArray(value: unknown, label: string): string[] { assertCondition(Array.isArray(value) && value.every((item) => typeof item === "string"), `${label} must be string array`, value); return value as string[]; } function runCli(args: string[], expectStatus: number): JsonRecord { const result = spawnSync("bun", ["scripts/cli.ts", ...args], { cwd: process.cwd(), encoding: "utf8", maxBuffer: 4 * 1024 * 1024, }); assertCondition(result.status === expectStatus, `status mismatch for ${args.join(" ")}`, { status: result.status, stdout: result.stdout.slice(-2000), stderr: result.stderr.slice(-2000), }); assertCondition(result.stdout.trim().length > 0, `command produced no stdout: ${args.join(" ")}`); return asRecord(JSON.parse(result.stdout) as unknown, "cli envelope"); } function dataOf(envelope: JsonRecord): JsonRecord { return asRecord(envelope.data, "data"); } async function readJson(response: Response): Promise { return asRecord(await response.json() as unknown, "response body"); } const sessionId = `no-daemon-smoke-contract-${process.pid}`; const liveSessionPath = join(process.cwd(), ".state", "commander", "sessions", `${sessionId}.json`); assertCondition(!existsSync(liveSessionPath), "precondition failed: smoke session path should not already exist", liveSessionPath); const smoke = dataOf(runCli(["commander", "smoke", "--dry-run", "--session-id", sessionId], 0)); assertCondition(smoke.ok === true, "smoke must succeed", smoke); assertCondition(smoke.phase === "source-contract", "smoke must remain source-contract phase", smoke); assertCondition(smoke.mode === "dry-run", "smoke must report dry-run mode", smoke); assertCondition(smoke.mutation === false, "smoke must be non-mutating", smoke); const noDaemon = asRecord(smoke.noDaemonSmokeContract, "noDaemonSmokeContract"); for (const flag of [ "startsDaemon", "startsPtyBridge", "startsStdioBridge", "opensSshBridge", "sendsClaudeqq", "restartsServices", "interruptsTasks", "cancelsTasks", "deploys", "runsFullCheckOrE2e", ]) { assertCondition(noDaemon[flag] === false, `${flag} must be false`, noDaemon); } assertCondition(asStringArray(noDaemon.allowedCommands, "allowedCommands").includes("bun scripts/host-codex-commander-no-daemon-smoke-contract-test.ts"), "smoke should name this lightweight contract", noDaemon); const validationPlan = asRecordArray(smoke.validationPlan, "validationPlan"); const surfaces = validationPlan.map((item) => item.surface); for (const expected of [ "health endpoint", "state file", "trace summary dry-run", "approval draft preview", "SSH bridge boundary", ]) { assertCondition(surfaces.includes(expected), `missing validation surface ${expected}`, surfaces); } for (const item of validationPlan) { assertCondition(asStringArray(item.expectedEvidence, "expectedEvidence").length > 0, "each validation item must define evidence", item); assertCondition(asStringArray(item.noRuntimeSideEffects, "noRuntimeSideEffects").length > 0, "each validation item must define no-side-effect boundary", item); } const smokeWithoutDryRun = dataOf(runCli(["commander", "smoke", "--session-id", sessionId], 1)); assertCondition(smokeWithoutDryRun.error === "dry-run-required", "smoke must require --dry-run", smokeWithoutDryRun); assertCondition(!existsSync(liveSessionPath), "smoke CLI must not write live commander state", liveSessionPath); const tmp = mkdtempSync(join(tmpdir(), "host-codex-commander-smoke-")); try { const runtime: RuntimeConfig = { rootDir: tmp, host: "127.0.0.1", port: 4261, logFile: join(tmp, "logs", "commander.jsonl"), serviceId: "host-codex-commander", stateRoot: tmp, sessionId, }; const health = commanderHealth(runtime, "2026-05-21T00:00:00.000Z"); assertCondition(health.ok === true && health.service === "host-codex-commander", "health helper must expose service metadata", health); assertCondition(health.stateRoot === tmp, "health helper must use temp state root", health); const handler = createCommanderRequestHandler(runtime); const healthBody = await readJson(await handler(new Request("http://localhost/health"))); assertCondition(healthBody.ok === true, "short-lived handler health route must succeed without Bun.serve", healthBody); const trace = summarizeCommanderTrace({ taskId: "task-smoke", sessionId, traceJsonl: [ JSON.stringify({ seq: 1, kind: "message", status: "running", summary: "checking token=ghp_1234567890abcdef" }), JSON.stringify({ seq: 2, kind: "event", status: "attention_required", text: "needs approval" }), ].join("\n"), taskSummary: "summary password=secret", }); assertCondition(trace.taskId === "task-smoke", "trace summary must preserve task id", trace); assertCondition(trace.sessionId === sessionId, "trace summary must preserve session id", trace); assertCondition(trace.lastSeq === 2, "trace summary must compute last seq", trace); assertCondition(trace.status === "attention_required", "trace summary must derive attention_required status", trace); assertCondition(trace.redactionsApplied >= 2, "trace summary must redact mock secrets", trace); } finally { rmSync(tmp, { recursive: true, force: true }); } const approval = dataOf(runCli([ "commander", "approval", "request", "--action", "code-queue-task-cancel", "--reason", "token=ghp_1234567890abcdef", "--dry-run", ], 0)); const claudeqq = asRecord(approval.claudeqq, "claudeqq"); assertCondition(claudeqq.mutation === false, "approval preview must not mutate ClaudeQQ", claudeqq); assertCondition(claudeqq.sendImplemented === false, "approval preview must not implement sending", claudeqq); assertCondition(!JSON.stringify(approval).includes("ghp_1234567890abcdef"), "approval preview must redact secret-like reason", approval); const doc = readFileSync("docs/reference/host-codex-commander.md", "utf8"); for (const snippet of [ "commander smoke --dry-run", "无 daemon smoke contract", "health endpoint", "SSH bridge boundary", ]) { assertCondition(doc.includes(snippet), `reference doc missing snippet: ${snippet}`); } process.stdout.write(`${JSON.stringify({ ok: true, checks: [ "commander smoke --dry-run is non-mutating and dry-run required", "no-daemon smoke contract forbids daemon, SSH/PTY/stdio bridge, ClaudeQQ send, restart, interrupt, cancel, deploy, and full e2e", "health endpoint and trace summary are validated through short-lived source-level helpers", "approval draft preview remains sendImplemented=false and redacted", "reference doc describes the dev validation surfaces and no-daemon boundary", ], }, null, 2)}\n`);