Initial public-ready standalone repository

This commit is contained in:
OpenClaw Local committed 2026-03-11 12:23:57 +01:00
commit d002386bc2
66 files changed
+28683

No files matched your search

+651
View File
@@ -0,0 +1,651 @@
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { spawn } from "node:child_process";
interface DoDCheckResult {
id: string;
passed: boolean;
detail: string;
}
type DoDStatus = "DONE" | "NOT_DONE";
interface DoDStatusArtifact {
generatedAt: string;
runtimeDir: string;
status: DoDStatus;
passed: boolean;
checks: DoDCheckResult[];
}
interface WorkerProgress {
cycle?: number;
lastStep?: {
id?: string;
exitCode?: number;
};
}
interface ProductDoDDefinition {
fullChinese: boolean;
nonTechnicalCopy: boolean;
middleSchoolReadable: boolean;
globalVisibility: boolean;
appleNativeUI?: boolean;
realSubscriptionConnected?: boolean;
tiApproval?: boolean;
notes?: string;
}
interface ProductSourceAudit {
fullChinese: boolean;
nonTechnicalCopy: boolean;
middleSchoolReadable: boolean;
globalVisibility: boolean;
appleNativeUI: boolean;
details: {
fullChinese: string;
nonTechnicalCopy: string;
middleSchoolReadable: string;
globalVisibility: string;
appleNativeUI: string;
};
}
interface RealSubscriptionAudit {
connected: boolean;
detail: string;
sourcePath?: string;
}
interface PhraseGroup {
id: string;
options: string[];
}
const REQUIRED_ZH_GROUPS: PhraseGroup[] = [
{ id: "title", options: ["全局可视", "全局总览"] },
{
id: "summary",
options: [
"一眼看全局:定时任务、任务心跳、当前任务和工具调用。",
"一眼看全局:定时任务、任务心跳、当前任务、工具调用。",
"一眼看四件事:定时任务、任务心跳、当前任务、工具调用。",
],
},
{ id: "schedule", options: ["定时任务:"] },
{ id: "heartbeat", options: ["任务心跳:", "心跳检查:"] },
{ id: "currentTasks", options: ["当前任务:", "进行中任务:"] },
{ id: "toolCalls", options: ["工具调用:"] },
];
const REQUIRED_EN_GROUPS: PhraseGroup[] = [
{ id: "title", options: ["Global Visibility"] },
{
id: "summary",
options: [
"One place to see cron, heartbeat, current tasks, and tool calls.",
"One place to see timed jobs, heartbeat, current tasks, and tool calls.",
"At a glance: timed jobs, heartbeat, current tasks, and tool calls.",
"Simple view: schedule checks (cron), heartbeat checks, current tasks, and tool calls.",
"Simple view: schedule (cron), heartbeat, tasks in progress, and tool calls.",
],
},
{ id: "schedule", options: ["Cron:", "Timed jobs:", "Schedule checks (cron):", "Schedule (cron):"] },
{ id: "heartbeat", options: ["Heartbeat:", "Heartbeat checks:"] },
{ id: "currentTasks", options: ["Current tasks:", "Tasks in progress:"] },
{ id: "toolCalls", options: ["Tool calls:"] },
];
const GLOBAL_VISIBILITY_RENDER_TOKENS = [
"renderGlobalVisibilityStrip(",
"renderGlobalVisibilityCard(",
"renderGlobalVisibilityStripCard(",
'<div class="content-stack">${globalVisibilityBlock}${sectionBody}</div>',
"copy.scheduleLabel",
"copy.heartbeatLabel",
"copy.currentTasksLabel",
"copy.toolCallsLabel",
'buildGlobalVisibilityDetailHref("cron", language)',
'buildGlobalVisibilityDetailHref("heartbeat", language)',
'buildGlobalVisibilityDetailHref("current_task", language)',
'buildGlobalVisibilityDetailHref("tool_call", language)',
'taskType: "cron"',
'taskType: "heartbeat"',
'taskType: "current_task"',
'taskType: "tool_call"',
"signalCounts: {",
"schedule: enabledCronCount",
"heartbeat: enabledHeartbeatCount",
"currentTasks: currentTasksCount",
"toolCalls: toolCallsCount",
];
const GLOBAL_VISIBILITY_RENDER_GROUPS: PhraseGroup[] = [
{
id: "overview-block",
options: [
'const globalVisibilityBlock = options.section === "overview" ? globalVisibilityCard : globalVisibilityStripCard;',
'const globalVisibilityBlock = options.section === "overview" ? globalVisibilityCard : "";',
],
},
];
const NON_TECH_JARGON = ["telemetry", "instrumentation", "payload", "schema", "protocol", "SDK"];
const MIXED_ZH_PHRASES = [
"Cron 正在运行。",
"还没有设置 Cron。",
"保持 Cron 开启。",
"定时任务(cron)",
"定时检查(cron)",
"定时安排(cron)",
];
const APPLE_NATIVE_UI_RENDER_TOKENS = [
"data-apple-window-controls=\"true\"",
"--apple-glass-blur",
"-webkit-backdrop-filter",
"@media (prefers-reduced-motion: reduce)",
];
const APPLE_NATIVE_UI_RENDER_GROUPS: PhraseGroup[] = [
{
id: "polish-marker",
options: [
'data-ui-polish="apple-native-v1"',
'data-ui-polish="apple-native-v2"',
'data-ui-polish="apple-native-v3"',
],
},
{
id: "hover-elevation",
options: [
".card:hover { transform: translateY(-1px);",
".card:hover {",
],
},
];
async function writeJsonAtomic(path: string, value: unknown): Promise<void> {
await mkdir(dirname(path), { recursive: true });
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
await rename(tmp, path);
}
async function runNodeScript(
scriptPath: string,
args: string[],
): Promise<{ code: number | null; stdout: string; stderr: string }> {
return await new Promise((resolvePromise, rejectPromise) => {
const child = spawn(process.execPath, ["--import", "tsx", scriptPath, ...args], {
cwd: process.cwd(),
env: process.env,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.on("data", (chunk) => {
stdout += String(chunk);
});
child.stderr.on("data", (chunk) => {
stderr += String(chunk);
});
child.on("error", rejectPromise);
child.on("close", (code) => resolvePromise({ code, stdout, stderr }));
});
}
async function readProgress(path: string): Promise<WorkerProgress | null> {
try {
const raw = await readFile(path, "utf8");
return JSON.parse(raw) as WorkerProgress;
} catch {
return null;
}
}
async function readProductDoD(path: string): Promise<ProductDoDDefinition | null> {
try {
const raw = await readFile(path, "utf8");
return JSON.parse(raw) as ProductDoDDefinition;
} catch {
return null;
}
}
function findMissingPhrases(input: string, phrases: string[]): string[] {
return phrases.filter((phrase) => !input.includes(phrase));
}
function findMissingGroups(input: string, groups: PhraseGroup[]): string[] {
return groups
.filter((group) => group.options.every((phrase) => !input.includes(phrase)))
.map((group) => `${group.id}(${group.options.join(" | ")})`);
}
async function auditProductSource(uiServerPath: string): Promise<ProductSourceAudit> {
let source = "";
try {
source = await readFile(uiServerPath, "utf8");
} catch (error) {
const detail = error instanceof Error ? error.message : String(error);
return {
fullChinese: false,
nonTechnicalCopy: false,
middleSchoolReadable: false,
globalVisibility: false,
appleNativeUI: false,
details: {
fullChinese: `unable to read ${uiServerPath}: ${detail}`,
nonTechnicalCopy: `unable to read ${uiServerPath}: ${detail}`,
middleSchoolReadable: `unable to read ${uiServerPath}: ${detail}`,
globalVisibility: `unable to read ${uiServerPath}: ${detail}`,
appleNativeUI: `unable to read ${uiServerPath}: ${detail}`,
},
};
}
const copyBlock = source.match(
/function globalVisibilityCopy[\s\S]*?function formatExecutorAgentLabel/,
)?.[0];
const modelBlock = source.match(
/async function buildGlobalVisibilityViewModel[\s\S]*?function dashboardSectionLinks/,
)?.[0];
const globalVisibilityBlock =
[copyBlock, modelBlock]
.filter((segment): segment is string => typeof segment === "string" && segment.length > 0)
.join("\n\n")
.trim() || source;
const missingZh = findMissingGroups(globalVisibilityBlock, REQUIRED_ZH_GROUPS);
const missingEn = findMissingGroups(globalVisibilityBlock, REQUIRED_EN_GROUPS);
const missingRender = findMissingPhrases(source, GLOBAL_VISIBILITY_RENDER_TOKENS);
const missingRenderGroups = findMissingGroups(source, GLOBAL_VISIBILITY_RENDER_GROUPS);
const missingAppleNativeTokens = findMissingPhrases(source, APPLE_NATIVE_UI_RENDER_TOKENS);
const missingAppleNativeGroups = findMissingGroups(source, APPLE_NATIVE_UI_RENDER_GROUPS);
const matchedMixedZh = MIXED_ZH_PHRASES.filter((phrase) => globalVisibilityBlock.includes(phrase));
const matchedJargon = NON_TECH_JARGON.filter((token) =>
globalVisibilityBlock.toLowerCase().includes(token.toLowerCase()),
);
const readabilitySamples = [
...REQUIRED_EN_GROUPS.flatMap((group) => group.options),
"Tasks checked:",
"Tasks selected:",
"Tasks started:",
"Recent tool calls:",
"Tool calls in recent activity:",
];
const longPhrases = readabilitySamples.filter((phrase) => phrase.length > 90);
const hardWords = readabilitySamples
.flatMap((phrase) => phrase.split(/[^A-Za-z]+/))
.filter((word) => word.length >= 14);
return {
fullChinese: missingZh.length === 0 && matchedMixedZh.length === 0,
nonTechnicalCopy: missingEn.length === 0 && matchedJargon.length === 0,
middleSchoolReadable: longPhrases.length === 0 && hardWords.length === 0,
globalVisibility:
missingRender.length === 0 &&
missingRenderGroups.length === 0 &&
missingEn.length === 0 &&
missingZh.length === 0,
appleNativeUI: missingAppleNativeTokens.length === 0 && missingAppleNativeGroups.length === 0,
details: {
fullChinese:
missingZh.length === 0 && matchedMixedZh.length === 0
? "ui source includes required zh copy for global visibility"
: `missing zh copy: ${missingZh.join(", ") || "none"} mixed zh/en copy: ${matchedMixedZh.join(", ") || "none"}`,
nonTechnicalCopy:
missingEn.length === 0 && matchedJargon.length === 0
? "global visibility copy remains plain-language and operator-facing"
: `missing en plain copy=${missingEn.join(", ") || "none"} jargon=${matchedJargon.join(", ") || "none"}`,
middleSchoolReadable:
longPhrases.length === 0 && hardWords.length === 0
? "global visibility copy length/wording stays middle-school readable"
: `long phrases=${longPhrases.join(" | ") || "none"} hard words=${hardWords.join(", ") || "none"}`,
globalVisibility:
missingRender.length === 0 && missingRenderGroups.length === 0
? "strip + card render all four signals (cron/heartbeat/current tasks/tool calls) across sections"
: `missing render tokens: ${[...missingRender, ...missingRenderGroups].join(", ")}`,
appleNativeUI:
missingAppleNativeTokens.length === 0 && missingAppleNativeGroups.length === 0
? "apple-native polish markers present (glass surfaces, window controls, reduced-motion safety)"
: `missing apple-native tokens: ${[...missingAppleNativeTokens, ...missingAppleNativeGroups].join(", ")}`,
},
};
}
function subscriptionSnapshotPaths(runtimeDir: string): string[] {
const openclawHome = process.env.OPENCLAW_HOME?.trim() || join(homedir(), ".openclaw");
const paths = [
process.env.OPENCLAW_SUBSCRIPTION_SNAPSHOT_PATH?.trim(),
resolve(runtimeDir, "subscription-snapshot.json"),
join(openclawHome, "subscription.json"),
join(openclawHome, "subscription-snapshot.json"),
join(openclawHome, "billing", "subscription.json"),
join(openclawHome, "billing", "subscription-snapshot.json"),
join(openclawHome, "billing", "usage.json"),
join(openclawHome, "usage", "subscription.json"),
join(openclawHome, "usage", "subscription-snapshot.json"),
].filter((item): item is string => typeof item === "string" && item.trim().length > 0);
return [...new Set(paths)];
}
function asObjectValue(input: unknown): Record<string, unknown> | undefined {
return input !== null && typeof input === "object" && !Array.isArray(input)
? (input as Record<string, unknown>)
: undefined;
}
function asStringValue(input: unknown): string | undefined {
return typeof input === "string" ? input : undefined;
}
function pickFirstNonNegative(values: unknown[]): number | undefined {
for (const value of values) {
if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
}
return undefined;
}
function parseSubscriptionSnapshotSignal(raw: unknown): {
consumed?: number;
remaining?: number;
limit?: number;
unit: string;
planLabel: string;
} {
const root = asObjectValue(raw) ?? {};
const subscription = asObjectValue(root.subscription) ?? {};
const usage = asObjectValue(root.usage) ?? asObjectValue(subscription.usage) ?? {};
const rootCost = asObjectValue(root.cost) ?? {};
const subscriptionCost = asObjectValue(subscription.cost) ?? {};
const plan = asObjectValue(root.plan) ?? asObjectValue(subscription.plan) ?? {};
const consumed = pickFirstNonNegative([
root.consumed,
root.used,
root.spent,
subscription.consumed,
subscription.used,
subscription.spent,
usage.consumed,
usage.used,
usage.spent,
rootCost.used,
rootCost.spent,
subscriptionCost.used,
subscriptionCost.spent,
]);
const limit = pickFirstNonNegative([
root.limit,
root.total,
root.quota,
root.cap,
subscription.limit,
subscription.total,
subscription.quota,
subscription.cap,
usage.limit,
usage.total,
usage.quota,
rootCost.limit,
subscriptionCost.limit,
]);
const remaining = pickFirstNonNegative([
root.remaining,
root.left,
subscription.remaining,
subscription.left,
usage.remaining,
usage.left,
typeof limit === "number" && typeof consumed === "number" ? Math.max(0, limit - consumed) : undefined,
]);
const unit =
asStringValue(root.unit) ??
asStringValue(subscription.unit) ??
asStringValue(usage.unit) ??
asStringValue(root.currency) ??
asStringValue(subscription.currency) ??
asStringValue(usage.currency) ??
"USD";
const planLabel =
asStringValue(plan.name) ??
asStringValue(root.planLabel) ??
asStringValue(root.planName) ??
asStringValue(root.plan) ??
asStringValue(root.tier) ??
asStringValue(subscription.planLabel) ??
asStringValue(subscription.planName) ??
asStringValue(subscription.plan) ??
asStringValue(subscription.tier) ??
"Subscription";
return {
consumed,
remaining,
limit,
unit,
planLabel,
};
}
async function auditRealSubscriptionSource(runtimeDir: string): Promise<RealSubscriptionAudit> {
const paths = subscriptionSnapshotPaths(runtimeDir);
const partial: string[] = [];
const unreadable: string[] = [];
const missing: string[] = [];
for (const path of paths) {
try {
const raw = JSON.parse(await readFile(path, "utf8")) as unknown;
const parsed = parseSubscriptionSnapshotSignal(raw);
const hasAllFields =
typeof parsed.consumed === "number" && typeof parsed.remaining === "number" && typeof parsed.limit === "number";
if (hasAllFields) {
return {
connected: true,
sourcePath: path,
detail: `provider snapshot connected at ${path} (${parsed.planLabel}; ${parsed.unit})`,
};
}
const missingFields = [
typeof parsed.consumed === "number" ? "" : "consumed",
typeof parsed.remaining === "number" ? "" : "remaining",
typeof parsed.limit === "number" ? "" : "limit",
].filter((item) => item !== "");
partial.push(`${path} missing ${missingFields.join("/") || "required fields"}`);
} catch (error) {
const code =
error && typeof error === "object" && "code" in error && typeof (error as { code?: unknown }).code === "string"
? (error as { code: string }).code
: undefined;
if (code === "ENOENT") {
missing.push(path);
} else {
unreadable.push(`${path}${code ? ` (${code})` : ""}`);
}
}
}
if (partial.length > 0) {
return {
connected: false,
detail: `provider snapshot found but incomplete: ${partial.join("; ")}`,
};
}
if (unreadable.length > 0) {
return {
connected: false,
detail: `provider snapshot unreadable: ${unreadable.join("; ")}`,
};
}
return {
connected: false,
detail: missing.length > 0 ? `provider snapshot missing at: ${missing.join(", ")}` : "provider snapshot paths not configured",
};
}
function toProductChecks(
product: ProductDoDDefinition | null,
sourceAudit: ProductSourceAudit,
subscriptionAudit: RealSubscriptionAudit,
requireTiApproval: boolean,
): DoDCheckResult[] {
const declared = product ?? null;
const checks: DoDCheckResult[] = [
{
id: "product-dod-definition",
passed: declared !== null,
detail: declared ? "runtime/evidence/product-dod.json present" : "missing runtime/evidence/product-dod.json",
},
];
const declaredFullChinese = declared?.fullChinese === true;
checks.push({
id: "product-full-chinese",
passed: declaredFullChinese && sourceAudit.fullChinese,
detail: `声明=${declaredFullChinese ? "通过" : "未通过"};源码=${sourceAudit.fullChinese ? "通过" : "未通过"}(${sourceAudit.details.fullChinese})`,
});
const declaredNonTechnical = declared?.nonTechnicalCopy === true;
checks.push({
id: "product-non-technical-copy",
passed: declaredNonTechnical && sourceAudit.nonTechnicalCopy,
detail: `声明=${declaredNonTechnical ? "通过" : "未通过"};源码=${sourceAudit.nonTechnicalCopy ? "通过" : "未通过"}(${sourceAudit.details.nonTechnicalCopy})`,
});
const declaredReadable = declared?.middleSchoolReadable === true;
checks.push({
id: "product-middle-school-readable",
passed: declaredReadable && sourceAudit.middleSchoolReadable,
detail: `声明=${declaredReadable ? "通过" : "未通过"};源码=${sourceAudit.middleSchoolReadable ? "通过" : "未通过"}(${sourceAudit.details.middleSchoolReadable})`,
});
const declaredGlobalVisibility = declared?.globalVisibility === true;
checks.push({
id: "product-global-visibility",
passed: declaredGlobalVisibility && sourceAudit.globalVisibility,
detail: `声明=${declaredGlobalVisibility ? "通过" : "未通过"};源码=${sourceAudit.globalVisibility ? "通过" : "未通过"}(${sourceAudit.details.globalVisibility})`,
});
const declaredAppleNativeUI = declared?.appleNativeUI === true;
checks.push({
id: "product-apple-native-ui",
passed: declaredAppleNativeUI && sourceAudit.appleNativeUI,
detail:
`声明=${declaredAppleNativeUI ? "通过" : "未通过"};` +
`客观检查=${sourceAudit.appleNativeUI ? "通过" : "未通过"}(${sourceAudit.details.appleNativeUI})`,
});
const declaredRealSubscriptionConnected = declared?.realSubscriptionConnected === true;
checks.push({
id: "product-real-subscription-connected",
passed: declaredRealSubscriptionConnected && subscriptionAudit.connected,
detail:
`声明=${declaredRealSubscriptionConnected ? "通过" : "未通过"};` +
`客观检查=${subscriptionAudit.connected ? "通过" : "未通过"}(${subscriptionAudit.detail})`,
});
const tiApproved = declared?.tiApproval === true;
checks.push({
id: "product-ti-approval",
passed: requireTiApproval ? tiApproved : true,
detail: requireTiApproval
? tiApproved
? "Ti 人工验收:通过(严格模式)"
: "Ti 人工验收:未通过(严格模式)"
: tiApproved
? "Ti 人工验收:已记录(非阻塞)"
: "Ti 人工验收:待补充(非阻塞)",
});
return checks;
}
async function runChecks(runtimeDir: string): Promise<DoDCheckResult[]> {
const checks: DoDCheckResult[] = [];
const goalPath = resolve(runtimeDir, "goal-state.json");
const evidencePath = resolve(runtimeDir, "evidence", "latest.json");
const progressPath = resolve(runtimeDir, "evidence", "worker-progress.json");
const productDoDPath = resolve(runtimeDir, "evidence", "product-dod.json");
const uiServerPath = resolve(process.cwd(), "src", "ui", "server.ts");
const requireTiApproval = (process.env.DOD_REQUIRE_TI_APPROVAL ?? "false").toLowerCase() === "true";
const goalGate = await runNodeScript(resolve(process.cwd(), "scripts", "goal-gate.ts"), [goalPath]);
checks.push({
id: "goal-gate",
passed: goalGate.code === 0,
detail: goalGate.code === 0 ? "goal gate passed" : (goalGate.stderr.trim() || goalGate.stdout.trim() || "goal gate failed"),
});
const evidenceGate = await runNodeScript(resolve(process.cwd(), "scripts", "evidence-gate.ts"), ["validate", evidencePath]);
checks.push({
id: "evidence-gate",
passed: evidenceGate.code === 0,
detail:
evidenceGate.code === 0
? "evidence bundle valid"
: (evidenceGate.stderr.trim() || evidenceGate.stdout.trim() || "evidence validation failed"),
});
const progress = await readProgress(progressPath);
const stepPassed = progress?.lastStep?.exitCode === 0;
checks.push({
id: "worker-last-step",
passed: stepPassed,
detail: stepPassed
? `cycle=${progress?.cycle ?? "unknown"} step=${progress?.lastStep?.id ?? "unknown"} exit=0`
: "missing or failing worker-progress lastStep.exitCode",
});
const product = await readProductDoD(productDoDPath);
const sourceAudit = await auditProductSource(uiServerPath);
const subscriptionAudit = await auditRealSubscriptionSource(runtimeDir);
checks.push(...toProductChecks(product, sourceAudit, subscriptionAudit, requireTiApproval));
return checks;
}
async function main(): Promise<void> {
const runtimeDir = resolve(process.argv[2] ?? join(process.cwd(), "runtime"));
const statusPath = resolve(process.argv[3] ?? join(runtimeDir, "evidence", "dod-status.json"));
let checks: DoDCheckResult[] = [];
try {
checks = await runChecks(runtimeDir);
} catch (error) {
checks = [
{
id: "dod-check-runtime",
passed: false,
detail: error instanceof Error ? error.message : String(error),
},
];
}
const passed = checks.every((check) => check.passed);
const artifact: DoDStatusArtifact = {
generatedAt: new Date().toISOString(),
runtimeDir,
status: passed ? "DONE" : "NOT_DONE",
passed,
checks,
};
await writeJsonAtomic(statusPath, artifact);
if (!artifact.passed) {
console.error(`NOT_DONE status=${statusPath}`);
process.exit(1);
}
console.log(`DONE status=${statusPath}`);
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
+78
View File
@@ -0,0 +1,78 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
export interface EvidenceBundle {
generatedAt: string;
itemId: string;
completed: string;
filesChanged: string[];
verification: string;
visibleMarker: string;
}
const cmd = (process.argv[2] ?? "validate").toLowerCase();
const targetPath = resolve(process.argv[3] ?? join(process.cwd(), "runtime", "evidence", "latest.json"));
export function validateBundle(bundle: Partial<EvidenceBundle>): string[] {
const errors: string[] = [];
if (!bundle.itemId || bundle.itemId.trim().length === 0) errors.push("missing itemId");
if (!bundle.completed || bundle.completed.trim().length === 0) errors.push("missing completed");
if (!Array.isArray(bundle.filesChanged) || bundle.filesChanged.length === 0) errors.push("missing filesChanged[]");
if (!bundle.verification || bundle.verification.trim().length === 0) errors.push("missing verification");
if (!bundle.visibleMarker || bundle.visibleMarker.trim().length === 0) errors.push("missing visibleMarker");
return errors;
}
async function emit(path: string): Promise<number> {
const files = (process.env.EVIDENCE_FILES ?? "")
.split(",")
.map((x) => x.trim())
.filter(Boolean);
const bundle: EvidenceBundle = {
generatedAt: new Date().toISOString(),
itemId: process.env.EVIDENCE_ITEM_ID ?? "",
completed: process.env.EVIDENCE_COMPLETED ?? "",
filesChanged: files,
verification: process.env.EVIDENCE_VERIFICATION ?? "",
visibleMarker: process.env.EVIDENCE_VISIBLE_MARKER ?? "",
};
const errors = validateBundle(bundle);
if (errors.length > 0) {
console.error(`EVIDENCE_INVALID ${errors.join("; ")}`);
return 1;
}
await mkdir(dirname(path), { recursive: true });
await writeFile(path, `${JSON.stringify(bundle, null, 2)}\n`, "utf8");
console.log(`EVIDENCE_EMITTED ${path}`);
return 0;
}
async function validate(path: string): Promise<number> {
try {
const raw = await readFile(path, "utf8");
const bundle = JSON.parse(raw) as Partial<EvidenceBundle>;
const errors = validateBundle(bundle);
if (errors.length > 0) {
console.error(`EVIDENCE_INVALID ${errors.join("; ")}`);
return 1;
}
console.log(`EVIDENCE_VALID item=${bundle.itemId} file=${path}`);
return 0;
} catch (error) {
console.error(`EVIDENCE_READ_ERROR ${error instanceof Error ? error.message : String(error)}`);
return 1;
}
}
async function main(): Promise<void> {
const code = cmd === "emit" ? await emit(targetPath) : await validate(targetPath);
process.exit(code);
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
+128
View File
@@ -0,0 +1,128 @@
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { spawn } from "node:child_process";
interface EvidenceBundle {
generatedAt: string;
itemId: string;
completed: string;
filesChanged: string[];
verification: string;
visibleMarker: string;
}
interface OutboundProgressPayload {
reportedAt: string;
evidencePath: string;
progress: {
itemId: string;
completed: string;
filesChanged: string[];
verification: string;
visibleMarker: string;
};
}
function buildSnapshotId(now = new Date()): string {
const yyyy = now.getUTCFullYear();
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
const dd = String(now.getUTCDate()).padStart(2, "0");
const hh = String(now.getUTCHours()).padStart(2, "0");
const min = String(now.getUTCMinutes()).padStart(2, "0");
const sec = String(now.getUTCSeconds()).padStart(2, "0");
return `${yyyy}${mm}${dd}-${hh}${min}${sec}Z`;
}
function isCompletionLike(text: string): boolean {
return /(\bdone\b|\bcompleted\b|完成|已完成|全部完成)/i.test(text);
}
function isFullPassLike(text: string): boolean {
return /(\bfull\s+pass\b|\ball\s+checks?\s+pass(?:ed)?\b|全部通过|全通过|全量通过|全面通过)/i.test(text);
}
async function runNodeScript(scriptPath: string, args: string[]): Promise<number | null> {
return await new Promise((resolvePromise, rejectPromise) => {
const child = spawn(process.execPath, ["--import", "tsx", scriptPath, ...args], {
cwd: process.cwd(),
env: process.env,
stdio: "ignore",
});
child.on("error", rejectPromise);
child.on("close", (code) => resolvePromise(code));
});
}
function validateEvidenceBundle(bundle: Partial<EvidenceBundle>): string[] {
const errors: string[] = [];
if (!bundle.itemId || bundle.itemId.trim() === "") errors.push("missing itemId");
if (!bundle.completed || bundle.completed.trim() === "") errors.push("missing completed");
if (!Array.isArray(bundle.filesChanged) || bundle.filesChanged.length === 0) errors.push("missing filesChanged[]");
if (!bundle.verification || bundle.verification.trim() === "") errors.push("missing verification");
if (!bundle.visibleMarker || bundle.visibleMarker.trim() === "") errors.push("missing visibleMarker");
return errors;
}
async function writeJsonAtomic(path: string, value: unknown): Promise<void> {
await mkdir(dirname(path), { recursive: true });
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
await rename(tmp, path);
}
async function main(): Promise<void> {
const evidencePath = resolve(process.argv[2] ?? join(process.cwd(), "runtime", "evidence", "latest.json"));
const outboundDir = resolve(process.argv[3] ?? join(process.cwd(), "runtime", "evidence", "outbound"));
const raw = await readFile(evidencePath, "utf8");
const evidence = JSON.parse(raw) as Partial<EvidenceBundle>;
const errors = validateEvidenceBundle(evidence);
if (errors.length > 0) {
console.error(`REPORT_BLOCKED_EVIDENCE ${errors.join("; ")}`);
process.exit(1);
}
const completionText = String(evidence.completed ?? "");
const requireDodGate = (process.env.REQUIRE_DOD_FOR_COMPLETION ?? "true").toLowerCase() !== "false";
const claimsCompletion = isCompletionLike(completionText) || isFullPassLike(completionText);
if ((requireDodGate && claimsCompletion) || isFullPassLike(completionText)) {
const runtimeDir = resolve(process.cwd(), "runtime");
const dodCode = await runNodeScript(resolve(process.cwd(), "scripts", "dod-check.ts"), [runtimeDir]);
if (dodCode !== 0) {
console.error("REPORT_BLOCKED_DOD NOT_DONE");
process.exit(1);
}
}
const now = new Date();
const snapshotId = buildSnapshotId(now);
const snapshotPath = resolve(outboundDir, `${snapshotId}.json`);
const latestPath = resolve(outboundDir, "latest.json");
const pointerPath = resolve(outboundDir, "latest.pointer.json");
const payload: OutboundProgressPayload = {
reportedAt: now.toISOString(),
evidencePath,
progress: {
itemId: evidence.itemId ?? "",
completed: evidence.completed ?? "",
filesChanged: evidence.filesChanged ?? [],
verification: evidence.verification ?? "",
visibleMarker: evidence.visibleMarker ?? "",
},
};
await writeJsonAtomic(snapshotPath, payload);
await writeJsonAtomic(latestPath, payload);
await writeJsonAtomic(pointerPath, {
updatedAt: now.toISOString(),
snapshotId,
latest: snapshotPath,
});
console.log(`REPORT_EMITTED latest=${latestPath} snapshot=${snapshotPath}`);
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
+403
View File
@@ -0,0 +1,403 @@
import { promises as fs } from "node:fs";
import path from "node:path";
import vm from "node:vm";
import { deflateSync } from "node:zlib";
type Sprite = string[][];
type SpriteRuntime = {
spriteSize: number;
spriteFactory: Record<string, () => Sprite>;
spriteVisualSpanCompensation: Record<string, number>;
};
type StaffAvatarExport = {
agentId: string;
animal: string;
label: string;
labelZh: string;
};
type SpriteBounds = {
minX: number;
minY: number;
maxX: number;
maxY: number;
width: number;
height: number;
span: number;
};
export const STAFF_AVATARS: StaffAvatarExport[] = [
{ agentId: "codex", animal: "robot", label: "Codex", labelZh: "机器人" },
{ agentId: "coq", animal: "rooster", label: "Coq", labelZh: "公鸡" },
{ agentId: "dolphin", animal: "dolphin", label: "Dolphin", labelZh: "海豚" },
{ agentId: "main", animal: "lion", label: "Main", labelZh: "狮子" },
{ agentId: "monkey", animal: "monkey", label: "Monkey", labelZh: "猴子" },
{ agentId: "otter", animal: "otter", label: "Otter", labelZh: "水獭" },
{ agentId: "pandas", animal: "panda", label: "Pandas", labelZh: "熊猫" },
{ agentId: "tiger", animal: "tiger", label: "Tiger", labelZh: "老虎" },
];
export function extractSpriteRuntime(source: string): SpriteRuntime {
const start = source.indexOf("const spriteSize = 44;");
const end = source.indexOf("const motionActors = [];", start);
if (start < 0 || end < 0 || end <= start) {
throw new Error("Unable to locate embedded staff avatar sprite runtime in src/ui/server.ts");
}
const block = source.slice(start, end);
const runtime = vm.runInNewContext(
`
(() => {
${block}
return { spriteSize, spriteFactory, spriteVisualSpanCompensation };
})()
`,
{ Math },
) as SpriteRuntime;
if (!runtime || typeof runtime.spriteSize !== "number" || !runtime.spriteFactory || !runtime.spriteVisualSpanCompensation) {
throw new Error("Failed to evaluate embedded avatar sprite runtime");
}
return runtime;
}
export async function loadSpriteRuntime(serverPath: string): Promise<SpriteRuntime> {
const source = await fs.readFile(serverPath, "utf8");
return extractSpriteRuntime(source);
}
export function getSpriteBounds(sprite: Sprite): SpriteBounds {
let minX = Number.POSITIVE_INFINITY;
let minY = Number.POSITIVE_INFINITY;
let maxX = Number.NEGATIVE_INFINITY;
let maxY = Number.NEGATIVE_INFINITY;
for (let y = 0; y < sprite.length; y += 1) {
for (let x = 0; x < sprite[y].length; x += 1) {
if (!sprite[y][x]) continue;
minX = Math.min(minX, x);
minY = Math.min(minY, y);
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
}
}
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {
return { minX: 0, minY: 0, maxX: 0, maxY: 0, width: 1, height: 1, span: 1 };
}
const width = maxX - minX + 1;
const height = maxY - minY + 1;
return { minX, minY, maxX, maxY, width, height, span: Math.max(width, height) };
}
export function centerSprite(sprite: Sprite, padding = 2): Sprite {
const bounds = getSpriteBounds(sprite);
const size = bounds.span + padding * 2;
const centered = Array.from({ length: size }, () => Array(size).fill(""));
const offsetX = Math.floor((size - bounds.width) / 2);
const offsetY = Math.floor((size - bounds.height) / 2);
for (let y = bounds.minY; y <= bounds.maxY; y += 1) {
for (let x = bounds.minX; x <= bounds.maxX; x += 1) {
const color = sprite[y]?.[x];
if (!color) continue;
centered[offsetY + (y - bounds.minY)][offsetX + (x - bounds.minX)] = color;
}
}
return centered;
}
export function renderSpriteSvg(sprite: Sprite, pixelScale = 8): string {
const centered = centerSprite(sprite);
const size = centered.length;
const width = size * pixelScale;
const rects: string[] = [];
for (let y = 0; y < size; y += 1) {
for (let x = 0; x < size; x += 1) {
const color = centered[y]?.[x];
if (!color) continue;
rects.push(
`<rect x="${x * pixelScale}" y="${y * pixelScale}" width="${pixelScale}" height="${pixelScale}" fill="${color}" />`,
);
}
}
return [
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${width}" viewBox="0 0 ${width} ${width}" shape-rendering="crispEdges">`,
`<rect width="${width}" height="${width}" fill="transparent" />`,
...rects,
`</svg>`,
].join("\n");
}
export function renderSpriteRgba(sprite: Sprite, pixelScale = 8): { width: number; height: number; data: Buffer } {
const centered = centerSprite(sprite);
const size = centered.length;
const width = size * pixelScale;
const height = size * pixelScale;
const data = Buffer.alloc(width * height * 4);
for (let y = 0; y < size; y += 1) {
for (let x = 0; x < size; x += 1) {
const color = centered[y]?.[x];
for (let py = 0; py < pixelScale; py += 1) {
for (let px = 0; px < pixelScale; px += 1) {
const idx = ((y * pixelScale + py) * width + (x * pixelScale + px)) * 4;
if (!color) {
data[idx + 0] = 0;
data[idx + 1] = 0;
data[idx + 2] = 0;
data[idx + 3] = 0;
continue;
}
const rgb = hexToRgb(color);
data[idx + 0] = rgb.r;
data[idx + 1] = rgb.g;
data[idx + 2] = rgb.b;
data[idx + 3] = 255;
}
}
}
}
return { width, height, data };
}
function hexToRgb(hex: string): { r: number; g: number; b: number } {
const safe = hex.trim().replace(/^#/, "");
if (safe.length !== 6) {
throw new Error(`Unsupported color "${hex}"`);
}
return {
r: Number.parseInt(safe.slice(0, 2), 16),
g: Number.parseInt(safe.slice(2, 4), 16),
b: Number.parseInt(safe.slice(4, 6), 16),
};
}
const crcTable = (() => {
const table = new Uint32Array(256);
for (let i = 0; i < 256; i += 1) {
let c = i;
for (let k = 0; k < 8; k += 1) {
c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1);
}
table[i] = c >>> 0;
}
return table;
})();
function crc32(buffer: Buffer): number {
let crc = 0xffffffff;
for (let i = 0; i < buffer.length; i += 1) {
crc = crcTable[(crc ^ buffer[i]) & 0xff] ^ (crc >>> 8);
}
return (crc ^ 0xffffffff) >>> 0;
}
function pngChunk(type: string, data: Buffer): Buffer {
const typeBuffer = Buffer.from(type, "ascii");
const length = Buffer.alloc(4);
length.writeUInt32BE(data.length, 0);
const crcBuffer = Buffer.concat([typeBuffer, data]);
const crc = Buffer.alloc(4);
crc.writeUInt32BE(crc32(crcBuffer), 0);
return Buffer.concat([length, typeBuffer, data, crc]);
}
export function renderSpritePng(sprite: Sprite, pixelScale = 8): Buffer {
const rgba = renderSpriteRgba(sprite, pixelScale);
const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
const ihdr = Buffer.alloc(13);
ihdr.writeUInt32BE(rgba.width, 0);
ihdr.writeUInt32BE(rgba.height, 4);
ihdr[8] = 8;
ihdr[9] = 6;
ihdr[10] = 0;
ihdr[11] = 0;
ihdr[12] = 0;
const stride = rgba.width * 4;
const raw = Buffer.alloc((stride + 1) * rgba.height);
for (let y = 0; y < rgba.height; y += 1) {
const rowStart = y * (stride + 1);
raw[rowStart] = 0;
rgba.data.copy(raw, rowStart + 1, y * stride, (y + 1) * stride);
}
const idat = deflateSync(raw, { level: 9 });
return Buffer.concat([
signature,
pngChunk("IHDR", ihdr),
pngChunk("IDAT", idat),
pngChunk("IEND", Buffer.alloc(0)),
]);
}
export function renderPreviewHtml(items: StaffAvatarExport[]): string {
const cards = items
.map(
(item) => `<article class="card">
<img src="./${item.agentId}.png" alt="${item.label}" width="176" height="176" />
<div class="meta">
<h2>${item.label}</h2>
<p>${item.labelZh}</p>
<code>${item.agentId}</code>
</div>
</article>`,
)
.join("\n");
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Staff Avatar Exports</title>
<style>
:root {
color-scheme: light;
--bg: #f5f5f7;
--panel: #ffffff;
--border: rgba(17, 24, 39, 0.08);
--text: #1d1d1f;
--muted: #6e6e73;
--shadow: 0 18px 40px rgba(15, 23, 42, 0.06);
font-family: "SF Pro Display", "SF Pro Text", "Helvetica Neue", sans-serif;
}
body {
margin: 0;
background: linear-gradient(180deg, #fbfbfd 0%, var(--bg) 100%);
color: var(--text);
padding: 40px;
}
h1 {
margin: 0 0 12px;
font-size: 34px;
line-height: 1.05;
}
p.lead {
margin: 0 0 28px;
color: var(--muted);
font-size: 17px;
line-height: 1.5;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 18px;
}
.card {
background: rgba(255,255,255,0.88);
border: 1px solid var(--border);
border-radius: 24px;
padding: 18px;
box-shadow: var(--shadow);
backdrop-filter: blur(20px);
}
img {
display: block;
width: 176px;
height: 176px;
margin: 0 auto 12px;
image-rendering: pixelated;
}
h2 {
margin: 0;
font-size: 22px;
}
.meta p,
.meta code {
margin: 6px 0 0;
color: var(--muted);
font-size: 14px;
display: block;
}
</style>
</head>
<body>
<h1>Staff Avatar Exports</h1>
<p class="lead">Exported from the current pixel avatar definitions embedded in <code>src/ui/server.ts</code>.</p>
<section class="grid">
${cards}
</section>
</body>
</html>`;
}
export async function exportStaffAvatars(outputDir: string, serverPath: string): Promise<string[]> {
const runtime = await loadSpriteRuntime(serverPath);
await fs.mkdir(outputDir, { recursive: true });
const written: string[] = [];
for (const item of STAFF_AVATARS) {
const renderer = runtime.spriteFactory[item.animal];
if (!renderer) {
throw new Error(`Missing renderer for animal "${item.animal}"`);
}
const sprite = renderer();
const svg = renderSpriteSvg(sprite);
const svgPath = path.join(outputDir, `${item.agentId}.svg`);
await fs.writeFile(svgPath, svg, "utf8");
written.push(svgPath);
const png = renderSpritePng(sprite);
const pngPath = path.join(outputDir, `${item.agentId}.png`);
await fs.writeFile(pngPath, png);
written.push(pngPath);
}
const manifestPath = path.join(outputDir, "manifest.json");
await fs.writeFile(
manifestPath,
JSON.stringify(
{
generatedAt: new Date().toISOString(),
source: "src/ui/server.ts",
count: STAFF_AVATARS.length,
avatars: STAFF_AVATARS.map((item) => ({
agentId: item.agentId,
animal: item.animal,
label: item.label,
labelZh: item.labelZh,
svg: `${item.agentId}.svg`,
png: `${item.agentId}.png`,
})),
},
null,
2,
),
"utf8",
);
written.push(manifestPath);
const previewPath = path.join(outputDir, "index.html");
await fs.writeFile(previewPath, renderPreviewHtml(STAFF_AVATARS), "utf8");
written.push(previewPath);
return written;
}
async function main(): Promise<void> {
const repoRoot = process.cwd();
const serverPath = path.join(repoRoot, "src/ui/server.ts");
const outputDir = path.join(repoRoot, "runtime/exports/staff-avatars");
const written = await exportStaffAvatars(outputDir, serverPath);
process.stdout.write(`Exported ${written.length} staff avatar files to ${outputDir}\n`);
}
if (require.main === module) {
main().catch((error: unknown) => {
const message = error instanceof Error ? error.stack ?? error.message : String(error);
process.stderr.write(`${message}\n`);
process.exitCode = 1;
});
}
+38
View File
@@ -0,0 +1,38 @@
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
interface GoalState {
goalId: string;
definitionOfDone: string[];
phases: Array<{ id: string; status: string }>;
}
const path = resolve(process.argv[2] ?? "runtime/goal-state.json");
async function main() {
const raw = await readFile(path, "utf8");
const state = JSON.parse(raw) as GoalState;
const inProgress = state.phases.filter((p) => p.status === "in_progress");
const blocked = state.phases.filter((p) => p.status === "blocked");
if (blocked.length > 0) {
console.error(`GATE_BLOCKED phases=${blocked.map((p) => p.id).join(",")}`);
process.exit(1);
}
if (inProgress.length > 1) {
console.error(`GATE_INVALID multiple_in_progress=${inProgress.map((p) => p.id).join(",")}`);
process.exit(1);
}
const doneCount = state.phases.filter((p) => p.status === "done").length;
const total = state.phases.length;
const current = inProgress[0]?.id ?? "none";
console.log(`GATE_OK goal=${state.goalId} done=${doneCount}/${total} current=${current}`);
}
main().catch((e) => {
console.error(e instanceof Error ? e.message : String(e));
process.exit(1);
});
+56
View File
@@ -0,0 +1,56 @@
import { mkdir, stat, writeFile } from "node:fs/promises";
import { join, resolve } from "node:path";
export interface HealthSnapshot {
generatedAt: string;
files: {
lock: { path: string; ageMs: number | null };
evidence: { path: string; ageMs: number | null };
recovery: { path: string; ageMs: number | null };
};
}
async function ageMs(path: string): Promise<number | null> {
try {
const s = await stat(path);
return Math.max(0, Date.now() - s.mtimeMs);
} catch {
return null;
}
}
export async function buildHealthSnapshot(runtimeDir = resolve(process.cwd(), "runtime")): Promise<HealthSnapshot> {
return {
generatedAt: new Date().toISOString(),
files: {
lock: {
path: join(runtimeDir, "run.lock"),
ageMs: await ageMs(join(runtimeDir, "run.lock")),
},
evidence: {
path: join(runtimeDir, "evidence", "latest.json"),
ageMs: await ageMs(join(runtimeDir, "evidence", "latest.json")),
},
recovery: {
path: join(runtimeDir, "recovery", "latest.json"),
ageMs: await ageMs(join(runtimeDir, "recovery", "latest.json")),
},
},
};
}
async function main() {
const runtime = resolve(process.cwd(), "runtime");
const out = resolve(runtime, "health", "latest.json");
const snapshot = await buildHealthSnapshot(runtime);
await mkdir(resolve(runtime, "health"), { recursive: true });
await writeFile(out, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
console.log(`HEALTH_SNAPSHOT_WRITTEN ${out}`);
}
if ((process.argv[1] ?? "").endsWith("health-snapshot.ts")) {
main().catch((e) => {
console.error(e instanceof Error ? e.message : String(e));
process.exit(1);
});
}
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
import datetime
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
BASE = ROOT / "runtime"
STATE_PATH = BASE / "state.json"
DOD_PATH = BASE / "evidence" / "dod-status.json"
OUTPUT_PATH = BASE / "evidence" / "dod-eval.json"
now = datetime.datetime.now(datetime.timezone.utc)
res = {"ts": now.isoformat(), "status": "fail", "reason": "init", "checks": {}}
def load(p):
with open(p, "r", encoding="utf-8") as f:
return json.load(f)
try:
state = load(STATE_PATH)
dod = load(DOD_PATH)
res["checks"]["state_exists"] = True
res["checks"]["dod_exists"] = True
missing = []
for k in ["updatedAt", "status"]:
if k not in state:
missing.append(k)
res["checks"]["state_fields_ok"] = len(missing) == 0
res["checks"]["state_missing_fields"] = missing
fresh = False
try:
t = datetime.datetime.fromisoformat(str(state.get("updatedAt")).replace("Z", "+00:00"))
fresh = (now - t).total_seconds() <= 1200
except Exception:
fresh = False
res["checks"]["fresh_20m"] = fresh
text = json.dumps(dod, ensure_ascii=False)
done_all = "A1" in text and "E8" in text and (
"all_pass" in text or "allPassed" in text or "全达标" in text
)
res["checks"]["dod_all_pass_hint"] = done_all
st = str(state.get("status", "")).upper()
if st in ["ALERT", "FAIL", "STALE"]:
res["status"] = "alert"
res["reason"] = f"state_status={st}"
elif not fresh:
res["status"] = "alert"
res["reason"] = "state_stale"
else:
res["status"] = "ok"
res["reason"] = "healthy"
except FileNotFoundError as e:
res["status"] = "fail"
res["reason"] = f"missing_file:{e.filename}"
except Exception as e:
res["status"] = "fail"
res["reason"] = f"exception:{e}"
OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
with open(OUTPUT_PATH, "w", encoding="utf-8") as f:
json.dump(res, f, ensure_ascii=False, indent=2)
print(json.dumps(res, ensure_ascii=False))
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
import datetime
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
BASE = ROOT / "runtime" / "evidence"
REGRESSION_PATH = BASE / "regression-status.json"
SNAPSHOT_PATH = BASE / "mission-control-jobs-v2-snapshot.json"
OUTPUT_PATH = BASE / "rollback-plan.json"
now = datetime.datetime.now(datetime.timezone.utc).isoformat()
plan = {"ts": now, "canRollback": False, "reason": "", "jobs": [], "commands": []}
try:
with open(REGRESSION_PATH, "r", encoding="utf-8") as regression_file:
reg = json.load(regression_file)
with open(SNAPSHOT_PATH, "r", encoding="utf-8") as snapshot_file:
snap = json.load(snapshot_file)
status = str(reg.get("status", "")).lower()
if status in ["fail", "alert"]:
jobs = snap.get("jobs", [])
plan["canRollback"] = True
plan["reason"] = f"regression_{status}"
for j in jobs:
jid = j.get("id")
plan["jobs"].append(jid)
plan["commands"].append(f"openclaw cron update {jid} --json '<snapshot-payload>'")
else:
plan["reason"] = "regression_not_triggered"
except Exception as e:
plan["reason"] = f"error:{e}"
BASE.mkdir(parents=True, exist_ok=True)
with open(OUTPUT_PATH, "w", encoding="utf-8") as output_file:
json.dump(plan, output_file, ensure_ascii=False, indent=2)
print(json.dumps(plan, ensure_ascii=False))
+76
View File
@@ -0,0 +1,76 @@
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { buildHealthSnapshot } from "./health-snapshot";
interface SnapshotPointer {
updatedAt: string;
snapshotId: string;
latest: string;
}
function buildSnapshotId(now = new Date()): string {
const yyyy = now.getUTCFullYear();
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
const dd = String(now.getUTCDate()).padStart(2, "0");
const hh = String(now.getUTCHours()).padStart(2, "0");
const min = String(now.getUTCMinutes()).padStart(2, "0");
const sec = String(now.getUTCSeconds()).padStart(2, "0");
return `${yyyy}${mm}${dd}-${hh}${min}${sec}Z`;
}
async function writeJsonAtomic(path: string, value: unknown): Promise<void> {
await mkdir(dirname(path), { recursive: true });
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
await rename(tmp, path);
}
async function readJson<T>(path: string): Promise<T> {
const raw = await readFile(path, "utf8");
return JSON.parse(raw) as T;
}
async function main(): Promise<void> {
const runtimeDir = resolve(process.argv[2] ?? join(process.cwd(), "runtime"));
const healthDir = resolve(runtimeDir, "health");
const evidenceDir = resolve(runtimeDir, "evidence");
const evidenceLatestPath = resolve(evidenceDir, "latest.json");
const now = new Date();
const snapshotId = buildSnapshotId(now);
const healthSnapshotPath = resolve(healthDir, "snapshots", `${snapshotId}.json`);
const evidenceSnapshotPath = resolve(evidenceDir, "snapshots", `${snapshotId}.json`);
const healthLatestPath = resolve(healthDir, "latest.json");
const healthPointerPath = resolve(healthDir, "latest.pointer.json");
const evidencePointerPath = resolve(evidenceDir, "latest.pointer.json");
const healthSnapshot = await buildHealthSnapshot(runtimeDir);
const evidenceSnapshot = await readJson<Record<string, unknown>>(evidenceLatestPath);
const healthPointer: SnapshotPointer = {
updatedAt: now.toISOString(),
snapshotId,
latest: healthSnapshotPath,
};
const evidencePointer: SnapshotPointer & { sourceLatest: string } = {
updatedAt: now.toISOString(),
snapshotId,
latest: evidenceSnapshotPath,
sourceLatest: evidenceLatestPath,
};
await writeJsonAtomic(healthSnapshotPath, healthSnapshot);
await writeJsonAtomic(evidenceSnapshotPath, evidenceSnapshot);
await writeJsonAtomic(healthLatestPath, healthSnapshot);
await writeJsonAtomic(healthPointerPath, healthPointer);
await writeJsonAtomic(evidencePointerPath, evidencePointer);
console.log(
`PERIODIC_SNAPSHOT_OK id=${snapshotId} health=${healthSnapshotPath} evidence=${evidenceSnapshotPath}`,
);
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
FILES=()
while IFS= read -r file; do
FILES+=("$file")
done < <(
rg --files --hidden \
-g '!.git/**' \
-g '!node_modules/**' \
-g '!dist/**' \
-g '!runtime/**' \
-g '!coverage/**' \
-g '!scripts/release-audit.sh'
)
if [ "${#FILES[@]}" -eq 0 ]; then
echo "release-audit: no source files found" >&2
exit 1
fi
check_no_match() {
local description="$1"
local pattern="$2"
if rg -n --no-heading -P -e "$pattern" "${FILES[@]}" >/tmp/release-audit-match.txt 2>/dev/null; then
echo "release-audit: failed: ${description}" >&2
cat /tmp/release-audit-match.txt >&2
rm -f /tmp/release-audit-match.txt
exit 1
fi
rm -f /tmp/release-audit-match.txt
}
check_exists() {
local file="$1"
if [ ! -f "$file" ]; then
echo "release-audit: missing required file: $file" >&2
exit 1
fi
}
check_exists "README.md"
check_exists "LICENSE"
check_exists ".gitignore"
check_exists ".env.example"
check_exists "package.json"
check_no_match "absolute macOS home paths" '/Users/[^/]+/'
check_no_match "absolute Linux home paths" '/home/[^/]+/'
check_no_match "hard-coded internal channel ids" '1477617216529760378'
check_no_match "obvious OpenAI-style secret keys" 'sk-[A-Za-z0-9]{20,}'
check_no_match "hard-coded bearer tokens" 'Authorization: Bearer (?!<)[^[:space:]]+'
check_no_match "hard-coded local API tokens" 'LOCAL_API_TOKEN=(?!<)[^[:space:]]+'
check_no_match "hard-coded x-local-token header values" 'x-local-token:[[:space:]]*(?!<)[^[:space:]]+'
if [ -d ".git" ]; then
if git ls-files | rg '^(runtime|dist|node_modules|coverage|plans|workflows)/' >/tmp/release-audit-match.txt 2>/dev/null; then
echo "release-audit: failed: ignored build/runtime/internal-only paths are tracked" >&2
cat /tmp/release-audit-match.txt >&2
rm -f /tmp/release-audit-match.txt
exit 1
fi
rm -f /tmp/release-audit-match.txt
fi
echo "release-audit: passed"
+306
View File
@@ -0,0 +1,306 @@
import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { spawn } from "node:child_process";
interface RunLock {
owner: string;
leaseUntil: string;
}
interface WorkerHeartbeat {
generatedAt: string;
pid?: number;
state?: string;
}
interface SupervisorReport {
generatedAt: string;
status: "healthy" | "restarted" | "failed" | "done";
reason: string;
retries: number;
maxRetries: number;
observed: {
heartbeatAgeMs?: number;
heartbeatState?: string;
workerPid?: number;
workerDead?: boolean;
};
actions: string[];
}
function isExpired(lock: RunLock): boolean {
const expiry = Date.parse(lock.leaseUntil);
if (Number.isNaN(expiry)) return true;
return expiry <= Date.now();
}
async function readLock(path: string): Promise<RunLock | null> {
try {
const raw = await readFile(path, "utf8");
return JSON.parse(raw) as RunLock;
} catch {
return null;
}
}
async function readHeartbeat(path: string): Promise<WorkerHeartbeat | null> {
try {
const raw = await readFile(path, "utf8");
return JSON.parse(raw) as WorkerHeartbeat;
} catch {
return null;
}
}
async function ageMs(path: string): Promise<number | undefined> {
try {
const fileStat = await stat(path);
return Math.max(0, Date.now() - fileStat.mtimeMs);
} catch {
return undefined;
}
}
function isPidAlive(pid: number): boolean {
if (!Number.isInteger(pid) || pid <= 0) return false;
try {
process.kill(pid, 0);
return true;
} catch (error) {
const code = (error as NodeJS.ErrnoException)?.code;
if (code === "EPERM") return true;
return false;
}
}
async function writeJsonAtomic(path: string, value: unknown): Promise<void> {
await mkdir(dirname(path), { recursive: true });
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
await rename(tmp, path);
}
async function appendAudit(path: string, line: string): Promise<void> {
await mkdir(dirname(path), { recursive: true });
let existing = "";
try {
existing = await readFile(path, "utf8");
} catch {
existing = "";
}
await writeFile(path, `${existing}${line}\n`, "utf8");
}
async function runNodeScript(
scriptPath: string,
args: string[],
env: NodeJS.ProcessEnv = process.env,
): Promise<{ code: number | null; stdout: string; stderr: string }> {
return await new Promise((resolvePromise, rejectPromise) => {
const child = spawn(process.execPath, ["--import", "tsx", scriptPath, ...args], {
cwd: process.cwd(),
env,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.on("data", (chunk) => {
stdout += String(chunk);
});
child.stderr.on("data", (chunk) => {
stderr += String(chunk);
});
child.on("error", rejectPromise);
child.on("close", (code) => resolvePromise({ code, stdout, stderr }));
});
}
async function runLockCommand(action: "acquire" | "renew" | "release", lockPath: string, owner: string): Promise<void> {
const result = await runNodeScript(resolve(process.cwd(), "scripts", "run-lock.ts"), [action, lockPath], {
...process.env,
RUN_OWNER: owner,
});
if (result.code !== 0) {
throw new Error(result.stderr.trim() || result.stdout.trim() || `run-lock ${action} failed`);
}
}
async function runDoDCheck(runtimeDir: string, statusPath: string): Promise<boolean> {
const result = await runNodeScript(resolve(process.cwd(), "scripts", "dod-check.ts"), [runtimeDir, statusPath]);
return result.code === 0;
}
async function startWorker(
workerEntry: string,
runtimeDir: string,
mode: "detached" | "sync",
): Promise<{ ok: boolean; detail: string }> {
if (mode === "sync") {
const run = await runNodeScript(resolve(workerEntry), [runtimeDir], process.env);
if (run.code !== 0) {
const combined = `${run.stdout}\n${run.stderr}`;
if (combined.includes("WORKER_MAX_CYCLES_REACHED")) {
return { ok: true, detail: "worker restarted (sync bounded cycles)" };
}
return { ok: false, detail: run.stderr.trim() || run.stdout.trim() || "worker restart failed" };
}
return { ok: true, detail: "worker restarted (sync)" };
}
return await new Promise((resolvePromise) => {
const child = spawn(process.execPath, ["--import", "tsx", resolve(workerEntry), runtimeDir], {
cwd: process.cwd(),
env: process.env,
detached: true,
stdio: "ignore",
});
child.on("error", (error) => {
resolvePromise({ ok: false, detail: error instanceof Error ? error.message : String(error) });
});
child.unref();
resolvePromise({ ok: true, detail: "worker restarted (detached)" });
});
}
async function sleep(ms: number): Promise<void> {
await new Promise((resolvePromise) => {
setTimeout(resolvePromise, ms);
});
}
async function main(): Promise<void> {
const runtimeDir = resolve(process.argv[2] ?? join(process.cwd(), "runtime"));
const owner = process.env.RUN_OWNER ?? "pandas-main";
const once = (process.env.SUPERVISOR_ONCE ?? "false").toLowerCase() === "true";
const pollMs = Number(process.env.SUPERVISOR_POLL_MS ?? 15_000);
const maxRetries = Number(process.env.SUPERVISOR_MAX_RESTARTS ?? 3);
const heartbeatMaxAgeMs = Number(process.env.SUPERVISOR_HEARTBEAT_MAX_AGE_MS ?? 120_000);
const restartMode = (process.env.SUPERVISOR_RESTART_MODE ?? "detached") === "sync" ? "sync" : "detached";
const workerEntry = resolve(process.env.SUPERVISOR_WORKER_ENTRY ?? join(process.cwd(), "scripts", "resident-worker.ts"));
const lockPath = resolve(runtimeDir, "run.lock");
const heartbeatPath = resolve(runtimeDir, "health", "worker-heartbeat.json");
const dodStatusPath = resolve(runtimeDir, "evidence", "dod-status.json");
const supervisorLockPath = resolve(runtimeDir, "health", "supervisor-lock.json");
const existingSupervisor = await readHeartbeat(supervisorLockPath);
const existingPid = existingSupervisor?.pid;
if (existingPid && existingPid !== process.pid && isPidAlive(existingPid)) {
console.log(`SUPERVISOR_ALREADY_RUNNING pid=${existingPid}`);
process.exit(0);
}
await writeJsonAtomic(supervisorLockPath, {
generatedAt: new Date().toISOString(),
pid: process.pid,
state: "running",
});
const reportPath = resolve(runtimeDir, "recovery", "supervisor-latest.json");
const auditPath = resolve(runtimeDir, "recovery", "supervisor-audit.log");
let retries = 0;
while (true) {
const actions: string[] = [];
const heartbeat = await readHeartbeat(heartbeatPath);
const heartbeatAgeMs = await ageMs(heartbeatPath);
const workerPid = heartbeat?.pid;
const workerDead = workerPid !== undefined ? !isPidAlive(workerPid) : false;
const stale = heartbeatAgeMs === undefined || heartbeatAgeMs > heartbeatMaxAgeMs;
const dead = workerDead;
let status: SupervisorReport["status"] = "healthy";
let reason = stale ? "worker-heartbeat-stale" : "worker-healthy";
if (heartbeat?.state === "done") {
const dodPassed = await runDoDCheck(runtimeDir, dodStatusPath);
actions.push(`dod-check-${dodPassed ? "pass" : "fail"}`);
if (dodPassed) {
status = "done";
reason = "dod-pass";
} else {
reason = "worker-done-but-dod-failed";
}
}
if (status !== "done" && (stale || dead)) {
if (retries >= maxRetries) {
status = "failed";
reason = "max-retries-exhausted";
actions.push("restart-skipped-max-retries");
} else {
const lock = await readLock(lockPath);
if (lock && !isExpired(lock) && lock.owner !== owner) {
status = "failed";
reason = `lock-conflict owner=${lock.owner}`;
actions.push("restart-blocked-lock-conflict");
} else {
if (lock && !isExpired(lock) && lock.owner === owner) {
await runLockCommand("release", lockPath, owner).catch(() => {
// best-effort release before restart
});
actions.push("released-existing-owner-lock");
}
await runLockCommand("acquire", lockPath, owner).catch(() => {
// worker can also acquire after restart; keep attempting restart.
});
actions.push("acquired-supervisor-lock");
const started = await startWorker(workerEntry, runtimeDir, restartMode);
if (!started.ok) {
retries += 1;
status = retries >= maxRetries ? "failed" : "healthy";
reason = `restart-failed: ${started.detail}`;
actions.push("restart-worker-failed");
} else {
retries += 1;
status = "restarted";
reason = stale && dead ? "worker-stale-and-dead" : stale ? "worker-stale" : "worker-dead";
actions.push("restart-worker");
}
}
}
}
const report: SupervisorReport = {
generatedAt: new Date().toISOString(),
status,
reason,
retries,
maxRetries,
observed: {
heartbeatAgeMs,
heartbeatState: heartbeat?.state,
workerPid,
workerDead,
},
actions,
};
await writeJsonAtomic(reportPath, report);
await appendAudit(
auditPath,
`${report.generatedAt} | ${status} | ${reason} | retries=${retries}/${maxRetries} | actions=${actions.join(",") || "none"}`,
);
if (status === "failed") {
console.error(`SUPERVISOR_FAILED report=${reportPath}`);
process.exit(1);
}
if (status === "done") {
console.log(`SUPERVISOR_DONE report=${reportPath}`);
process.exit(0);
}
if (once) {
console.log(`SUPERVISOR_ONCE status=${status} report=${reportPath}`);
process.exit(0);
}
await sleep(pollMs);
}
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
+322
View File
@@ -0,0 +1,322 @@
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { randomUUID } from "node:crypto";
import { spawn } from "node:child_process";
interface RunLock {
runId: string;
owner: string;
leaseUntil: string;
}
interface WorkerProgressArtifact {
generatedAt: string;
runId: string;
owner: string;
cycle: number;
state: "running" | "waiting" | "done";
lastStep: {
id: string;
command: string;
startedAt: string;
finishedAt: string;
exitCode: number | null;
stdoutTail: string;
stderrTail: string;
};
dod: {
passed: boolean;
statusPath: string;
};
}
interface WorkerHeartbeatArtifact {
generatedAt: string;
runId: string;
owner: string;
pid: number;
cycle: number;
state: "running" | "waiting" | "done";
stepId: string;
stepExitCode: number | null;
dodPassed: boolean;
}
function getNpmCommand(): string {
return process.platform === "win32" ? "npm.cmd" : "npm";
}
function parseSteps(raw: string): string[] {
const steps = raw
.split(",")
.map((value) => value.trim())
.filter(Boolean);
return steps.length > 0 ? steps : ["goal-gate"];
}
function getStepCommand(
stepId: string,
runtimeDir: string,
): { command: string; args: string[]; label: string } {
const npm = getNpmCommand();
if (stepId === "build") return { command: npm, args: ["run", "build"], label: "npm run build" };
if (stepId === "test") return { command: npm, args: ["test"], label: "npm test" };
if (stepId === "evidence-report") {
return { command: npm, args: ["run", "evidence:report"], label: "npm run evidence:report" };
}
if (stepId === "goal-gate") {
return {
command: process.execPath,
args: ["--import", "tsx", resolve(process.cwd(), "scripts", "goal-gate.ts"), resolve(runtimeDir, "goal-state.json")],
label: "goal-gate",
};
}
if (stepId === "evidence-validate") {
return {
command: process.execPath,
args: [
"--import",
"tsx",
resolve(process.cwd(), "scripts", "evidence-gate.ts"),
"validate",
resolve(runtimeDir, "evidence", "latest.json"),
],
label: "evidence-validate",
};
}
if (stepId === "health-snapshot") {
return {
command: process.execPath,
args: ["--import", "tsx", resolve(process.cwd(), "scripts", "health-snapshot.ts")],
label: "health-snapshot",
};
}
if (stepId === "codex-patch") {
const prompt = process.env.WORKER_CODE_PROMPT ??
"Improve Mission Control product DoD gaps: full Chinese coverage, non-technical copy, middle-school readability, and global visibility for cron/heartbeat/current tasks/tool calls. Make minimal safe code changes.";
return {
command: "codex",
args: ["exec", "--full-auto", prompt],
label: "codex exec --full-auto",
};
}
throw new Error(`unknown worker step: ${stepId}`);
}
function tail(value: string, maxChars = 2000): string {
if (value.length <= maxChars) return value;
return value.slice(value.length - maxChars);
}
async function writeJsonAtomic(path: string, value: unknown): Promise<void> {
await mkdir(dirname(path), { recursive: true });
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
await rename(tmp, path);
}
async function runCommand(
command: string,
args: string[],
env: NodeJS.ProcessEnv = process.env,
): Promise<{ code: number | null; stdout: string; stderr: string }> {
return await new Promise((resolvePromise, rejectPromise) => {
const child = spawn(command, args, {
cwd: process.cwd(),
env,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.on("data", (chunk) => {
stdout += String(chunk);
});
child.stderr.on("data", (chunk) => {
stderr += String(chunk);
});
child.on("error", rejectPromise);
child.on("close", (code) => resolvePromise({ code, stdout, stderr }));
});
}
async function readLock(path: string): Promise<RunLock | null> {
try {
const raw = await readFile(path, "utf8");
return JSON.parse(raw) as RunLock;
} catch {
return null;
}
}
function isExpired(lock: RunLock): boolean {
const expiry = Date.parse(lock.leaseUntil);
if (Number.isNaN(expiry)) return true;
return expiry <= Date.now();
}
async function runLockCommand(action: "acquire" | "renew" | "release", lockPath: string, owner: string): Promise<void> {
const result = await runCommand(
process.execPath,
["--import", "tsx", resolve(process.cwd(), "scripts", "run-lock.ts"), action, lockPath],
{ ...process.env, RUN_OWNER: owner },
);
if (result.code !== 0) {
throw new Error(result.stderr.trim() || result.stdout.trim() || `run-lock ${action} failed`);
}
}
async function ensureOwnedLock(lockPath: string, owner: string): Promise<void> {
const existing = await readLock(lockPath);
if (existing && !isExpired(existing) && existing.owner !== owner) {
throw new Error(`WORKER_LOCK_CONFLICT expected=${owner} current=${existing.owner} leaseUntil=${existing.leaseUntil}`);
}
if (!existing || isExpired(existing)) {
await runLockCommand("acquire", lockPath, owner);
return;
}
await runLockCommand("renew", lockPath, owner);
}
async function runDoDCheck(runtimeDir: string, statusPath: string): Promise<boolean> {
const result = await runCommand(process.execPath, [
"--import",
"tsx",
resolve(process.cwd(), "scripts", "dod-check.ts"),
runtimeDir,
statusPath,
]);
return result.code === 0;
}
async function sleep(ms: number): Promise<void> {
await new Promise((resolvePromise) => {
setTimeout(resolvePromise, ms);
});
}
async function main(): Promise<void> {
const runtimeDir = resolve(process.argv[2] ?? join(process.cwd(), "runtime"));
const owner = process.env.RUN_OWNER ?? "pandas-main";
const steps = parseSteps(process.env.WORKER_STEPS ?? "codex-patch,test,evidence-report,health-snapshot");
const intervalMs = Number(process.env.WORKER_INTERVAL_MS ?? 15_000);
const maxCycles = Number(process.env.WORKER_MAX_CYCLES ?? 0);
const lockPath = resolve(runtimeDir, "run.lock");
const heartbeatPath = resolve(runtimeDir, "health", "worker-heartbeat.json");
const progressPath = resolve(runtimeDir, "evidence", "worker-progress.json");
const dodStatusPath = resolve(runtimeDir, "evidence", "dod-status.json");
const runId = randomUUID();
let cycle = 0;
try {
await ensureOwnedLock(lockPath, owner);
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
}
while (true) {
cycle += 1;
const stepId = steps[(cycle - 1) % steps.length];
const step = getStepCommand(stepId, runtimeDir);
const stepStartedAt = new Date().toISOString();
const runningHeartbeat: WorkerHeartbeatArtifact = {
generatedAt: new Date().toISOString(),
runId,
owner,
pid: process.pid,
cycle,
state: "running",
stepId,
stepExitCode: null,
dodPassed: false,
};
await writeJsonAtomic(heartbeatPath, runningHeartbeat);
let stepResult: { code: number | null; stdout: string; stderr: string };
try {
stepResult = await runCommand(step.command, step.args);
} catch (error) {
stepResult = { code: 1, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
}
const stepFinishedAt = new Date().toISOString();
try {
await ensureOwnedLock(lockPath, owner);
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
}
const interimProgress: WorkerProgressArtifact = {
generatedAt: new Date().toISOString(),
runId,
owner,
cycle,
state: "waiting",
lastStep: {
id: stepId,
command: step.label,
startedAt: stepStartedAt,
finishedAt: stepFinishedAt,
exitCode: stepResult.code,
stdoutTail: tail(stepResult.stdout),
stderrTail: tail(stepResult.stderr),
},
dod: {
passed: false,
statusPath: dodStatusPath,
},
};
await writeJsonAtomic(progressPath, interimProgress);
const dodPassed = await runDoDCheck(runtimeDir, dodStatusPath);
const state: WorkerProgressArtifact["state"] = dodPassed ? "done" : "waiting";
const progress: WorkerProgressArtifact = {
...interimProgress,
generatedAt: new Date().toISOString(),
state,
dod: {
passed: dodPassed,
statusPath: dodStatusPath,
},
};
const heartbeat: WorkerHeartbeatArtifact = {
generatedAt: new Date().toISOString(),
runId,
owner,
pid: process.pid,
cycle,
state,
stepId,
stepExitCode: stepResult.code,
dodPassed,
};
await writeJsonAtomic(progressPath, progress);
await writeJsonAtomic(heartbeatPath, heartbeat);
if (dodPassed) {
await runLockCommand("release", lockPath, owner).catch(() => {
// Keep done outcome even if release fails; lock lease expiry is still a safe fallback.
});
console.log(`WORKER_DONE cycles=${cycle} dodStatus=${dodStatusPath}`);
process.exit(0);
}
if (maxCycles > 0 && cycle >= maxCycles) {
console.error(`WORKER_MAX_CYCLES_REACHED cycles=${cycle}`);
process.exit(1);
}
await sleep(intervalMs);
}
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
+143
View File
@@ -0,0 +1,143 @@
import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { randomUUID } from "node:crypto";
interface RunLock {
runId: string;
owner: string;
startedAt: string;
updatedAt: string;
leaseUntil: string;
fencingToken: number;
pid?: number;
}
const runtimeDir = resolve(process.cwd(), "runtime");
const lockPath = resolve(process.argv[3] ?? join(runtimeDir, "run.lock"));
const cmd = (process.argv[2] ?? "status").toLowerCase();
const owner = process.env.RUN_OWNER || "pandas-main";
const leaseMs = Number(process.env.RUN_LEASE_MS ?? 10 * 60 * 1000);
async function readLock(path: string): Promise<RunLock | null> {
try {
const raw = await readFile(path, "utf8");
return JSON.parse(raw) as RunLock;
} catch {
return null;
}
}
function isExpired(lock: RunLock, now = Date.now()): boolean {
const expiry = Date.parse(lock.leaseUntil);
if (Number.isNaN(expiry)) return true;
return expiry <= now;
}
async function writeLock(path: string, lock: RunLock): Promise<void> {
await mkdir(dirname(path), { recursive: true });
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmp, `${JSON.stringify(lock, null, 2)}\n`, "utf8");
await rename(tmp, path);
}
async function acquire(path: string): Promise<number> {
const now = Date.now();
const current = await readLock(path);
if (current && !isExpired(current, now)) {
console.error(`LOCKED runId=${current.runId} owner=${current.owner} leaseUntil=${current.leaseUntil}`);
return 1;
}
const nextToken = (current?.fencingToken ?? 0) + 1;
const runId = randomUUID();
const lock: RunLock = {
runId,
owner,
startedAt: new Date(now).toISOString(),
updatedAt: new Date(now).toISOString(),
leaseUntil: new Date(now + leaseMs).toISOString(),
fencingToken: nextToken,
pid: process.pid,
};
await writeLock(path, lock);
console.log(`ACQUIRED runId=${lock.runId} fencingToken=${lock.fencingToken} leaseUntil=${lock.leaseUntil}`);
return 0;
}
async function renew(path: string): Promise<number> {
const now = Date.now();
const lock = await readLock(path);
if (!lock) {
console.error("MISSING_LOCK");
return 1;
}
if (isExpired(lock, now)) {
console.error(`EXPIRED runId=${lock.runId}`);
return 1;
}
if (lock.owner !== owner) {
console.error(`NOT_OWNER expected=${owner} current=${lock.owner}`);
return 1;
}
const next: RunLock = {
...lock,
updatedAt: new Date(now).toISOString(),
leaseUntil: new Date(now + leaseMs).toISOString(),
pid: process.pid,
};
await writeLock(path, next);
console.log(`RENEWED runId=${next.runId} leaseUntil=${next.leaseUntil}`);
return 0;
}
async function release(path: string): Promise<number> {
const lock = await readLock(path);
if (!lock) {
console.log("RELEASED noop=no-lock");
return 0;
}
if (lock.owner !== owner && !isExpired(lock)) {
console.error(`NOT_OWNER expected=${owner} current=${lock.owner}`);
return 1;
}
await rm(path, { force: true });
console.log(`RELEASED runId=${lock.runId}`);
return 0;
}
async function status(path: string): Promise<number> {
const lock = await readLock(path);
if (!lock) {
console.log("STATUS unlocked");
return 0;
}
const expired = isExpired(lock);
console.log(`STATUS ${expired ? "expired" : "locked"} runId=${lock.runId} owner=${lock.owner} leaseUntil=${lock.leaseUntil} fencingToken=${lock.fencingToken}`);
return expired ? 2 : 0;
}
async function main(): Promise<void> {
// quick sanity: parent runtime dir should be writable once
await mkdir(dirname(lockPath), { recursive: true });
try {
await stat(dirname(lockPath));
} catch {
// ignore
}
let code = 0;
if (cmd === "acquire") code = await acquire(lockPath);
else if (cmd === "renew") code = await renew(lockPath);
else if (cmd === "release") code = await release(lockPath);
else code = await status(lockPath);
process.exit(code);
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
OUT="$ROOT/runtime/evidence/verifier-status.json"
TMP="$OUT.tmp"
cd "$ROOT"
./scripts/mc_dod_evaluator.py > "$TMP"
python3 - "$TMP" "$OUT" <<'PY2'
import datetime
import json
import pathlib
import sys
tmp_path = pathlib.Path(sys.argv[1])
out_path = pathlib.Path(sys.argv[2])
raw = tmp_path.read_text(encoding="utf-8").strip()
data = json.loads(raw) if raw else {}
if not isinstance(data, dict):
raise SystemExit("Verifier must output a JSON object")
data.setdefault("ok", bool(data.get("status") == "DONE"))
data.setdefault("status", data.get("status", "UNKNOWN"))
data.setdefault("checkedItems", [])
data.setdefault("failedChecks", [])
data.setdefault("evidence", [])
data.setdefault("nextRecommendedItem", "")
data["updatedAt"] = datetime.datetime.now(datetime.timezone.utc).isoformat()
out_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(json.dumps(data, ensure_ascii=False))
tmp_path.unlink(missing_ok=True)
PY2
+109
View File
@@ -0,0 +1,109 @@
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { spawn } from "node:child_process";
type HealStatus = "healthy" | "stalled" | "healed" | "failed";
interface HealReport {
generatedAt: string;
status: HealStatus;
reason: string;
thresholds: {
evidenceMaxAgeMs: number;
lockMaxAgeMs: number;
};
observed: {
evidenceAgeMs?: number;
lockAgeMs?: number;
};
actions: string[];
}
const runtimeDir = resolve(process.cwd(), "runtime");
const evidencePath = resolve(process.argv[2] ?? join(runtimeDir, "evidence", "latest.json"));
const lockPath = resolve(process.argv[3] ?? join(runtimeDir, "run.lock"));
const reportPath = resolve(process.argv[4] ?? join(runtimeDir, "recovery", "latest.json"));
const auditPath = resolve(process.argv[5] ?? join(dirname(reportPath), "audit.log"));
const evidenceMaxAgeMs = Number(process.env.STALL_EVIDENCE_MAX_AGE_MS ?? 30 * 60 * 1000);
const lockMaxAgeMs = Number(process.env.STALL_LOCK_MAX_AGE_MS ?? 30 * 60 * 1000);
const owner = process.env.RUN_OWNER ?? "pandas-main";
async function mtimeAgeMs(path: string): Promise<number | undefined> {
try {
const s = await stat(path);
return Date.now() - s.mtimeMs;
} catch {
return undefined;
}
}
function runReleaseLock(): Promise<boolean> {
return new Promise((resolvePromise) => {
const child = spawn("npm", ["run", "lock:release"], {
cwd: process.cwd(),
env: { ...process.env, RUN_OWNER: owner },
stdio: "ignore",
});
child.on("exit", (code) => resolvePromise(code === 0));
});
}
async function appendRecoveryAudit(line: string): Promise<void> {
await mkdir(dirname(auditPath), { recursive: true });
let old = "";
try {
old = await readFile(auditPath, "utf8");
} catch {
old = "";
}
await writeFile(auditPath, `${old}${line}\n`, "utf8");
}
async function main(): Promise<void> {
const evidenceAgeMs = await mtimeAgeMs(evidencePath);
const lockAgeMs = await mtimeAgeMs(lockPath);
const stalledByEvidence = evidenceAgeMs === undefined || evidenceAgeMs > evidenceMaxAgeMs;
const stalledByLock = lockAgeMs !== undefined && lockAgeMs > lockMaxAgeMs;
const actions: string[] = [];
let status: HealStatus = "healthy";
let reason = "within-threshold";
if (stalledByEvidence || stalledByLock) {
status = "stalled";
reason = `${stalledByEvidence ? "evidence-stale" : ""}${stalledByEvidence && stalledByLock ? "+" : ""}${stalledByLock ? "lock-stale" : ""}`;
const released = await runReleaseLock();
actions.push(released ? "release-lock-ok" : "release-lock-failed");
if (released) {
status = "healed";
actions.push("restart-chain-required");
} else {
status = "failed";
}
}
const report: HealReport = {
generatedAt: new Date().toISOString(),
status,
reason,
thresholds: { evidenceMaxAgeMs, lockMaxAgeMs },
observed: { evidenceAgeMs, lockAgeMs },
actions,
};
await mkdir(dirname(reportPath), { recursive: true });
await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
await appendRecoveryAudit(`${report.generatedAt} | ${report.status} | ${report.reason} | actions=${actions.join(",") || "none"}`);
console.log(`AUTO_HEAL_${report.status.toUpperCase()} reason=${report.reason} actions=${actions.join(",") || "none"}`);
process.exit(report.status === "failed" ? 1 : 0);
}
main().catch((err) => {
console.error(err instanceof Error ? err.message : String(err));
process.exit(1);
});
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
PORT="${UI_SMOKE_PORT:-4516}"
WAIT_SECONDS="${UI_SMOKE_WAIT_SECONDS:-10}"
LOG_DIR="$ROOT/runtime"
LOG_FILE="$LOG_DIR/ui-smoke-${PORT}.log"
mkdir -p "$LOG_DIR"
cd "$ROOT"
UI_MODE=true UI_PORT="$PORT" npm run dev >"$LOG_FILE" 2>&1 &
PID=$!
cleanup() {
kill "$PID" >/dev/null 2>&1 || true
wait "$PID" 2>/dev/null || true
}
trap cleanup EXIT
wait_for_ui() {
local deadline=$((SECONDS + WAIT_SECONDS))
while true; do
if curl -fsS "http://127.0.0.1:${PORT}/" >/dev/null 2>&1; then
return 0
fi
if (( SECONDS >= deadline )); then
echo "UI smoke failed: server did not become ready within ${WAIT_SECONDS}s." >&2
cat "$LOG_FILE" >&2 || true
return 1
fi
sleep 1
done
}
wait_for_ui
curl -fsS "http://127.0.0.1:${PORT}/" | grep -E "Mission Control|Office|Usage|Language:" >/dev/null
curl -fsS "http://127.0.0.1:${PORT}/docs?lang=en" | grep -E "Open document workbench|Mission Control|Docs" >/dev/null
printf 'UI smoke passed on http://127.0.0.1:%s\n' "$PORT"
+108
View File
@@ -0,0 +1,108 @@
import assert from "node:assert/strict";
import { computeBudgetSummary } from "../src/runtime/budget-governance";
import type {
ProjectStoreSnapshot,
SessionStatusSnapshot,
SessionSummary,
TaskStoreSnapshot,
} from "../src/types";
function main(): void {
const sessions: SessionSummary[] = [
{ sessionKey: "s1", agentId: "main", state: "running" },
{ sessionKey: "s2", agentId: "main", state: "running" },
{ sessionKey: "s3", agentId: "worker", state: "idle" },
];
const statuses: SessionStatusSnapshot[] = [
{
sessionKey: "s1",
tokensIn: 110,
tokensOut: 30,
cost: 5,
updatedAt: "2026-03-01T00:00:00.000Z",
},
{
sessionKey: "s2",
tokensIn: 15,
tokensOut: 10,
cost: 1,
updatedAt: "2026-03-01T00:00:00.000Z",
},
{
sessionKey: "s3",
tokensIn: 20,
tokensOut: 5,
cost: 0.5,
updatedAt: "2026-03-01T00:00:00.000Z",
},
];
const tasks: TaskStoreSnapshot = {
tasks: [
{
projectId: "proj-1",
taskId: "task-over",
title: "Task Over",
status: "in_progress",
owner: "main",
definitionOfDone: [],
artifacts: [],
rollback: { strategy: "manual", steps: [] },
sessionKeys: ["s1"],
budget: { tokensIn: 100, warnRatio: 0.8 },
updatedAt: "2026-03-01T00:00:00.000Z",
},
{
projectId: "proj-1",
taskId: "task-ok",
title: "Task Ok",
status: "todo",
owner: "main",
definitionOfDone: [],
artifacts: [],
rollback: { strategy: "manual", steps: [] },
sessionKeys: ["s2"],
budget: { totalTokens: 100, warnRatio: 0.8 },
updatedAt: "2026-03-01T00:00:00.000Z",
},
],
agentBudgets: [
{
agentId: "main",
thresholds: { totalTokens: 200, warnRatio: 0.8 },
},
],
updatedAt: "2026-03-01T00:00:00.000Z",
};
const projects: ProjectStoreSnapshot = {
projects: [
{
projectId: "proj-1",
title: "Project 1",
status: "active",
owner: "main",
budget: { totalTokens: 120, warnRatio: 0.8 },
updatedAt: "2026-03-01T00:00:00.000Z",
},
],
updatedAt: "2026-03-01T00:00:00.000Z",
};
const summary = computeBudgetSummary(sessions, statuses, tasks, projects);
assert.equal(summary.total, 4);
assert.equal(summary.ok, 1);
assert.equal(summary.warn, 1);
assert.equal(summary.over, 2);
const byId = new Map(summary.evaluations.map((item) => [`${item.scope}:${item.scopeId}`, item.status]));
assert.equal(byId.get("task:task-over"), "over");
assert.equal(byId.get("task:task-ok"), "ok");
assert.equal(byId.get("project:proj-1"), "over");
assert.equal(byId.get("agent:main"), "warn");
console.log("validate-budget-compute: ok");
}
main();
+106
View File
@@ -0,0 +1,106 @@
import assert from "node:assert/strict";
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
async function main(): Promise<void> {
const previousCwd = process.cwd();
const tempRoot = await mkdtemp(join(tmpdir(), "cc-task-store-"));
try {
process.chdir(tempRoot);
await mkdir(join(tempRoot, "runtime"), { recursive: true });
await writeFile(
join(tempRoot, "runtime", "tasks.json"),
JSON.stringify(
{
tasks: [],
agentBudgets: [],
updatedAt: "2026-03-01T00:00:00.000Z",
},
null,
2,
),
"utf8",
);
await writeFile(
join(tempRoot, "runtime", "projects.json"),
JSON.stringify(
{
projects: [
{
projectId: "p1",
title: "Project One",
status: "active",
owner: "ops",
budget: {},
updatedAt: "2026-03-01T00:00:00.000Z",
},
],
updatedAt: "2026-03-01T00:00:00.000Z",
},
null,
2,
),
"utf8",
);
const taskStore = await import("../src/runtime/task-store");
const created = await taskStore.createTask({
projectId: "p1",
taskId: "task-1",
title: "Ship endpoint",
owner: "main",
dueAt: "2026-03-03T10:00:00.000Z",
sessionKeys: ["agent:main:main"],
status: "todo",
});
assert.equal(created.projectId, "p1");
assert.equal(created.task.taskId, "task-1");
assert.equal(created.task.status, "todo");
assert.equal(created.task.dueAt, "2026-03-03T10:00:00.000Z");
const updated = await taskStore.updateTaskStatus({
taskId: "task-1",
projectId: "p1",
status: "blocked",
});
assert.equal(updated.task.status, "blocked");
const store = await taskStore.loadTaskStore();
const listed = taskStore.listTasks(store);
assert.equal(listed.length, 1);
assert.equal(listed[0].taskId, "task-1");
assert.equal(listed[0].status, "blocked");
await assert.rejects(
taskStore.createTask({
projectId: "missing",
taskId: "task-2",
title: "Invalid",
}),
(error: unknown) =>
error instanceof taskStore.TaskStoreValidationError &&
error.statusCode === 404,
);
await assert.rejects(
taskStore.updateTaskStatus({
taskId: "task-1",
projectId: "p1",
status: "bad-status",
}),
(error: unknown) =>
error instanceof taskStore.TaskStoreValidationError &&
error.statusCode === 400,
);
console.log("validate-task-store: ok");
} finally {
process.chdir(previousCwd);
await rm(tempRoot, { recursive: true, force: true });
}
}
void main();
+219
View File
@@ -0,0 +1,219 @@
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { spawn } from "node:child_process";
interface RunLock {
runId: string;
owner: string;
startedAt: string;
updatedAt: string;
leaseUntil: string;
fencingToken: number;
pid?: number;
}
interface RecoveryReport {
generatedAt: string;
status: "healthy" | "stalled" | "healed" | "failed";
reason: string;
thresholds: {
evidenceMaxAgeMs: number;
lockMaxAgeMs: number;
};
observed: {
evidenceAgeMs?: number;
lockAgeMs?: number;
};
actions: string[];
}
interface DoDStatus {
status?: "DONE" | "NOT_DONE";
passed?: boolean;
}
interface WorkerHeartbeat {
pid?: number;
}
function isExpired(lock: RunLock, nowMs = Date.now()): boolean {
const expiry = Date.parse(lock.leaseUntil);
if (Number.isNaN(expiry)) return true;
return expiry <= nowMs;
}
function isPidAlive(pid: number): boolean {
if (!Number.isInteger(pid) || pid <= 0) return false;
try {
process.kill(pid, 0);
return true;
} catch (error) {
const code = (error as NodeJS.ErrnoException)?.code;
if (code === "EPERM") return true;
return false;
}
}
async function readJson<T>(path: string): Promise<T | null> {
try {
const raw = await readFile(path, "utf8");
return JSON.parse(raw) as T;
} catch {
return null;
}
}
async function readLock(path: string): Promise<RunLock | null> {
return await readJson<RunLock>(path);
}
async function runNodeScript(
scriptPath: string,
args: string[],
env: NodeJS.ProcessEnv,
): Promise<{ code: number | null; stdout: string; stderr: string }> {
return await new Promise((resolvePromise, rejectPromise) => {
const child = spawn(process.execPath, ["--import", "tsx", scriptPath, ...args], {
cwd: process.cwd(),
env,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.on("data", (chunk) => {
stdout += String(chunk);
});
child.stderr.on("data", (chunk) => {
stderr += String(chunk);
});
child.on("error", rejectPromise);
child.on("close", (code) => resolvePromise({ code, stdout, stderr }));
});
}
async function readRecovery(path: string): Promise<RecoveryReport | null> {
return await readJson<RecoveryReport>(path);
}
async function writeJsonAtomic(path: string, value: unknown): Promise<void> {
await mkdir(dirname(path), { recursive: true });
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
await rename(tmp, path);
}
async function inspectWorker(runtimeDir: string): Promise<{ active: boolean; detail: string }> {
const heartbeatPath = resolve(runtimeDir, "health", "worker-heartbeat.json");
const hb = await readJson<WorkerHeartbeat>(heartbeatPath);
const active = hb?.pid !== undefined && isPidAlive(hb.pid);
if (active) {
return { active: true, detail: `worker-alive pid=${hb?.pid}` };
}
return { active: false, detail: "worker-not-alive" };
}
async function main(): Promise<void> {
const runtimeDir = resolve(process.cwd(), "runtime");
const owner = process.env.RUN_OWNER ?? "pandas-main";
const lockPath = resolve(process.argv[2] ?? join(runtimeDir, "run.lock"));
const evidencePath = resolve(process.argv[3] ?? join(runtimeDir, "evidence", "latest.json"));
const recoveryPath = resolve(process.argv[4] ?? join(runtimeDir, "recovery", "latest.json"));
const reportPath = resolve(process.argv[5] ?? join(dirname(recoveryPath), "watchdog-latest.json"));
const dodStatusPath = resolve(runtimeDir, "evidence", "dod-status.json");
const lockBefore = await readLock(lockPath);
const lockExpired = lockBefore ? isExpired(lockBefore) : true;
const actions: string[] = [];
let lockAction = "none";
if (lockBefore && !lockExpired && lockBefore.owner !== owner) {
console.error(
`WATCHDOG_LOCK_CONFLICT expected=${owner} current=${lockBefore.owner} leaseUntil=${lockBefore.leaseUntil}`,
);
process.exit(1);
}
const runLockScript = resolve(process.cwd(), "scripts", "run-lock.ts");
if (!lockBefore || lockExpired) {
lockAction = "acquire";
const acquire = await runNodeScript(runLockScript, ["acquire", lockPath], {
...process.env,
RUN_OWNER: owner,
});
if (acquire.code !== 0) {
console.error(`WATCHDOG_LOCK_ACQUIRE_FAILED ${acquire.stderr.trim() || acquire.stdout.trim()}`);
process.exit(1);
}
actions.push("lock-acquire");
} else {
lockAction = "renew";
const renew = await runNodeScript(runLockScript, ["renew", lockPath], {
...process.env,
RUN_OWNER: owner,
});
if (renew.code !== 0) {
console.error(`WATCHDOG_LOCK_RENEW_FAILED ${renew.stderr.trim() || renew.stdout.trim()}`);
process.exit(1);
}
actions.push("lock-renew");
}
const healScript = resolve(process.cwd(), "scripts", "stall-auto-heal.ts");
const heal = await runNodeScript(healScript, [evidencePath, lockPath, recoveryPath], {
...process.env,
RUN_OWNER: owner,
});
if (heal.code !== 0) {
console.error(`WATCHDOG_HEAL_FAILED ${heal.stderr.trim() || heal.stdout.trim()}`);
process.exit(1);
}
actions.push("heal-check");
const dod = await readJson<DoDStatus>(dodStatusPath);
const isNotDone = dod?.status === "NOT_DONE" || dod?.passed === false || dod == null;
if (isNotDone) {
const worker = await inspectWorker(runtimeDir);
actions.push(worker.detail);
// Single starter rule: watchdog never starts worker directly.
// Supervisor is the only component allowed to (re)start resident worker.
if (!worker.active) {
actions.push("restart-delegated-to-supervisor");
}
} else {
actions.push("dod-done");
}
const lockAfter = await readLock(lockPath);
const recovery = await readRecovery(recoveryPath);
const outcome =
recovery?.status === "healed" ? "healed" : recovery?.status === "failed" ? "failed" : "healthy";
const report = {
generatedAt: new Date().toISOString(),
owner,
lockPath,
lockAction,
lock: lockAfter,
evidencePath,
recoveryPath,
recovery,
dodStatus: dod?.status ?? "UNKNOWN",
outcome,
actions,
};
await writeJsonAtomic(reportPath, report);
if (outcome === "failed") {
console.error(`WATCHDOG_FAILED report=${reportPath}`);
process.exit(1);
}
console.log(`WATCHDOG_OK outcome=${outcome} action=${lockAction} report=${reportPath}`);
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});