904 lines
63 KiB
TypeScript
904 lines
63 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { test } from "bun:test";
|
|
|
|
import { runHwlabCli } from "../src/hwlab-cli-lib.ts";
|
|
import { artifactsFromKeilStatusForTest, buildWaitBlockerForTest, extractKeilJobId, jobStatusCommandForTest, summarizeHwpodOperationForTest } from "../src/hwlab-caserun-lib.ts";
|
|
|
|
const SUBJECT_COMMIT_ID = "df7a4e6e551fa90d64bde5537cc000f89d63dd20";
|
|
const SUBJECT_REPO_LOCAL_PATH = "F:\\Work\\HWLAB-CASE-F103";
|
|
const TEST_RUNTIME_API_URL = "http://api.test";
|
|
const TEST_RUNTIME_WEB_URL = "http://web.test";
|
|
const TEST_RUNTIME_ENV = { HWLAB_RUNTIME_API_URL: TEST_RUNTIME_API_URL, HWLAB_RUNTIME_WEB_URL: TEST_RUNTIME_WEB_URL };
|
|
|
|
test("hwlab-cli case prepare copies a case hwpod-spec into isolated run state", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const caseRepo = path.join(root, "hwlab-case-registry");
|
|
const cwd = path.join(root, "hwlab");
|
|
const caseDir = path.join(caseRepo, "cases", "d601-f103-v2-compile");
|
|
try {
|
|
await mkdir(path.join(caseRepo, ".git"), { recursive: true });
|
|
await writeFile(path.join(caseRepo, ".git", "HEAD"), "ref: refs/heads/main\n", "utf8");
|
|
await mkdir(caseDir, { recursive: true });
|
|
await mkdir(cwd, { recursive: true });
|
|
await writeFile(path.join(caseDir, "case.json"), JSON.stringify({ contractVersion: "hwpod-case-v1", caseId: "d601-f103-v2-compile", hwpodSpec: "hwpod-spec.yaml", subject: { repoLocalPath: SUBJECT_REPO_LOCAL_PATH, commitId: SUBJECT_COMMIT_ID, subdir: "projects/01_baseline" } }, null, 2), "utf8");
|
|
await writeFile(path.join(caseDir, "hwpod-spec.yaml"), hwpodSpecText(), "utf8");
|
|
|
|
const requests: any[] = [];
|
|
const result = await runHwlabCli(["case", "prepare", "d601-f103-v2-compile", "--case-repo", caseRepo, "--run-id", "run-test"], { cwd, now: () => "2026-06-05T00:00:00.000Z", env: TEST_RUNTIME_ENV, fetchImpl: subjectWorktreeFetch(requests) });
|
|
|
|
assert.equal(result.exitCode, 0, JSON.stringify(result.payload, null, 2));
|
|
assert.equal(result.payload.action, "case.prepare.prepare");
|
|
assert.equal(result.payload.runId, "run-test");
|
|
assert.equal(result.payload.caseRepo, caseRepo);
|
|
assert.equal(result.payload.compileOnly, true);
|
|
assert.equal(result.payload.subject.repoLocalPath, SUBJECT_REPO_LOCAL_PATH);
|
|
assert.equal(result.payload.subject.commitId, SUBJECT_COMMIT_ID);
|
|
assert.equal(result.payload.subject.worktreePath, "F:\\Work\\HWLAB-CASE-F103\\.worktree\\caserun-run-test");
|
|
const runLocalSpec = await readFile(path.join(cwd, ".state", "hwlab-cli", "caserun", "run-test", ".hwlab", "hwpod-spec.yaml"), "utf8");
|
|
assert.match(runLocalSpec, /node-d601-f103-v2/u);
|
|
assert.match(runLocalSpec, /path: "F:\\\\Work\\\\HWLAB-CASE-F103\\\\\.worktree\\\\caserun-run-test"/u);
|
|
const setupRequest = requests.find((item) => item.body?.ops?.some((op: any) => op.opId === "op_02_subject_worktree_add"));
|
|
assert.ok(setupRequest);
|
|
assert.equal(setupRequest.body.ops.length, 4);
|
|
assert.equal(setupRequest.body.ops[0].args.workspacePath, SUBJECT_REPO_LOCAL_PATH);
|
|
assert.deepEqual(setupRequest.body.ops[0].args.argv, ["cat-file", "-e", SUBJECT_COMMIT_ID]);
|
|
assert.deepEqual(setupRequest.body.ops[1].args.argv, ["worktree", "add", "--detach", "--force", ".worktree\\caserun-run-test", SUBJECT_COMMIT_ID]);
|
|
assert.deepEqual(setupRequest.body.ops[2].args.argv, ["-C", ".worktree\\caserun-run-test", "rev-parse", "HEAD"]);
|
|
assert.equal(setupRequest.body.ops[3].opId, "op_04_keil_sidecars");
|
|
assert.equal(setupRequest.body.ops[3].args.command, "node");
|
|
const sidecarInput = JSON.parse(setupRequest.body.ops[3].args.argv[2]);
|
|
assert.equal(sidecarInput.sourceBase, "projects\\01_baseline\\Projects\\MDK-ARM\\atk_f103");
|
|
assert.equal(sidecarInput.destinationBase, ".worktree\\caserun-run-test\\projects\\01_baseline\\Projects\\MDK-ARM\\atk_f103");
|
|
assert.equal(setupRequest.body.ops[3].args.commandBinding.sourceBase, "projects\\01_baseline\\Projects\\MDK-ARM\\atk_f103");
|
|
assert.equal(setupRequest.body.ops[3].args.commandBinding.destinationBase, ".worktree\\caserun-run-test\\projects\\01_baseline\\Projects\\MDK-ARM\\atk_f103");
|
|
assert.equal(result.payload.subject.setup.keilSidecars.copied, 1);
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("case prepare requires an explicit v0.3 runtime API URL", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const caseRepo = path.join(root, "hwlab-case-registry");
|
|
const cwd = path.join(root, "hwlab");
|
|
try {
|
|
await createCaseRepo(caseRepo, "d601-f103-v2-compile");
|
|
await mkdir(cwd, { recursive: true });
|
|
|
|
const result = await runHwlabCli(["case", "prepare", "d601-f103-v2-compile", "--case-repo", caseRepo], { cwd, now: () => "2026-06-05T00:00:00.000Z" });
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.error.code, "caserun_runtime_api_url_required");
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("case prepare rejects the removed v0.2 runtime API URL", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const caseRepo = path.join(root, "hwlab-case-registry");
|
|
const cwd = path.join(root, "hwlab");
|
|
try {
|
|
await createCaseRepo(caseRepo, "d601-f103-v2-compile");
|
|
await mkdir(cwd, { recursive: true });
|
|
|
|
const result = await runHwlabCli(["case", "prepare", "d601-f103-v2-compile", "--case-repo", caseRepo], { cwd, now: () => "2026-06-05T00:00:00.000Z", env: { HWLAB_RUNTIME_API_URL: "http://74.48.78.17:19667" } });
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.error.code, "caserun_removed_v02_runtime_url");
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("case prepare rejects legacy subject repo/ref without a local repo path and commit id", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const caseRepo = path.join(root, "hwlab-case-registry");
|
|
const cwd = path.join(root, "hwlab");
|
|
const caseDir = path.join(caseRepo, "cases", "legacy-case");
|
|
try {
|
|
await mkdir(path.join(caseRepo, ".git"), { recursive: true });
|
|
await writeFile(path.join(caseRepo, ".git", "HEAD"), "ref: refs/heads/main\n", "utf8");
|
|
await mkdir(caseDir, { recursive: true });
|
|
await mkdir(cwd, { recursive: true });
|
|
await writeFile(path.join(caseDir, "case.json"), JSON.stringify({ contractVersion: "hwpod-case-v1", caseId: "legacy-case", hwpodSpec: "hwpod-spec.yaml", subject: { repo: "git@github.com:pikasTech/D601-HWLAB.git", ref: "main" } }, null, 2), "utf8");
|
|
await writeFile(path.join(caseDir, "hwpod-spec.yaml"), hwpodSpecText(), "utf8");
|
|
|
|
const result = await runHwlabCli(["case", "prepare", "legacy-case", "--case-repo", caseRepo], { cwd, now: () => "2026-06-05T00:00:00.000Z", env: TEST_RUNTIME_ENV, fetchImpl: subjectWorktreeFetch([]) });
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.error.code, "subject_repo_local_path_required");
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("case prepare uses the new registry by default and refuses the removed hwpod-cases path", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const cwd = path.join(root, "hwlab");
|
|
const newRepo = path.join(root, "hwlab-case-registry");
|
|
const removedRepo = path.join(root, "hwpod-cases");
|
|
const caseId = "d601-f103-v2-compile";
|
|
try {
|
|
await createCaseRepo(newRepo, caseId);
|
|
await createCaseRepo(removedRepo, caseId);
|
|
await mkdir(cwd, { recursive: true });
|
|
|
|
const defaultResult = await runHwlabCli(["case", "prepare", caseId, "--run-id", "run-default"], { cwd, now: () => "2026-06-05T00:00:00.000Z", env: TEST_RUNTIME_ENV, fetchImpl: subjectWorktreeFetch([]) });
|
|
assert.equal(defaultResult.exitCode, 0);
|
|
assert.equal(defaultResult.payload.caseRepo, newRepo);
|
|
|
|
const removedResult = await runHwlabCli(["case", "prepare", caseId, "--case-repo", removedRepo], { cwd, now: () => "2026-06-05T00:00:00.000Z" });
|
|
assert.equal(removedResult.exitCode, 1);
|
|
assert.equal(removedResult.payload.error.code, "removed_case_repo_path");
|
|
assert.equal(removedResult.payload.error.details.standardCaseRepo, "/root/hwlab-case-registry");
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("case prompt previews the real assembled prompt in Chinese without starting a run", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const caseRepo = path.join(root, "hwlab-case-registry");
|
|
const cwd = path.join(root, "hwlab");
|
|
const caseDir = path.join(caseRepo, "cases", "d601-f103-v2-prompt-preview");
|
|
try {
|
|
await mkdir(path.join(caseRepo, ".git"), { recursive: true });
|
|
await writeFile(path.join(caseRepo, ".git", "HEAD"), "ref: refs/heads/main\n", "utf8");
|
|
await mkdir(caseDir, { recursive: true });
|
|
await mkdir(cwd, { recursive: true });
|
|
await writeFile(path.join(caseDir, "case.json"), JSON.stringify({
|
|
contractVersion: "hwpod-case-v1",
|
|
caseId: "d601-f103-v2-prompt-preview",
|
|
title: "D601-F103-V2 中文 Prompt 预览",
|
|
hwpodSpec: "hwpod-spec.yaml",
|
|
subject: { repoLocalPath: SUBJECT_REPO_LOCAL_PATH, commitId: SUBJECT_COMMIT_ID, subdir: "projects/01_baseline" },
|
|
agentTask: { workspace: "subjectWorktree", prompt: "请在隔离主体工作区中读取 main.c,并只回报当前文件中与串口初始化相关的函数名。", constraints: ["不要修改源码。"] }
|
|
}, null, 2), "utf8");
|
|
await writeFile(path.join(caseDir, "hwpod-spec.yaml"), hwpodSpecText(), "utf8");
|
|
|
|
const result = await runHwlabCli(["case", "prompt", "d601-f103-v2-prompt-preview", "--case-repo", caseRepo, "--run-id", "run-prompt-preview"], { cwd, now: () => "2026-06-08T00:00:00.000Z", env: TEST_RUNTIME_ENV });
|
|
|
|
assert.equal(result.exitCode, 0, JSON.stringify(result.payload, null, 2));
|
|
assert.equal(result.payload.action, "case.prompt");
|
|
assert.equal(result.payload.previewOnly, true);
|
|
assert.equal(result.payload.serviceRuntime, false);
|
|
assert.match(result.payload.prompt, /# HWPOD CaseRun 代码代理任务/u);
|
|
assert.match(result.payload.prompt, /## 运行时装配/u);
|
|
assert.match(result.payload.prompt, /## HWPOD 运行时/u);
|
|
assert.match(result.payload.prompt, /## 任务/u);
|
|
assert.match(result.payload.prompt, /## 约束/u);
|
|
assert.match(result.payload.prompt, /## 执行流程/u);
|
|
assert.match(result.payload.prompt, /思维过程和输出消息一律使用中文/u);
|
|
assert.match(result.payload.prompt, /验证模式: 仅执行编译构建检查/u);
|
|
assert.match(result.payload.prompt, /请在隔离主体工作区中读取 main\.c/u);
|
|
assert.match(result.payload.prompt, /hwpod-ctl spec validate --hwpod-id d601-f103-v2 --workspace-path/u);
|
|
assert.doesNotMatch(result.payload.prompt, /Runtime Assembly|Standard smoke sequence|verificationMode: compile-only build check|## Task|## Constraints|## Flow/u);
|
|
await assert.rejects(stat(path.join(cwd, ".state", "hwlab-cli", "caserun", "run-prompt-preview")));
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("case run extracts Keil async job ids from hwpod-node output", () => {
|
|
const jobId = extractKeilJobId({ body: { results: [{ stdout: JSON.stringify({ ok: true, job_id: "20260605_203835_798515c0" }) }] } });
|
|
assert.equal(jobId, "20260605_203835_798515c0");
|
|
|
|
const nestedJobId = extractKeilJobId({ body: { results: [{ output: { stdout: JSON.stringify({ accepted: true, job_id: "20260605_210457_bdf3292a" }) } }] } });
|
|
assert.equal(nestedJobId, "20260605_210457_bdf3292a");
|
|
});
|
|
|
|
test("case run uses cmd.run command plus argv for Keil job-status", () => {
|
|
const command = jobStatusCommandForTest("py -3", "C:\\Users\\liang\\.agents\\skills\\keil\\keil-cli.py", "job-1");
|
|
assert.equal(command.command, "cmd.exe");
|
|
assert.deepEqual(command.argv.slice(0, 3), ["/d", "/s", "/c"]);
|
|
assert.match(command.argv[3], /^py -3 C:\\Users\\liang\\\.agents\\skills\\keil\\keil-cli\.py job-status job-1$/u);
|
|
});
|
|
|
|
test("case run exposes Keil hex and axf artifacts in evidence summary", () => {
|
|
assert.deepEqual(artifactsFromKeilStatusForTest({ result: { hex_file: "F:\\out.hex", axf_file: "F:\\out.axf" } }), ["F:\\out.hex", "F:\\out.axf"]);
|
|
});
|
|
|
|
test("case run normalizes v0.3 HWPOD build operation topology and typed wait blockers", () => {
|
|
const document = { metadata: { name: "d601-f103-v2" }, spec: { nodeBinding: { nodeId: "node-d601-f103-v2" }, workspace: { path: "F:\\Work\\case" } } };
|
|
const operation = summarizeHwpodOperationForTest({ body: { hwpodId: "d601-f103-v2", nodeId: "node-d601-f103-v2", results: [{ opId: "op_build", op: "debug.build", ok: true, status: "completed", workspacePath: "F:\\Work\\case", output: { stdout: '{"job_id":"job-1"}' } }] } }, document, "debug.build");
|
|
assert.equal(operation.topology.ok, true);
|
|
assert.equal(operation.jobId, "job-1");
|
|
assert.equal(buildWaitBlockerForTest(operation, { ok: true, status: "completed", warningCount: 7, artifacts: ["F:\\out.hex"] }, 0), null);
|
|
assert.equal(buildWaitBlockerForTest({ ...operation, jobId: "" }, null, 0).code, "hwpod_build_job_id_missing");
|
|
assert.equal(buildWaitBlockerForTest(operation, { ok: false, status: "timeout", timedOut: true, polls: 3 }, 0).code, "hwpod_build_wait_timeout");
|
|
assert.equal(buildWaitBlockerForTest(operation, { ok: false, status: "failed", returnCode: 2 }, 0).code, "hwpod_build_failed");
|
|
assert.equal(buildWaitBlockerForTest(operation, { ok: true, status: "completed", artifacts: ["F:\\out.axf"] }, 0).code, "hwpod_build_hex_missing");
|
|
const mismatch = summarizeHwpodOperationForTest({ body: { nodeId: "wrong-node", results: [{ op: "debug.build", ok: true, output: { stdout: '{"job_id":"job-2"}' } }] } }, document, "debug.build");
|
|
assert.equal(buildWaitBlockerForTest(mismatch, null, 0).code, "hwpod_topology_mismatch");
|
|
const missingObserved = summarizeHwpodOperationForTest({ body: { results: [{ op: "debug.build", ok: true, output: { stdout: '{"job_id":"job-3"}' } }] } }, document, "debug.build");
|
|
assert.deepEqual(missingObserved.topology.blocker.details, [
|
|
{ field: "hwpodId", expected: "d601-f103-v2", observed: null },
|
|
{ field: "nodeId", expected: "node-d601-f103-v2", observed: null },
|
|
{ field: "workspacePath", expected: "F:\\Work\\case", observed: null }
|
|
]);
|
|
const yamlEmpty = summarizeHwpodOperationForTest({ body: { hwpodId: "response-only", nodeId: "response-node", results: [{ op: "debug.build", ok: true, workspacePath: "response-path", output: { stdout: '{"job_id":"job-4"}' } }] } }, { metadata: {}, spec: {} }, "debug.build");
|
|
assert.equal(yamlEmpty.topology.ok, true);
|
|
});
|
|
|
|
test("case run result finalizes legacy build-completed state and refreshes registry artifacts", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const caseRepo = path.join(root, "hwlab-case-registry");
|
|
const cwd = path.join(root, "hwlab");
|
|
const runId = "run-refresh-registry";
|
|
const runDir = path.join(cwd, ".state", "hwlab-cli", "caserun", runId);
|
|
const registryRunDir = path.join(caseRepo, "runs", "d601-f103-v2-compile", runId);
|
|
try {
|
|
await mkdir(path.join(caseRepo, ".git"), { recursive: true });
|
|
await writeFile(path.join(caseRepo, ".git", "HEAD"), "ref: refs/heads/main\n", "utf8");
|
|
await mkdir(path.join(runDir, ".hwlab"), { recursive: true });
|
|
await writeFile(path.join(runDir, ".hwlab", "hwpod-spec.yaml"), hwpodSpecText(), "utf8");
|
|
await writeFile(path.join(runDir, "agent-prompt.md"), "prompt\n", "utf8");
|
|
await writeFile(path.join(runDir, "agent-diff.patch"), "", "utf8");
|
|
await writeFile(path.join(runDir, "worker.stdout.log"), "final worker payload\n", "utf8");
|
|
await writeFile(path.join(runDir, "worker.stderr.log"), "", "utf8");
|
|
const run = {
|
|
caseId: "d601-f103-v2-compile",
|
|
runId,
|
|
runDir,
|
|
caseRepo,
|
|
caseDir: "",
|
|
caseFile: "",
|
|
sourceSpecPath: "",
|
|
specPath: path.join(runDir, ".hwlab", "hwpod-spec.yaml"),
|
|
apiUrl: "http://api.test",
|
|
compileOnly: true,
|
|
createdAt: "2026-06-05T00:00:00.000Z",
|
|
updatedAt: "2026-06-05T00:00:10.000Z",
|
|
status: "running",
|
|
stage: "build-completed",
|
|
definition: {},
|
|
subject: { repoLocalPath: SUBJECT_REPO_LOCAL_PATH, commitId: SUBJECT_COMMIT_ID, subdir: "projects/01_baseline", worktreePath: "F:\\Work\\HWLAB-CASE-F103\\.worktree\\caserun-run-refresh-registry", workspacePath: "", setup: {} },
|
|
evidencePath: path.join(runDir, "evidence.json"),
|
|
_control: { status: "running", stage: "build-completed", stdoutPath: path.join(runDir, "worker.stdout.log"), stderrPath: path.join(runDir, "worker.stderr.log"), resultPath: path.join(runDir, "result.json") }
|
|
};
|
|
const evidence = {
|
|
contractVersion: "hwpod-case-run-evidence-v1",
|
|
caseId: run.caseId,
|
|
runId,
|
|
compileOnly: true,
|
|
autoEvaluation: false,
|
|
status: "recorded",
|
|
subject: run.subject,
|
|
agent: { traceId: "trc_refresh", sessionId: "ses_refresh", conversationId: "cnv_refresh", threadId: "thread-refresh", providerProfile: "deepseek", projectId: "prj_hwpod_workbench", promptSha256: "prompt-sha" },
|
|
agentTrace: { traceId: "trc_refresh", status: "recorded", terminalStatus: "completed", commandCount: 1, hwpodCommandCount: 1, hwpodBuildCommandCount: 1, agentRun: { runId: "run_agent_refresh" } },
|
|
agentDiff: { statusShort: "", diffStat: "", diffPatchSha256: "diff-sha" },
|
|
hwpod: { source: "case-run-runner-post-agent-compile-check", exitCode: 0 },
|
|
keilJob: { jobId: "job-refresh", status: "completed", returnCode: 0 },
|
|
artifacts: [],
|
|
decisions: { downloadSkipped: true }
|
|
};
|
|
await writeJsonFile(path.join(runDir, "run.json"), run);
|
|
await writeJsonFile(path.join(runDir, "evidence.json"), evidence);
|
|
await writeJsonFile(path.join(runDir, "result.json"), { ok: true, action: "case.run", status: "completed", caseId: run.caseId, runId, runDir, collect: { caseRepoFiles: [] }, evidence: {} });
|
|
await mkdir(registryRunDir, { recursive: true });
|
|
await writeFile(path.join(registryRunDir, "worker.stdout.log"), "", "utf8");
|
|
|
|
const result = await runHwlabCli(["case", "run", "result", runId], { cwd, now: () => "2026-06-05T00:00:11.000Z" });
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.ready, true);
|
|
assert.equal(result.payload.registryArchive.artifactManifestPath, path.join(registryRunDir, "artifact-manifest.json"));
|
|
assert.equal(await readFile(path.join(registryRunDir, "worker.stdout.log"), "utf8"), "final worker payload\n");
|
|
const manifest = JSON.parse(await readFile(path.join(registryRunDir, "artifact-manifest.json"), "utf8"));
|
|
assert.equal(manifest.trace.traceId, "trc_refresh");
|
|
assert.equal(manifest.files.some((item: any) => item.path === "worker.stdout.log" && item.bytes === Buffer.byteLength("final worker payload\n")), true);
|
|
const registryResult = JSON.parse(await readFile(path.join(registryRunDir, "result.json"), "utf8"));
|
|
assert.equal(registryResult.collect.artifactManifestPath, path.join(registryRunDir, "artifact-manifest.json"));
|
|
const finalizedRun = JSON.parse(await readFile(path.join(runDir, "run.json"), "utf8"));
|
|
assert.equal(finalizedRun.status, "completed");
|
|
assert.equal(finalizedRun.stage, "completed");
|
|
assert.equal(finalizedRun._control.status, "completed");
|
|
assert.equal(finalizedRun._control.stage, "completed");
|
|
assert.equal(finalizedRun._control.completedAt, "2026-06-05T00:00:11.000Z");
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("case run orchestrates agent prompt, diff capture, and compile evidence without auto evaluation", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const caseRepo = path.join(root, "hwlab-case-registry");
|
|
const cwd = path.join(root, "hwlab");
|
|
const caseDir = path.join(caseRepo, "cases", "d601-f103-v2-compile");
|
|
try {
|
|
await mkdir(path.join(caseRepo, ".git"), { recursive: true });
|
|
await writeFile(path.join(caseRepo, ".git", "HEAD"), "ref: refs/heads/main\n", "utf8");
|
|
await mkdir(caseDir, { recursive: true });
|
|
await mkdir(cwd, { recursive: true });
|
|
await writeFile(path.join(caseDir, "case.json"), JSON.stringify({
|
|
contractVersion: "hwpod-case-v1",
|
|
caseId: "d601-f103-v2-compile",
|
|
title: "D601-F103-V2 Keil compile-only CaseRun",
|
|
hwpodSpec: "hwpod-spec.yaml",
|
|
subject: { repoLocalPath: SUBJECT_REPO_LOCAL_PATH, commitId: SUBJECT_COMMIT_ID, subdir: "projects/01_baseline" },
|
|
diffCollection: { ignore: [{ pattern: "build/**", reason: "generated output" }] },
|
|
agentTask: { workspace: "subjectWorktree", prompt: "请通过 HWPOD 做一个很小的源码修改。", timeoutMs: 234000, pollIntervalMs: 10 },
|
|
}, null, 2), "utf8");
|
|
await writeFile(path.join(caseDir, "hwpod-spec.yaml"), hwpodSpecText(), "utf8");
|
|
|
|
const requests: any[] = [];
|
|
const runProcessCalls: string[][] = [];
|
|
let sawAgentRunningState = false;
|
|
const result = await runHwlabCli([
|
|
"case",
|
|
"run",
|
|
"d601-f103-v2-compile",
|
|
"--case-repo",
|
|
caseRepo,
|
|
"--run-id",
|
|
"run-agent-flow",
|
|
"--poll-interval-ms",
|
|
"10",
|
|
"--agent-poll-interval-ms",
|
|
"10"
|
|
], {
|
|
cwd,
|
|
now: () => "2026-06-05T00:00:00.000Z",
|
|
sleep: async () => {
|
|
const runDir = path.join(cwd, ".state", "hwlab-cli", "caserun", "run-agent-flow");
|
|
const runState = JSON.parse(await readFile(path.join(runDir, "run.json"), "utf8"));
|
|
assert.equal(runState.stage, "agent-running");
|
|
assert.equal(runState.agent.stageStatus, "running");
|
|
assert.equal(runState.agent.sessionId, "ses_case_run");
|
|
assert.equal(runState.agent.conversationId, "cnv_case_d601-f103-v2-compile_run-agent-flow");
|
|
assert.equal(runState.agent.threadId, "thread-case");
|
|
assert.equal(runState.agent.timeoutMs, 234000);
|
|
assert.equal(runState.agent.pollIntervalMs, 250);
|
|
assert.equal(runState.agent.toolCallSummary.count, 3);
|
|
assert.match(runState.agent.traceId, /^trc_case_/u);
|
|
assert.match(runState.agent.nextPollCommand, /client agent result trc_case_/u);
|
|
assert.equal(runState._control.stage, "agent-running");
|
|
await writeFile(path.join(runDir, "worker.stdout.log"), "worker accepted\n", "utf8");
|
|
await writeFile(path.join(runDir, "worker.stderr.log"), "", "utf8");
|
|
sawAgentRunningState = true;
|
|
},
|
|
env: { ...TEST_RUNTIME_ENV, HWLAB_API_KEY: "hwl_live_test_case_agent_key" },
|
|
runProcess: async (command) => {
|
|
runProcessCalls.push(command);
|
|
const gitArgs = command.slice(1);
|
|
if (command.includes("client") && command.includes("agent") && command.includes("trace") && command.includes("--full")) {
|
|
const traceId = command[command.indexOf("trace") + 1] ?? "trc_case_test";
|
|
return { command, exitCode: 0, stdout: JSON.stringify({ ok: true, action: "client.agent.trace", status: "succeeded", body: { traceId, status: "succeeded", traceStatus: "completed", render: "web", source: "hwlab-cli.client.agent.trace", sourceEventCount: 3, renderedRowCount: 3, rows: [
|
|
{ rowId: "trace-request:1", seq: 1, tone: "source", header: "00:00:00 请求接受", body: null },
|
|
{ rowId: "tool:cmd-build", seq: 2, tone: "ok", header: "00:00:01 ok commandExecution", bodyFormat: "text", body: "hwpod build --spec .hwlab/hwpod-spec.yaml\nstdout:\n{ \"ok\": true, \"job_id\": \"20260605_203835_798515c0\" }\nexitCode=0" },
|
|
{ rowId: "trace-final-response:3", seq: 3, tone: "ok", header: "00:00:02 助手最终消息", terminal: true, bodyFormat: "markdown", body: "done" }
|
|
], finalResponse: { present: true, status: "completed", text: "done" }, traceSummary: { terminalStatus: "completed" } } }), stderr: "" };
|
|
}
|
|
if (command[0] === "git" && gitArgs[0] === "status") return { command, exitCode: 0, stdout: "?? runs/d601-f103-v2-compile/run-agent-flow/\n", stderr: "" };
|
|
if (command[0] === "git" && gitArgs[0] === "add") return { command, exitCode: 0, stdout: "", stderr: "" };
|
|
if (command[0] === "git" && gitArgs[0] === "diff") return { command, exitCode: 1, stdout: "", stderr: "" };
|
|
if (command[0] === "git" && gitArgs[0] === "commit") return { command, exitCode: 0, stdout: "[main abc1234] data: archive caserun run-agent-flow\n", stderr: "" };
|
|
if (command[0] === "git" && gitArgs[0] === "push") return { command, exitCode: 0, stdout: "", stderr: "To github.com:pikasTech/hwlab-case-registry.git\n" };
|
|
return { command, exitCode: 0, stdout: JSON.stringify({ body: { hwpodId: "d601-f103-v2", nodeId: "node-d601-f103-v2", results: [{ opId: "op_build", op: "debug.build", ok: true, status: "completed", workspacePath: `${SUBJECT_REPO_LOCAL_PATH}\\.worktree\\caserun-run-agent-flow`, output: { stdout: JSON.stringify({ job_id: "job-compile-1" }) } }] } }), stderr: "" };
|
|
},
|
|
fetchImpl: caseRunFlowFetch(requests)
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0, JSON.stringify(result.payload, null, 2));
|
|
assert.equal(result.payload.action, "case.run");
|
|
assert.equal(result.payload.status, "completed");
|
|
assert.equal(result.payload.evidence.status, "recorded");
|
|
assert.equal(result.payload.evidence.autoEvaluation, false);
|
|
assert.equal(result.payload.evidence.ok, undefined);
|
|
assert.equal(result.payload.agent.stageStatus, "completed");
|
|
assert.equal(result.payload.agent.traceId.startsWith("trc_case_"), true);
|
|
assert.equal(result.payload.agent.timeoutMs, 234000);
|
|
assert.equal(result.payload.agent.requestedProviderProfile, "deepseek");
|
|
assert.equal(result.payload.agent.resolvedBackendProfile, "dsflash-go");
|
|
assert.equal(result.payload.agent.model, "deepseek-v4-flash");
|
|
assert.equal(result.payload.agent.infrastructureBackend, "agentrun-v01/dsflash-go");
|
|
assert.equal(result.payload.agent.terminalStatus, "completed");
|
|
assert.equal(result.payload.agent.toolCallSummary.count, 3);
|
|
assert.equal(result.payload.traceLookup.commands.trace, `hwlab-cli client agent trace ${result.payload.agent.traceId} --render web`);
|
|
assert.equal(result.payload.traceLookup.commands.result, `hwlab-cli client agent result ${result.payload.agent.traceId}`);
|
|
assert.equal(result.payload.traceLookup.commands.inspect, `hwlab-cli client agent inspect --trace-id ${result.payload.agent.traceId}`);
|
|
assert.equal(result.payload.agentTrace.source, "hwlab-cli.client.agent.trace");
|
|
assert.equal(result.payload.agentTrace.lookupOnly, undefined);
|
|
assert.equal(result.payload.agentTrace.commandCount, 3);
|
|
assert.equal(result.payload.agentTrace.hwpodCommandCount, 3);
|
|
assert.equal(result.payload.agentTrace.hwpodBuildCommandCount, 1);
|
|
assert.equal(result.payload.evidence.agentTrace.commandCount, 3);
|
|
assert.equal(result.payload.evidence.agentTrace.hwpodCommandCount, 3);
|
|
assert.equal(result.payload.evidence.agentFinal.present, true);
|
|
assert.equal(result.payload.evidence.agentFinal.text, "done");
|
|
assert.equal(result.payload.evidence.postValidation.boundary, "runner-post-agent-validation");
|
|
assert.equal(result.payload.evidence.warnings.agentReportedBuildWarningCount, 2);
|
|
assert.equal(result.payload.evidence.warnings.runnerPostValidationWarningCount, 7);
|
|
assert.equal(result.payload.evidence.agentTrace.keilJobCandidates.includes("20260605_203835_798515c0"), true);
|
|
assert.equal(result.payload.evidence.agentTask.timeoutMs, 234000);
|
|
assert.equal(result.payload.agent.polls <= 3, true);
|
|
assert.equal(result.payload.evidence.hwpod.source, "case-run-runner-post-agent-compile-check");
|
|
assert.match(result.payload.agentDiff.statusShort, /projects\/01_baseline\/main\.c/u);
|
|
assert.deepEqual(result.payload.agentDiff.diffCollection.untracked, { total: 2, included: 1, omitted: 1 });
|
|
assert.deepEqual(result.payload.agentDiff.diffCollection.submodules, [{ path: "vendor/lib", commit: "0123456789abcdef", state: "modified" }]);
|
|
assert.equal(result.payload.agentDiff.diffCollection.included[0].path, "projects/01_baseline/new.c");
|
|
assert.equal(result.payload.agentDiff.diffCollection.omitted[0].path, "projects/01_baseline/build/out.bin");
|
|
assert.equal(result.payload.agentDiff.diffCollection.omitted[0].reason, "generated output");
|
|
assert.match(await readFile(result.payload.agentDiff.diffPatchPath, "utf8"), /diff --git a\/projects\/01_baseline\/new\.c b\/projects\/01_baseline\/new\.c/u);
|
|
assert.match(await readFile(result.payload.agentDiff.diffPatchPath, "utf8"), /build\/out\.bin rule=build\/\*\*/u);
|
|
assert.equal(result.payload.build.autoEvaluation, false);
|
|
assert.equal(result.payload.build.traceLookup.commands.trace, result.payload.traceLookup.commands.trace);
|
|
assert.equal(result.payload.build.agentTraceHwpodCommandCount, 3);
|
|
assert.equal(result.payload.build.hwpodExitCode, 0);
|
|
assert.equal(result.payload.build.hwpodSource, "case-run-runner-post-agent-compile-check");
|
|
assert.equal(result.payload.build.keilStatus, "completed");
|
|
assert.equal(result.payload.collect.autoEvaluation, false);
|
|
assert.equal(result.payload.collect.registrySync.status, "pushed");
|
|
assert.equal(result.payload.collect.registrySync.path, "runs/d601-f103-v2-compile/run-agent-flow");
|
|
assert.equal(runProcessCalls.some((command) => command[0] === "git" && command[1] === "add" && command.at(-1) === "runs/d601-f103-v2-compile/run-agent-flow"), true);
|
|
assert.equal(runProcessCalls.some((command) => command[0] === "git" && command[1] === "commit" && command.includes("data: archive caserun run-agent-flow")), true);
|
|
assert.equal(runProcessCalls.some((command) => command[0] === "git" && command[1] === "push" && command.includes("origin") && command.includes("HEAD")), true);
|
|
assert.equal(sawAgentRunningState, true);
|
|
|
|
const registryRunDir = path.join(caseRepo, "runs", "d601-f103-v2-compile", "run-agent-flow");
|
|
const registryFiles = [
|
|
"evidence.json",
|
|
"summary.md",
|
|
"run.json",
|
|
"result.json",
|
|
"agent-trace.json",
|
|
"agent-trace.md",
|
|
"agent-messages.json",
|
|
"agent-transcript.md",
|
|
"final-response.md",
|
|
"agent-prompt.md",
|
|
"agent-diff.patch",
|
|
path.join(".hwlab", "hwpod-spec.yaml"),
|
|
"worker.stdout.log",
|
|
"worker.stderr.log",
|
|
"artifact-manifest.json"
|
|
];
|
|
for (const file of registryFiles) await stat(path.join(registryRunDir, file));
|
|
const manifest = JSON.parse(await readFile(path.join(registryRunDir, "artifact-manifest.json"), "utf8"));
|
|
assert.equal(manifest.contractVersion, "hwpod-case-run-artifact-manifest-v1");
|
|
assert.equal(manifest.trace.traceId, result.payload.agent.traceId);
|
|
assert.equal(manifest.trace.sessionId, "ses_case_run");
|
|
assert.equal(manifest.trace.conversationId, "cnv_case_d601-f103-v2-compile_run-agent-flow");
|
|
assert.equal(manifest.trace.source, "hwlab-cli.client.agent.trace");
|
|
assert.equal(manifest.trace.lookupOnly, undefined);
|
|
assert.equal(manifest.trace.lookup.commands.trace, result.payload.traceLookup.commands.trace);
|
|
assert.equal(manifest.traceLookup.commands.inspect, result.payload.traceLookup.commands.inspect);
|
|
assert.equal(manifest.prompt.sha256, result.payload.evidence.agent.promptSha256);
|
|
assert.equal(manifest.subject.commitId, SUBJECT_COMMIT_ID);
|
|
assert.equal(manifest.diff.sha256, result.payload.evidence.agentDiff.diffPatchSha256);
|
|
assert.deepEqual(manifest.diff.collection.untracked, { total: 2, included: 1, omitted: 1 });
|
|
assert.equal(manifest.readableAgent.renderer, "tools/src/hwlab-cli/trace-renderer:traceDisplayRows");
|
|
assert.equal(manifest.readableAgent.messagesPath, "agent-messages.json");
|
|
assert.equal(manifest.readableAgent.tracePath, "agent-trace.md");
|
|
assert.equal(manifest.readableAgent.transcriptPath, "agent-transcript.md");
|
|
assert.equal(manifest.readableAgent.finalResponsePath, "final-response.md");
|
|
assert.equal(manifest.readableAgent.finalResponse.present, true);
|
|
assert.equal(manifest.readableAgent.finalResponse.text, "done");
|
|
assert.deepEqual(manifest.agentStage.kinds, ["build", "download", "uart-read"]);
|
|
assert.equal(manifest.agentStage.source, "agentrun-result.toolCallSummary");
|
|
assert.equal(manifest.agentStage.toolCallSummaryCount, 3);
|
|
assert.equal(manifest.trace.toolCallSummary.count, 3);
|
|
assert.equal(manifest.agent.requestedProviderProfile, "deepseek");
|
|
assert.equal(manifest.agent.resolvedBackendProfile, "dsflash-go");
|
|
assert.equal(manifest.agentFinal.present, true);
|
|
assert.equal(manifest.postValidation.warningCount, 7);
|
|
assert.equal(manifest.warnings.agentReportedBuildWarningCount, 2);
|
|
assert.equal(manifest.agentStage.autoEvaluation, false);
|
|
assert.equal(manifest.decisions.autoEvaluation, false);
|
|
assert.equal(manifest.decisions.downloadSkipped, undefined);
|
|
assert.equal(manifest.decisions.runnerPostAgentCompileCheck, "recorded");
|
|
assert.equal(manifest.files.some((item: any) => item.path === "agent-messages.json"), true);
|
|
assert.equal(manifest.files.some((item: any) => item.path === "agent-trace.md"), true);
|
|
assert.equal(manifest.files.some((item: any) => item.path === "final-response.md"), true);
|
|
assert.equal(manifest.files.some((item: any) => item.path === "worker.stdout.log"), true);
|
|
assert.equal(manifest.files.some((item: any) => item.path === ".hwlab/hwpod-spec.yaml"), true);
|
|
const messages = JSON.parse(await readFile(path.join(registryRunDir, "agent-messages.json"), "utf8"));
|
|
assert.equal(messages.renderer, "tools/src/hwlab-cli/trace-renderer:traceDisplayRows");
|
|
assert.equal(messages.lookupOnly, undefined);
|
|
assert.equal(messages.traceLookup.commands.trace, result.payload.traceLookup.commands.trace);
|
|
assert.equal(messages.finalResponse.present, true);
|
|
assert.equal(messages.rows.length, 3);
|
|
assert.match(JSON.stringify(messages.rows), /hwpod build --spec \.hwlab\/hwpod-spec\.yaml/u);
|
|
const traceMarkdown = await readFile(path.join(registryRunDir, "agent-trace.md"), "utf8");
|
|
assert.match(traceMarkdown, /renderer: tools\/src\/hwlab-cli\/trace-renderer:traceDisplayRows/u);
|
|
assert.match(traceMarkdown, /traceLookupStrategy: id_plus_existing_cli/u);
|
|
assert.match(traceMarkdown, /client agent trace trc_case_/u);
|
|
assert.match(traceMarkdown, /## Subject Diff/u);
|
|
assert.match(traceMarkdown, /\+printf\("hello"\);/u);
|
|
const finalResponse = await readFile(path.join(registryRunDir, "final-response.md"), "utf8");
|
|
assert.match(finalResponse, /present: true/u);
|
|
assert.match(finalResponse, /done/u);
|
|
assert.equal(result.payload.collect.caseRepoRunDir, registryRunDir);
|
|
assert.equal(result.payload.collect.artifactManifestPath, path.join(registryRunDir, "artifact-manifest.json"));
|
|
assert.deepEqual(result.payload.collect.agentStage.kinds, ["build", "download", "uart-read"]);
|
|
assert.equal(result.payload.collect.caseRepoFiles.some((item: string) => item.endsWith("artifact-manifest.json")), true);
|
|
const registryResult = JSON.parse(await readFile(path.join(registryRunDir, "result.json"), "utf8"));
|
|
assert.equal(registryResult.collect.artifactManifestPath, path.join(registryRunDir, "artifact-manifest.json"));
|
|
const summaryMarkdown = await readFile(path.join(registryRunDir, "summary.md"), "utf8");
|
|
assert.match(summaryMarkdown, /traceLookupStrategy: id_plus_existing_cli/u);
|
|
assert.match(summaryMarkdown, /traceCommand: hwlab-cli client agent trace trc_case_/u);
|
|
assert.match(summaryMarkdown, /agentTraceHwpodCommandCount: 3/u);
|
|
assert.match(summaryMarkdown, /hwpod build --spec \.hwlab\/hwpod-spec\.yaml/u);
|
|
assert.doesNotMatch(summaryMarkdown, /downloadSkipped/u);
|
|
|
|
const prompt = await readFile(path.join(cwd, ".state", "hwlab-cli", "caserun", "run-agent-flow", "agent-prompt.md"), "utf8");
|
|
assert.match(prompt, /不做自动评分或门禁判断/u);
|
|
assert.match(prompt, /运行时装配/u);
|
|
assert.match(prompt, /kind=gitbundle/u);
|
|
assert.match(prompt, /思维过程和输出消息一律使用中文/u);
|
|
assert.match(prompt, /验证模式: 仅执行编译构建检查/u);
|
|
assert.match(prompt, /请通过 HWPOD 做一个很小的源码修改/u);
|
|
assert.doesNotMatch(prompt, /Runtime Assembly|verificationMode: compile-only build check|Standard smoke sequence|## Task|## Constraints|## Flow|without auto-grading/u);
|
|
assert.doesNotMatch(prompt, /compile-only 阶段不要修改 subject 源码/u);
|
|
assert.doesNotMatch(prompt, /Write exactly this YAML/u);
|
|
assert.doesNotMatch(prompt, /```yaml/u);
|
|
assert.doesNotMatch(prompt, /apiVersion: hwlab\.pikastech\.com/u);
|
|
assert.doesNotMatch(prompt, /path: "F:\\\\Work\\\\HWLAB-CASE-F103/u);
|
|
assert.doesNotMatch(prompt, /CaseRun has already placed the run-local spec/u);
|
|
assert.doesNotMatch(prompt, /hwpod-ctl spec validate --spec \.hwlab\/hwpod-spec\.yaml/u);
|
|
assert.match(prompt, /hwpodId: d601-f103-v2/u);
|
|
assert.match(prompt, /hwpodWorkspaceArgs: --hwpod-id d601-f103-v2 --workspace-path 'F:\\Work\\HWLAB-CASE-F103\\.worktree\\caserun-run-agent-flow'/u);
|
|
assert.match(prompt, /hwpod-ctl spec validate --hwpod-id d601-f103-v2 --workspace-path/u);
|
|
assert.match(prompt, /不要创建、复制或修补本地 spec 文件/u);
|
|
assert.match(prompt, /标准 hwpod\/hwpod-ctl 命令/u);
|
|
assert.match(prompt, /hwpod job status <jobId>/u);
|
|
assert.match(prompt, /不要用 shell sleep、&&、timeout、watch、head、pipe/u);
|
|
assert.match(prompt, /独立短命令 `hwpod job status <jobId> --hwpod-id d601-f103-v2 --workspace-path/u);
|
|
const diff = await readFile(path.join(cwd, ".state", "hwlab-cli", "caserun", "run-agent-flow", "agent-diff.patch"), "utf8");
|
|
assert.match(diff, /printf\("hello"\)/u);
|
|
assert.equal(requests.some((item) => item.url === "http://web.test/v1/agent/sessions"), true);
|
|
assert.equal(requests.some((item) => item.url === "http://web.test/v1/agent/chat"), true);
|
|
const agentChatRequest = requests.find((item) => item.url === "http://web.test/v1/agent/chat");
|
|
assert.equal(Object.hasOwn(agentChatRequest.body, "workspaceFiles"), false);
|
|
assert.equal(Object.hasOwn(agentChatRequest.body, "hwpodSpecContent"), false);
|
|
assert.doesNotMatch(agentChatRequest.body.message, /path: "F:\\Work\\HWLAB-CASE-F103\\\.worktree\\caserun-run-agent-flow"/u);
|
|
assert.equal(requests.some((item) => item.url === "http://web.test/v1/agent/chat/result/" + result.payload.agent.traceId), true);
|
|
assert.equal(requests.some((item) => item.url === "http://web.test/v1/agent/traces/" + result.payload.agent.traceId), false);
|
|
const traceCommand = runProcessCalls.find((command) => command.includes("client") && command.includes("agent") && command.includes("trace") && command.includes("--render") && command.includes("web") && command.includes("--full"));
|
|
assert.equal(Boolean(traceCommand), true);
|
|
assert.equal(traceCommand?.includes("--base-url"), false);
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("case run archives rendered trace transcript when finalResponse is null", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const caseRepo = path.join(root, "hwlab-case-registry");
|
|
const cwd = path.join(root, "hwlab");
|
|
const runId = "run-final-null";
|
|
const runDir = path.join(cwd, ".state", "hwlab-cli", "caserun", runId);
|
|
const registryRunDir = path.join(caseRepo, "runs", "d601-f103-v2-compile", runId);
|
|
try {
|
|
await mkdir(path.join(caseRepo, ".git"), { recursive: true });
|
|
await writeFile(path.join(caseRepo, ".git", "HEAD"), "ref: refs/heads/main\n", "utf8");
|
|
await mkdir(path.join(runDir, ".hwlab"), { recursive: true });
|
|
await writeFile(path.join(runDir, ".hwlab", "hwpod-spec.yaml"), hwpodSpecText(), "utf8");
|
|
await writeFile(path.join(runDir, "agent-prompt.md"), "prompt\n", "utf8");
|
|
await writeFile(path.join(runDir, "agent-diff.patch"), "diff --git a/main.c b/main.c\n+printf(\"hello\");\n", "utf8");
|
|
await writeFile(path.join(runDir, "worker.stdout.log"), "worker complete\n", "utf8");
|
|
await writeFile(path.join(runDir, "worker.stderr.log"), "", "utf8");
|
|
const run = {
|
|
caseId: "d601-f103-v2-compile",
|
|
runId,
|
|
runDir,
|
|
caseRepo,
|
|
caseDir: "",
|
|
caseFile: "",
|
|
sourceSpecPath: "",
|
|
specPath: path.join(runDir, ".hwlab", "hwpod-spec.yaml"),
|
|
apiUrl: "http://api.test",
|
|
compileOnly: true,
|
|
createdAt: "2026-06-06T00:00:00.000Z",
|
|
updatedAt: "2026-06-06T00:00:10.000Z",
|
|
status: "completed",
|
|
stage: "completed",
|
|
definition: {},
|
|
subject: { repoLocalPath: SUBJECT_REPO_LOCAL_PATH, commitId: SUBJECT_COMMIT_ID, subdir: "projects/01_baseline", worktreePath: "F:\\Work\\HWLAB-CASE-F103\\.worktree\\caserun-run-final-null", workspacePath: "", setup: {} },
|
|
agent: { stageStatus: "failed", baseUrl: "http://web.test", projectId: "prj_hwpod_workbench", providerProfile: "deepseek", conversationId: "cnv_final_null", sessionId: "ses_final_null", threadId: "thread-final-null", traceId: "trc_final_null", promptPath: path.join(runDir, "agent-prompt.md"), promptSha256: "prompt-sha", result: { ok: true, status: "failed", finalResponse: null }, polls: 2, timedOut: false, sessionResponse: null },
|
|
agentTrace: { traceId: "trc_final_null", status: "failed", httpStatus: 200, sourceEventCount: 3, renderedRowCount: 0, commandCount: 0, hwpodCommandCount: 0, hwpodBuildCommandCount: 0, keilJobCandidates: [], terminalStatus: "failed", agentRun: { runId: "run_agent_final_null", commandId: "cmd_final_null" }, commands: [] },
|
|
agentDiff: { statusShort: " M main.c\n", diffStat: " main.c | 1 +\n", diffPatchPath: path.join(runDir, "agent-diff.patch"), diffPatchSha256: "diff-sha", sourceRootStatusShort: "", requests: [] },
|
|
evidencePath: path.join(runDir, "evidence.json"),
|
|
_control: { status: "completed", stage: "completed", stdoutPath: path.join(runDir, "worker.stdout.log"), stderrPath: path.join(runDir, "worker.stderr.log"), completedAt: "2026-06-06T00:00:10.000Z", resultPath: path.join(runDir, "result.json") }
|
|
};
|
|
const trace = {
|
|
ok: true,
|
|
status: "failed",
|
|
traceId: "trc_final_null",
|
|
finalResponse: null,
|
|
events: [
|
|
{ traceId: "trc_final_null", seq: 1, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "我先检查当前 HWPOD。", createdAt: "2026-06-06T00:00:01.000Z" },
|
|
{ traceId: "trc_final_null", seq: 2, label: "item/commandExecution:completed", type: "tool_call", toolName: "commandExecution", status: "completed", command: "/bin/sh -lc 'hwpod build --spec .hwlab/hwpod-spec.yaml'", stdoutSummary: "build completed", exitCode: 0, createdAt: "2026-06-06T00:00:02.000Z" },
|
|
{ traceId: "trc_final_null", seq: 3, label: "agentrun:terminal:failed", type: "result", terminal: true, status: "failed", message: "Error running remote compact task: unexpected status 404 Not Found", createdAt: "2026-06-06T00:00:03.000Z" }
|
|
],
|
|
rows: [
|
|
{ rowId: "trace-assistant:1", seq: 1, tone: "source", header: "00:00:01 助手消息", body: "我先检查当前 HWPOD。" },
|
|
{ rowId: "tool:build", seq: 2, tone: "ok", header: "00:00:02 ok commandExecution", bodyFormat: "text", body: "hwpod build --spec .hwlab/hwpod-spec.yaml\nstdout:\nbuild completed\nexitCode=0" },
|
|
{ rowId: "trace-terminal:3", seq: 3, tone: "blocked", header: "00:00:03 终止失败", terminal: true, body: "Error running remote compact task: unexpected status 404 Not Found" }
|
|
],
|
|
error: { message: "Error running remote compact task: unexpected status 404 Not Found" }
|
|
};
|
|
const evidence = {
|
|
contractVersion: "hwpod-case-run-evidence-v1",
|
|
caseId: run.caseId,
|
|
runId,
|
|
compileOnly: true,
|
|
autoEvaluation: false,
|
|
status: "recorded",
|
|
subject: run.subject,
|
|
agent: { traceId: "trc_final_null", baseUrl: TEST_RUNTIME_WEB_URL, sessionId: "ses_final_null", conversationId: "cnv_final_null", threadId: "thread-final-null", providerProfile: "deepseek", projectId: "prj_hwpod_workbench", promptSha256: "prompt-sha" },
|
|
agentTrace: run.agentTrace,
|
|
agentDiff: run.agentDiff,
|
|
hwpod: { source: "case-run-runner-post-agent-compile-check", exitCode: 0 },
|
|
artifacts: [],
|
|
decisions: { downloadSkipped: true }
|
|
};
|
|
await writeJsonFile(path.join(runDir, "run.json"), run);
|
|
await writeJsonFile(path.join(runDir, "agent-trace.json"), trace);
|
|
await writeJsonFile(path.join(runDir, "evidence.json"), evidence);
|
|
await writeJsonFile(path.join(runDir, "result.json"), { ok: true, action: "case.run", status: "completed", caseId: run.caseId, runId, runDir, collect: { caseRepoFiles: [] }, evidence: {} });
|
|
|
|
const result = await runHwlabCli(["case", "run", "result", runId], { cwd, now: () => "2026-06-06T00:00:11.000Z" });
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
const manifest = JSON.parse(await readFile(path.join(registryRunDir, "artifact-manifest.json"), "utf8"));
|
|
assert.equal(manifest.readableAgent.finalResponse.present, false);
|
|
assert.match(manifest.readableAgent.finalResponse.reason, /404 Not Found/u);
|
|
const messages = JSON.parse(await readFile(path.join(registryRunDir, "agent-messages.json"), "utf8"));
|
|
assert.equal(messages.finalResponse.present, false);
|
|
assert.match(JSON.stringify(messages.rows), /我先检查当前 HWPOD/u);
|
|
assert.match(JSON.stringify(messages.rows), /hwpod build --spec \.hwlab\/hwpod-spec\.yaml/u);
|
|
const traceMarkdown = await readFile(path.join(registryRunDir, "agent-trace.md"), "utf8");
|
|
assert.match(traceMarkdown, /finalResponse=null/u);
|
|
assert.match(traceMarkdown, /Error running remote compact task/u);
|
|
assert.match(traceMarkdown, /## Subject Diff/u);
|
|
assert.match(traceMarkdown, /\+printf\("hello"\);/u);
|
|
const finalMarkdown = await readFile(path.join(registryRunDir, "final-response.md"), "utf8");
|
|
assert.match(finalMarkdown, /present: false/u);
|
|
assert.match(finalMarkdown, /404 Not Found/u);
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("case aggregate writes one registry markdown entry without grading or broad registry commits", async () => {
|
|
const root = await mkdtempCaseRoot();
|
|
const caseRepo = path.join(root, "hwlab-case-registry");
|
|
const cwd = path.join(root, "hwlab");
|
|
const caseId = "d601-f103-v2-arm2d-integration";
|
|
const runId = "case04-completed-run";
|
|
const registryRunDir = path.join(caseRepo, "runs", caseId, runId);
|
|
try {
|
|
await mkdir(path.join(caseRepo, ".git"), { recursive: true });
|
|
await writeFile(path.join(caseRepo, ".git", "HEAD"), "ref: refs/heads/main\n", "utf8");
|
|
await mkdir(path.join(registryRunDir, ".hwlab"), { recursive: true });
|
|
await writeJsonFile(path.join(registryRunDir, "run.json"), {
|
|
caseId,
|
|
runId,
|
|
runDir: "/root/hwlab-v02/.state/hwlab-cli/caserun/case04-completed-run",
|
|
caseRepo,
|
|
apiUrl: TEST_RUNTIME_API_URL,
|
|
compileOnly: true,
|
|
createdAt: "2026-06-08T00:00:00.000Z",
|
|
completedAt: "2026-06-08T00:10:00.000Z",
|
|
subject: { repoLocalPath: SUBJECT_REPO_LOCAL_PATH, commitId: SUBJECT_COMMIT_ID, subdir: "projects/01_baseline", worktreePath: "F:\\Work\\HWLAB-CASE-F103\\.worktree\\caserun-case04" },
|
|
agent: { providerProfile: "dsflash-go", projectId: "prj_hwpod_workbench", conversationId: "cnv_case04", sessionId: "ses_case04", threadId: "thread-case04", traceId: "trc_case04", traceLookup: { commands: { trace: "hwlab-cli client agent trace trc_case04 --render web", result: "hwlab-cli client agent result trc_case04", inspect: "hwlab-cli client agent inspect --trace-id trc_case04" } } },
|
|
_control: { status: "completed", completedAt: "2026-06-08T00:10:00.000Z" }
|
|
});
|
|
await writeJsonFile(path.join(registryRunDir, "evidence.json"), {
|
|
contractVersion: "hwpod-case-run-evidence-v1",
|
|
caseId,
|
|
runId,
|
|
compileOnly: true,
|
|
autoEvaluation: false,
|
|
status: "recorded",
|
|
subject: { repoLocalPath: SUBJECT_REPO_LOCAL_PATH, commitId: SUBJECT_COMMIT_ID, subdir: "projects/01_baseline", worktreePath: "F:\\Work\\HWLAB-CASE-F103\\.worktree\\caserun-case04" },
|
|
agent: { providerProfile: "dsflash-go", projectId: "prj_hwpod_workbench", conversationId: "cnv_case04", sessionId: "ses_case04", threadId: "thread-case04", traceId: "trc_case04" },
|
|
traceLookup: { strategy: "id_plus_existing_cli", commands: { trace: "hwlab-cli client agent trace trc_case04 --render web", result: "hwlab-cli client agent result trc_case04", inspect: "hwlab-cli client agent inspect --trace-id trc_case04" } },
|
|
agentTrace: { traceId: "trc_case04", source: "hwlab-cli.client.agent.trace", sourceEventCount: 3, renderedRowCount: 3, hwpodCommandCount: 2, hwpodBuildCommandCount: 1, agentRun: { runId: "run_agent_case04", commandId: "cmd_case04" } },
|
|
agentDiff: { statusShort: " M projects/01_baseline/User/main.c\n", diffStat: " main.c | 3 +++\n", diffPatchSha256: "diff-sha", sourceRootChangedAfterPrepare: false, sourceRootChangedAfterAgent: true },
|
|
hwpod: { source: "case-run-runner-post-agent-compile-check", exitCode: 0 },
|
|
keilJob: { jobId: "job-case04", status: "completed" },
|
|
artifacts: [],
|
|
decisions: { runnerPostAgentCompileCheck: "recorded" }
|
|
});
|
|
const longTraceCommand = "hwpod workspace cat projects/01_baseline/Middlewares/Arm-2D/Library/include/arm_2d_types.h --spec .hwlab/hwpod-spec.yaml 2>&1 | grep -A 20 \"typedef struct arm_2d_tile_t\" 2>/dev/null || hwpod workspace cat projects/01_baseline/Middlewares/Arm-2D/Library/include/arm_2d_types.h --spec .hwlab/hwpod-spec.yaml 2>&1 | grep";
|
|
await writeJsonFile(path.join(registryRunDir, "agent-messages.json"), {
|
|
renderer: "tools/src/hwlab-cli/trace-renderer:traceDisplayRows",
|
|
sourceEventCount: 4,
|
|
renderedRowCount: 4,
|
|
rows: [
|
|
{ rowId: "row-1", seq: 1, header: "请求接受", body: "CaseRun started" },
|
|
{ rowId: "tool:hwpod-build", seq: 2, tone: "ok", toolState: "success", statusLabel: "工具调用成功", header: "00:00:02", preview: "hwpod build --spec .hwlab/hwpod-spec.yaml", body: "hwpod build --spec .hwlab/hwpod-spec.yaml\nexitCode=0" },
|
|
{ rowId: "tool:hwpod-cat", seq: 3, tone: "ok", toolState: "success", statusLabel: "工具调用成功", header: "00:00:03", preview: longTraceCommand, body: `${longTraceCommand} stdout: first line\nsecond line exitCode=0` },
|
|
{ rowId: "row-4", seq: 4, header: "助手最终消息", terminal: true, bodyFormat: "markdown", body: "Build completed" }
|
|
]
|
|
});
|
|
await writeJsonFile(path.join(registryRunDir, "artifact-manifest.json"), {
|
|
contractVersion: "hwpod-case-run-artifact-manifest-v1",
|
|
caseId,
|
|
runId,
|
|
traceLookup: { commands: { trace: "hwlab-cli client agent trace trc_case04 --render web", result: "hwlab-cli client agent result trc_case04", inspect: "hwlab-cli client agent inspect --trace-id trc_case04" } },
|
|
subject: { repoLocalPath: SUBJECT_REPO_LOCAL_PATH, commitId: SUBJECT_COMMIT_ID, subdir: "projects/01_baseline", worktreePath: "F:\\Work\\HWLAB-CASE-F103\\.worktree\\caserun-case04" },
|
|
agent: { providerProfile: "dsflash-go", projectId: "prj_hwpod_workbench", conversationId: "cnv_case04", sessionId: "ses_case04", threadId: "thread-case04", traceId: "trc_case04" },
|
|
trace: { traceId: "trc_case04", source: "hwlab-cli.client.agent.trace", hwpodCommandCount: 2, hwpodBuildCommandCount: 1, agentRun: { runId: "run_agent_case04", commandId: "cmd_case04" } },
|
|
diff: { path: "agent-diff.patch", sha256: "diff-sha", statusShort: " M projects/01_baseline/User/main.c\n", diffStat: " main.c | 3 +++\n", sourceRootChangedAfterPrepare: false, sourceRootChangedAfterAgent: true },
|
|
readableAgent: { renderer: "tools/src/hwlab-cli/trace-renderer:traceDisplayRows", finalResponse: { present: true, text: "Build completed" }, traceRender: { sourceEventCount: 3, renderedRowCount: 3 } },
|
|
keilJob: { jobId: "job-case04", status: "completed" },
|
|
files: [{ path: "evidence.json", bytes: 10, sha256: "evidence-sha" }]
|
|
});
|
|
await writeFile(path.join(registryRunDir, "agent-prompt.md"), "Implement ARM-2D integration\n", "utf8");
|
|
await writeFile(path.join(registryRunDir, "final-response.md"), "# CaseRun Final Response\n\nBuild completed\n", "utf8");
|
|
await writeFile(path.join(registryRunDir, "agent-diff.patch"), "diff --git a/main.c b/main.c\n+ d601_arm2d_demo_show();\n", "utf8");
|
|
await writeFile(path.join(registryRunDir, ".hwlab", "hwpod-spec.yaml"), hwpodSpecText(), "utf8");
|
|
await mkdir(cwd, { recursive: true });
|
|
|
|
const gitCommands: string[][] = [];
|
|
const result = await runHwlabCli(["case", "aggregate", caseId, "--case-repo", caseRepo, "--run-id", runId], {
|
|
cwd,
|
|
now: () => "2026-06-08T00:11:00.000Z",
|
|
runProcess: async (command) => {
|
|
gitCommands.push(command);
|
|
const rel = `runs/${caseId}/${runId}/aggregate.md`;
|
|
if (command[1] === "status") return { command, exitCode: 0, stdout: `?? ${rel}\n M runs/${caseId}/${runId}/result.json\n`, stderr: "" };
|
|
if (command[1] === "add") return { command, exitCode: 0, stdout: "", stderr: "" };
|
|
if (command[1] === "diff") return { command, exitCode: 1, stdout: "", stderr: "" };
|
|
if (command[1] === "commit") return { command, exitCode: 0, stdout: "[main abcdef1] data: aggregate caserun case04-completed-run\n", stderr: "" };
|
|
if (command[1] === "push") return { command, exitCode: 0, stdout: "", stderr: "To github.com:pikasTech/hwlab-case-registry.git\n" };
|
|
return { command, exitCode: 0, stdout: "", stderr: "" };
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0, JSON.stringify(result.payload, null, 2));
|
|
assert.equal(result.payload.action, "case.aggregate");
|
|
assert.equal(result.payload.autoEvaluation, false);
|
|
assert.equal(result.payload.aggregate.registrySync.status, "pushed");
|
|
assert.equal(result.payload.aggregate.registrySync.path, `runs/${caseId}/${runId}/aggregate.md`);
|
|
assert.deepEqual(gitCommands.find((command) => command[1] === "add")?.slice(-2), ["--", `runs/${caseId}/${runId}/aggregate.md`]);
|
|
assert.deepEqual(gitCommands.find((command) => command[1] === "commit")?.slice(-2), ["--", `runs/${caseId}/${runId}/aggregate.md`]);
|
|
const aggregate = await readFile(path.join(registryRunDir, "aggregate.md"), "utf8");
|
|
assert.match(aggregate, /## 运行环境信息/u);
|
|
assert.match(aggregate, /## HWPOD 信息/u);
|
|
assert.match(aggregate, /## Code Agent 信息/u);
|
|
assert.match(aggregate, /## 输入 Prompt/u);
|
|
assert.match(aggregate, /## 低噪声 Trace/u);
|
|
assert.doesNotMatch(aggregate, /• 2\. ok commandExecution/u);
|
|
assert.match(aggregate, /\*\*CaseRun started\*\*/u);
|
|
assert.match(aggregate, /- <details>\n <summary aria-label="工具调用成功:[^"]+" title="工具调用成功:[^"]+"><code>00:00:02<\/code> hwpod build --spec \.hwlab\/hwpod-spec\.yaml<\/summary>/u);
|
|
assert.match(aggregate, / ```text\n hwpod build --spec \.hwlab\/hwpod-spec\.yaml\n exitCode=0[\s\S]* <\/details>/u);
|
|
assert.match(aggregate, /<summary aria-label="工具调用成功:[^"]+" title="工具调用成功:[^"]+"><code>00:00:03<\/code> hwpod workspace cat projects\/01_baseline\/Middlewares\/Arm-2D\/Library\/include\/arm_2d_type/u);
|
|
assert.match(aggregate, / ```text\n hwpod workspace cat [^\n]+\n stdout:\n first line\n second line\n exitCode=0[\s\S]* <\/details>\n\n\*\*Build completed\*\*\n\n## Final Response/u);
|
|
for (const [, summary] of aggregate.matchAll(/<summary[^>]*>([\s\S]*?)<\/summary>/gu)) assert.doesNotMatch(summary, /\n|stdout:|stderr:|exitCode=|duration=|exit=|grep -A 20/u);
|
|
assert.doesNotMatch(aggregate, /<summary[^>]*>.*助手最终消息/u);
|
|
assert.match(aggregate, /## Final Response/u);
|
|
assert.match(aggregate, /## 最后 Diff/u);
|
|
assert.match(aggregate, /Implement ARM-2D integration/u);
|
|
assert.match(aggregate, /Build completed/u);
|
|
assert.match(aggregate, /d601_arm2d_demo_show/u);
|
|
assert.match(aggregate, /autoEvaluation: false/u);
|
|
assert.doesNotMatch(aggregate, /pass\/fail|score|打分/u);
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
async function mkdtempCaseRoot() {
|
|
return await import("node:fs/promises").then(({ mkdtemp }) => mkdtemp(path.join(os.tmpdir(), "hwlab-caserun-")));
|
|
}
|
|
|
|
async function createCaseRepo(caseRepo: string, caseId: string) {
|
|
const caseDir = path.join(caseRepo, "cases", caseId);
|
|
await mkdir(path.join(caseRepo, ".git"), { recursive: true });
|
|
await writeFile(path.join(caseRepo, ".git", "HEAD"), "ref: refs/heads/main\n", "utf8");
|
|
await mkdir(caseDir, { recursive: true });
|
|
await writeFile(path.join(caseDir, "case.json"), JSON.stringify({ contractVersion: "hwpod-case-v1", caseId, hwpodSpec: "hwpod-spec.yaml", subject: { repoLocalPath: SUBJECT_REPO_LOCAL_PATH, commitId: SUBJECT_COMMIT_ID, subdir: "projects/01_baseline" } }, null, 2), "utf8");
|
|
await writeFile(path.join(caseDir, "hwpod-spec.yaml"), hwpodSpecText(), "utf8");
|
|
}
|
|
|
|
async function writeJsonFile(file: string, value: any) {
|
|
await mkdir(path.dirname(file), { recursive: true });
|
|
await writeFile(file, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
}
|
|
|
|
function hwpodSpecText() {
|
|
return "kind: Hwpod\nmetadata:\n name: d601-f103-v2\nspec:\n nodeBinding:\n nodeId: node-d601-f103-v2\n workspace:\n path: F:\\\\Work\\\\HWLAB-CASE-F103\n toolchain: keil-mdk\n keilProject: projects/01_baseline/Projects/MDK-ARM/atk_f103.uvprojx\n keilTarget: USART\n targetDevice:\n board: D601-F103-V2\n debugProbe:\n type: daplink\n programBackend: keil\n probeUid: 95FFF39D3DB47E0D\n ioProbe:\n uart:\n port: COM9\n";
|
|
}
|
|
|
|
function subjectWorktreeFetch(requests: any[]) {
|
|
return async (url: RequestInfo | URL, init?: RequestInit) => {
|
|
const body = JSON.parse(String(init?.body ?? "{}"));
|
|
requests.push({ url: String(url), body });
|
|
const results = (body.ops ?? []).map((op: any, index: number) => {
|
|
if (op.opId === "op_02_subject_worktree_add") return { ok: true, output: { exitCode: 0, stdout: "Preparing worktree\n" } };
|
|
if (op.opId === "op_03_subject_worktree_head") return { ok: true, output: { exitCode: 0, stdout: `${SUBJECT_COMMIT_ID}\n` } };
|
|
if (op.opId === "op_04_keil_sidecars") return { ok: true, output: { exitCode: 0, stdout: "caseRunKeilSidecars {\"copied\":1,\"sourceBase\":\"projects\\\\01_baseline\\\\Projects\\\\MDK-ARM\\\\atk_f103\",\"destinationBase\":\".worktree\\\\caserun-run-test\\\\projects\\\\01_baseline\\\\Projects\\\\MDK-ARM\\\\atk_f103\"}\n" } };
|
|
return { ok: true, output: { exitCode: 0, stdout: index === 0 ? "" : "" } };
|
|
});
|
|
return new Response(JSON.stringify({ body: { results } }), { status: 200, headers: { "content-type": "application/json" } });
|
|
};
|
|
}
|
|
|
|
function caseRunFlowFetch(requests: any[]) {
|
|
let resultPolls = 0;
|
|
return async (url: RequestInfo | URL, init?: RequestInit) => {
|
|
const rawBody = String(init?.body ?? "{}");
|
|
const body = rawBody ? JSON.parse(rawBody) : {};
|
|
requests.push({ url: String(url), body });
|
|
if (String(url) === "http://web.test/v1/agent/sessions") {
|
|
return new Response(JSON.stringify({ ok: true, session: { sessionId: "ses_case_run", conversationId: body.conversationId, threadId: "thread-case", providerProfile: body.providerProfile } }), { status: 200, headers: { "content-type": "application/json" } });
|
|
}
|
|
if (String(url) === "http://web.test/v1/agent/chat") {
|
|
return new Response(JSON.stringify({ ok: true, accepted: true, traceId: body.traceId, resultUrl: `/v1/agent/chat/result/${body.traceId}` }), { status: 202, headers: { "content-type": "application/json" } });
|
|
}
|
|
if (String(url).startsWith("http://web.test/v1/agent/chat/result/")) {
|
|
resultPolls += 1;
|
|
const toolCallSummary = {
|
|
count: 3,
|
|
statusCounts: { completed: 3 },
|
|
exitCodeCounts: { "0": 3 },
|
|
items: [
|
|
{ toolName: "commandExecution", status: "completed", exitCode: 0, command: "hwpod build --spec .hwlab/hwpod-spec.yaml" },
|
|
{ toolName: "commandExecution", status: "completed", exitCode: 0, command: "hwpod download --artifact firmware.hex" },
|
|
{ toolName: "commandExecution", status: "completed", exitCode: 0, command: "hwpod uart read --timeout-ms 3000" }
|
|
]
|
|
};
|
|
return new Response(JSON.stringify({
|
|
ok: true,
|
|
status: resultPolls > 1 ? "completed" : "running",
|
|
reply: { content: "done" },
|
|
requestedProviderProfile: "deepseek",
|
|
resolvedBackendProfile: "dsflash-go",
|
|
model: "deepseek-v4-flash",
|
|
backend: "agentrun-v01/dsflash-go",
|
|
infrastructureBackend: "agentrun-v01/dsflash-go",
|
|
buildWarningCount: 2,
|
|
providerTrace: { backendProfile: "dsflash-go", model: "deepseek-v4-flash" },
|
|
agentRun: { runStatus: resultPolls > 1 ? "completed" : "running", commandStatus: resultPolls > 1 ? "completed" : "running", terminalStatus: resultPolls > 1 ? "completed" : "running" },
|
|
...(resultPolls > 1 ? { toolCallSummary } : { traceSummary: { toolCallSummary } })
|
|
}), { status: 200, headers: { "content-type": "application/json" } });
|
|
}
|
|
if (body.ops?.[0]?.args?.command === "node") {
|
|
return hwpodResponse([{ exitCode: 0, stdout: [
|
|
JSON.stringify({ path: "projects/01_baseline/new.c", bytes: 21, sha256: "a".repeat(64) }),
|
|
JSON.stringify({ path: "projects/01_baseline/build/out.bin", bytes: 4096, sha256: "b".repeat(64) })
|
|
].join("\n") }]);
|
|
}
|
|
if (body.ops?.[0]?.args?.argv?.includes("submodule")) {
|
|
return hwpodResponse([{ exitCode: 0, stdout: "+0123456789abcdef vendor/lib (heads/main)\n" }]);
|
|
}
|
|
if (body.ops?.[0]?.args?.argv?.includes("submodule")) {
|
|
return hwpodResponse([{ exitCode: 0, stdout: "+0123456789abcdef vendor/lib (heads/main)\n" }]);
|
|
}
|
|
if (body.ops?.[0]?.args?.argv?.includes("ls-files")) {
|
|
return hwpodResponse([{ exitCode: 0, stdout: "projects/01_baseline/new.c\nprojects/01_baseline/build/out.bin\n" }]);
|
|
}
|
|
if (body.ops?.[0]?.args?.argv?.includes("--no-index") && body.ops[0].args.argv.includes("--stat")) {
|
|
return hwpodResponse([{ exitCode: 1, stdout: " projects/01_baseline/new.c | 1 +\n" }]);
|
|
}
|
|
if (body.ops?.[0]?.args?.argv?.includes("--no-index") && body.ops[0].args.argv.includes("--binary")) {
|
|
return hwpodResponse([{ exitCode: 1, stdout: "diff --git a/dev/null b/projects/01_baseline/new.c\nnew file mode 100644\n--- a/dev/null\n+++ b/projects/01_baseline/new.c\n@@ -0,0 +1 @@\n+int added = 1;\n" }]);
|
|
}
|
|
if (body.ops?.[0]?.args?.argv?.includes("status") && body.ops?.length === 1) {
|
|
return hwpodResponse([{ exitCode: 0, stdout: body.ops[0].args.argv.includes("--short") ? " M projects/01_baseline/main.c\n?? projects/01_baseline/new.c\n?? projects/01_baseline/build/out.bin\n" : "" }]);
|
|
}
|
|
if (body.ops?.[0]?.args?.argv?.includes("--stat")) {
|
|
return hwpodResponse([{ exitCode: 0, stdout: " projects/01_baseline/main.c | 1 +\n" }]);
|
|
}
|
|
if (body.ops?.[0]?.args?.argv?.includes("--binary")) {
|
|
return hwpodResponse([{ exitCode: 0, stdout: "+printf(\"hello\");\n" }]);
|
|
}
|
|
if (body.ops?.[0]?.args?.argv?.includes("job-status")) {
|
|
return hwpodResponse([{ exitCode: 0, stdout: JSON.stringify({ status: "completed", return_code: 0, warning_count: 7, result: { hex_file: "F:\\out.hex", axf_file: "F:\\out.axf" } }) }]);
|
|
}
|
|
return subjectWorktreeFetch(requests)(url, init);
|
|
};
|
|
}
|
|
|
|
function hwpodResponse(outputs: Array<{ exitCode: number; stdout?: string; stderr?: string }>) {
|
|
return new Response(JSON.stringify({ body: { results: outputs.map((output) => ({ ok: output.exitCode === 0, output })) } }), { status: 200, headers: { "content-type": "application/json" } });
|
|
}
|