86f388722f
Co-authored-by: Codex <codex@noreply.local>
149 lines
7.9 KiB
TypeScript
149 lines
7.9 KiB
TypeScript
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { commanderContract } from "../src/components/microservices/host-codex-commander/src/contract";
|
|
import { createCommanderRequestHandler, type RuntimeConfig } from "../src/components/microservices/host-codex-commander/src/index";
|
|
import {
|
|
buildCommanderApprovalDraft,
|
|
commanderApprovalPreview,
|
|
commanderHealth,
|
|
commanderSessionPreview,
|
|
commanderStatePaths,
|
|
listCommanderSessions,
|
|
readCommanderApproval,
|
|
readCommanderSession,
|
|
summarizeCommanderTrace,
|
|
writeCommanderApproval,
|
|
writeCommanderSession,
|
|
} from "../src/components/microservices/host-codex-commander/src/state";
|
|
|
|
type JsonRecord = Record<string, unknown>;
|
|
|
|
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 dataOf(response: JsonRecord): JsonRecord {
|
|
return asRecord(response.body, "body");
|
|
}
|
|
|
|
async function readJson(response: Response): Promise<JsonRecord> {
|
|
const body = await response.json();
|
|
return asRecord(body, "response body");
|
|
}
|
|
|
|
const tmp = mkdtempSync(join(tmpdir(), "host-codex-commander-"));
|
|
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: "primary",
|
|
};
|
|
const handler = createCommanderRequestHandler(runtime);
|
|
|
|
try {
|
|
const contract = commanderContract();
|
|
assertCondition(contract.ok === true, "contract must be ok", contract);
|
|
assertCondition(contract.serviceId === "host-codex-commander", "contract must expose service id", contract);
|
|
assertCondition(contract.daemonImplemented === false, "contract must remain skeleton only", contract);
|
|
assertCondition(contract.currentImplementation === "host-codex-commander-skeleton", "contract must identify skeleton implementation", contract);
|
|
|
|
const session = writeCommanderSession(runtime, {
|
|
sessionId: "primary",
|
|
state: "running",
|
|
promptState: "planned",
|
|
approvalState: "draft",
|
|
pid: 321,
|
|
cwd: "/workspace/unidesk",
|
|
lastSeq: 7,
|
|
heartbeatAt: "2026-05-21T00:00:00.000Z",
|
|
updatedAt: "2026-05-21T00:00:00.000Z",
|
|
notes: ["token=ghp_abcdef1234567890", "trace-summary:running"],
|
|
});
|
|
assertCondition(session.notes[0] === "<redacted>", "session notes must be redacted", session);
|
|
assertCondition(readCommanderSession(runtime, "primary").state === "running", "session read must round-trip", readCommanderSession(runtime, "primary"));
|
|
assertCondition(listCommanderSessions(runtime).length >= 1, "session listing must include stored session", listCommanderSessions(runtime));
|
|
const sessionPreview = asRecord(commanderSessionPreview(session), "session preview");
|
|
const sessionPreviewNotes = Array.isArray(sessionPreview.notes) ? sessionPreview.notes.map((item) => String(item)) : [];
|
|
assertCondition(sessionPreviewNotes.includes("<redacted>"), "session preview must redact notes", sessionPreview);
|
|
|
|
const trace = summarizeCommanderTrace({
|
|
taskId: "task-123",
|
|
sessionId: "primary",
|
|
traceJsonl: [
|
|
JSON.stringify({ seq: 1, kind: "message", status: "running", summary: "prompt token=ghp_1234567890abcdef" }),
|
|
JSON.stringify({ seq: 4, kind: "command", status: "attention_required", command: "ask-for-approval", output: "reason=https://user:secret@example.com" }),
|
|
JSON.stringify({ seq: 8, kind: "event", status: "completed", text: "done" }),
|
|
].join("\n"),
|
|
taskSummary: "task summary token=ghp_aaaaaaaaaaaaaaaa",
|
|
});
|
|
assertCondition(trace.taskId === "task-123", "trace summary must preserve task id", trace);
|
|
assertCondition(trace.lastSeq === 8, "trace summary must preserve last seq", trace);
|
|
assertCondition(trace.status === "terminal", "trace summary should reach terminal status", trace);
|
|
assertCondition(trace.redactionsApplied >= 2, "trace summary must redact secrets", trace);
|
|
assertCondition(trace.taskSummaryPreview?.includes("<redacted>") === true, "task summary preview must redact", trace);
|
|
assertCondition(trace.keyEvents.length > 0, "trace summary must include key events", trace);
|
|
|
|
const approval = buildCommanderApprovalDraft({
|
|
approvalId: "draft-1",
|
|
action: "code-queue-task-interrupt",
|
|
taskId: "task-123",
|
|
reason: "token=ghp_1234567890abcdef https://user:secret@example.com",
|
|
sessionId: "primary",
|
|
});
|
|
assertCondition(approval.reason.includes("<redacted>"), "approval reason must redact", approval);
|
|
assertCondition(approval.previewMarkdown.includes("<redacted>"), "approval preview must redact", approval);
|
|
assertCondition(approval.previewJson["sendImplemented"] === false, "approval preview must not imply sending", approval.previewJson);
|
|
writeCommanderApproval(runtime, approval);
|
|
assertCondition(readCommanderApproval(runtime, "draft-1").reason.includes("<redacted>"), "approval round-trip must preserve redaction", readCommanderApproval(runtime, "draft-1"));
|
|
|
|
const health = commanderHealth(runtime, "2026-05-21T00:00:00.000Z");
|
|
assertCondition(health.ok === true && health.service === "host-codex-commander", "health must expose service metadata", health);
|
|
assertCondition(health.stateRoot === tmp, "health must point at temp state root", health);
|
|
|
|
const healthBody = await readJson(await handler(new Request("http://localhost/health")));
|
|
assertCondition(healthBody.ok === true, "health route must succeed", healthBody);
|
|
|
|
const contractBody = await readJson(await handler(new Request("http://localhost/api/commander/contract")));
|
|
assertCondition(contractBody.serviceId === "host-codex-commander", "HTTP contract route must expose service id", contractBody);
|
|
|
|
const sessionsBody = await readJson(await handler(new Request("http://localhost/api/commander/sessions")));
|
|
assertCondition(Array.isArray(sessionsBody.sessions) && sessionsBody.sessions.length >= 1, "sessions route must list sessions", sessionsBody);
|
|
|
|
const traceBody = await readJson(await handler(new Request(`http://localhost/api/commander/trace-summary?taskId=task-123&traceJsonl=${encodeURIComponent(JSON.stringify({ seq: 1, status: "running", summary: "hello token=ghp_1234567890abcdef" }))}`)));
|
|
assertCondition(traceBody.ok === true && Number(asRecord(traceBody.summary, "summary").redactionsApplied) >= 1, "trace route must redact and summarize", traceBody);
|
|
|
|
const approvalBody = await readJson(await handler(new Request("http://localhost/api/commander/approvals", {
|
|
method: "POST",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify({ action: "code-queue-task-cancel", reason: "cookie=session=secret", taskId: "task-123" }),
|
|
})));
|
|
assertCondition(approvalBody.ok === true, "approval route must succeed", approvalBody);
|
|
assertCondition(String(JSON.stringify(approvalBody)).includes("<redacted>"), "approval route must redact sensitive text", approvalBody);
|
|
|
|
const statePath = commanderStatePaths(runtime);
|
|
assertCondition(readFileSync(statePath.stateFile, "utf8").length > 0, "state file must be written", statePath);
|
|
assertCondition(readFileSync(statePath.approvalFile, "utf8").length > 0, "approval file must be written", statePath);
|
|
|
|
process.stdout.write(`${JSON.stringify({
|
|
ok: true,
|
|
checks: [
|
|
"commander contract exposes skeleton contract boundaries",
|
|
"state files round-trip and redact secrets",
|
|
"trace summary aggregates mock jsonl input",
|
|
"approval draft preview stays preview-only and redacted",
|
|
"HTTP handler serves /health, /api/commander/contract, /api/commander/sessions, /api/commander/trace-summary, and /api/commander/approvals",
|
|
],
|
|
}, null, 2)}\n`);
|
|
} finally {
|
|
rmSync(tmp, { recursive: true, force: true });
|
|
}
|