Files
pikasTech-HWLAB/web/hwlab-cloud-web/scripts/caserun-native-fixtures.ts
T

162 lines
6.1 KiB
TypeScript

import { createHash } from "node:crypto";
type NativeStatus = "queued" | "running" | "completed" | "failed" | "blocked" | "canceled";
interface NativeStep {
status: NativeStatus;
stage: string;
terminal: boolean;
blocker?: { code: string; summary: string; layer: string };
}
interface NativeRunState {
runId: string;
caseId: string;
cursor: number;
steps: NativeStep[];
}
const MODE = "native-test";
const CONTRACT_VERSION = "caserun.v1";
const BASE_TIME = Date.parse("2026-07-17T00:00:00.000Z");
const scenarios: Record<string, { title: string; steps: NativeStep[] }> = {
"native-queued": { title: "Native queued", steps: [{ status: "queued", stage: "queued", terminal: false }] },
"native-running": { title: "Native running", steps: [{ status: "queued", stage: "queued", terminal: false }, { status: "running", stage: "agent-running", terminal: false }] },
"native-completed": { title: "Native completed", steps: [{ status: "queued", stage: "queued", terminal: false }, { status: "running", stage: "agent-running", terminal: false }, { status: "completed", stage: "completed", terminal: true }] },
"native-failed": { title: "Native failed", steps: [{ status: "queued", stage: "queued", terminal: false }, { status: "running", stage: "building", terminal: false }, { status: "failed", stage: "failed", terminal: true, blocker: { code: "native-build-failed", summary: "Deterministic native build failure", layer: "build" } }] },
"native-blocked": { title: "Native blocked", steps: [{ status: "queued", stage: "queued", terminal: false }, { status: "blocked", stage: "preparing", terminal: true, blocker: { code: "native-capability-blocked", summary: "Deterministic native capability blocker", layer: "hwpod" } }] },
"native-canceled": { title: "Native canceled", steps: [{ status: "queued", stage: "queued", terminal: false }, { status: "running", stage: "agent-running", terminal: false }, { status: "canceled", stage: "canceled", terminal: true }] }
};
let runSequence = 0;
const runs = new Map<string, NativeRunState>();
function timestamp(index: number): string {
return new Date(BASE_TIME + index * 1000).toISOString();
}
function aggregateResponse(run: NativeRunState) {
const step = run.steps[run.cursor] ?? run.steps.at(-1)!;
const summary = {
status: step.status,
jobId: `job-${run.runId}`,
artifactCount: step.status === "completed" ? 2 : 0
};
const evidence = {
status: step.status,
blocker: step.blocker ?? null,
artifacts: step.status === "completed" ? [{ name: "firmware.bin" }, { name: "caserun.log" }] : []
};
const archivedRun = {
runId: run.runId,
caseId: run.caseId,
status: step.status,
stage: step.stage,
terminal: step.terminal,
mode: MODE
};
const source = { runId: run.runId, caseId: run.caseId, status: step.status, summary, evidence, run: archivedRun };
return {
ok: true,
mode: MODE,
contractVersion: "caserun.aggregate.v1",
runId: run.runId,
caseId: run.caseId,
status: step.status,
stage: step.stage,
terminal: step.terminal,
sourceAuthority: { mode: MODE, sequence: "request-driven" },
sha256: createHash("sha256").update(JSON.stringify(source)).digest("hex"),
summary,
evidence,
run: archivedRun
};
}
function runResponse(run: NativeRunState) {
const step = run.steps[run.cursor] ?? run.steps.at(-1)!;
return {
ok: true,
mode: MODE,
contractVersion: CONTRACT_VERSION,
runId: run.runId,
caseId: run.caseId,
status: step.status,
stage: step.stage,
terminal: step.terminal,
blocker: step.blocker ?? null,
createdAt: timestamp(0),
updatedAt: timestamp(run.cursor),
facts: {
jobId: `job-${run.runId}`,
artifactCount: step.status === "completed" ? 2 : 0,
evidenceStatus: step.status
},
references: {
status: { href: `/v1/caserun/runs/${run.runId}` },
events: { href: `/v1/caserun/runs/${run.runId}/events`, count: run.cursor + 1 },
aggregate: { href: `/v1/caserun/runs/${run.runId}/aggregate`, sha256: aggregateResponse(run).sha256 },
manifest: step.status === "completed" ? { href: `/v1/caserun/runs/${run.runId}/manifest`, count: 2 } : null,
replay: null,
trace: { traceId: `trace-${run.runId}` },
hwpod: { hwpodId: "native-hwpod", nodeId: "native-node" }
},
sourceAuthority: { mode: MODE, sequence: "request-driven" },
error: null,
statusUrl: `/v1/caserun/runs/${run.runId}`
};
}
export function listNativeCases() {
const cases = Object.entries(scenarios).map(([caseId, scenario]) => ({
caseId,
title: scenario.title,
mode: MODE,
available: true,
hwpodSpec: "native-test",
runtime: { sequence: scenario.steps.map((step) => step.status) }
}));
return { ok: true, mode: MODE, contractVersion: CONTRACT_VERSION, count: cases.length, cases, caseAuthority: { mode: MODE } };
}
export function startNativeRun(caseId: string) {
const scenario = scenarios[caseId];
if (!scenario) return null;
runSequence += 1;
const runId = `${caseId}-${runSequence}`;
const run: NativeRunState = { runId, caseId, cursor: 0, steps: scenario.steps };
runs.set(runId, run);
return runResponse(run);
}
export function readNativeRun(runId: string, advance = true) {
const run = runs.get(runId);
if (!run) return null;
if (advance && run.cursor < run.steps.length - 1) run.cursor += 1;
return runResponse(run);
}
export function readNativeEvents(runId: string) {
const run = runs.get(runId);
if (!run) return null;
const current = runResponse(run);
const events = run.steps.slice(0, run.cursor + 1).map((step, index) => ({
at: timestamp(index),
status: step.status,
stage: step.stage,
payload: { mode: MODE, terminal: step.terminal, blocker: step.blocker ?? null }
}));
return { ok: true, mode: MODE, contractVersion: CONTRACT_VERSION, runId, caseId: run.caseId, status: current.status, stage: current.stage, terminal: current.terminal, sourceAuthority: current.sourceAuthority, events };
}
export function readNativeAggregate(runId: string) {
const run = runs.get(runId);
return run ? aggregateResponse(run) : null;
}
export function resetNativeFixtures(): void {
runSequence = 0;
runs.clear();
}