From 5df18caffad15ce63651642be00b261aefa37853 Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Sun, 7 Jun 2026 01:05:52 +0800 Subject: [PATCH] fix: smooth caserun trace and uart hints --- tools/hwlab-cli/caserun.test.ts | 8 ++++---- tools/hwpod-harness.test.ts | 12 ++++++++++++ tools/src/hwlab-caserun-lib.ts | 10 +++++----- tools/src/hwpod-harness-lib.ts | 10 +++++----- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/tools/hwlab-cli/caserun.test.ts b/tools/hwlab-cli/caserun.test.ts index 8042a156..851c54c8 100644 --- a/tools/hwlab-cli/caserun.test.ts +++ b/tools/hwlab-cli/caserun.test.ts @@ -354,9 +354,9 @@ test("case run orchestrates agent prompt, diff capture, and compile evidence wit 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.traceLookup.commands.trace, `hwlab-cli client agent trace ${result.payload.agent.traceId} --base-url http://web.test --render web`); - assert.equal(result.payload.traceLookup.commands.result, `hwlab-cli client agent result ${result.payload.agent.traceId} --base-url http://web.test`); - assert.equal(result.payload.traceLookup.commands.inspect, `hwlab-cli client agent inspect --trace-id ${result.payload.agent.traceId} --base-url http://web.test`); + 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, 1); @@ -645,7 +645,7 @@ test("case run status reports latest run stage when async control stage is stale assert.equal(result.exitCode, 0); assert.equal(result.payload.ready, false); assert.equal(result.payload.stage, "agent-diff-collected"); - assert.equal(result.payload.traceLookup.commands.trace, "hwlab-cli client agent trace trc_case --base-url http://web.test --render web"); + assert.equal(result.payload.traceLookup.commands.trace, "hwlab-cli client agent trace trc_case --render web"); assert.equal(result.payload.staleControlWarning.code, "stale_control_stage"); } finally { await rm(root, { recursive: true, force: true }); diff --git a/tools/hwpod-harness.test.ts b/tools/hwpod-harness.test.ts index bb303b7e..f70d2858 100644 --- a/tools/hwpod-harness.test.ts +++ b/tools/hwpod-harness.test.ts @@ -170,6 +170,11 @@ test("hwpod-cli dry-run invokes hwpod-compiler-cli and exposes hwpod-node-ops pl assert.equal(insert.payload.plan.ops[0].args.line, " /* MARKER_OK */"); assert.equal(insert.payload.plan.ops[0].args.expectedSha, "abc123"); + const insertAliases = await runHwpodCli(["workspace", "insert-after", "--spec", specPath, "--path", "projects/01_baseline/User/main.c", "--marker", "device-pod LCD bring-up", "--content", " /* MARKER_ALIAS */", "--dry-run"], { now: () => NOW }); + assert.equal(insertAliases.exitCode, 0); + assert.equal(insertAliases.payload.plan.ops[0].args.anchor, "device-pod LCD bring-up"); + assert.equal(insertAliases.payload.plan.ops[0].args.line, " /* MARKER_ALIAS */"); + const replace = await runHwpodCli(["workspace", "replace", "--spec", specPath, "--path", "projects/01_baseline/User/main.c", "--find", " /* MARKER_OK */", "--replace", "", "--dry-run"], { now: () => NOW }); assert.equal(replace.exitCode, 0); assert.equal(replace.payload.plan.ops[0].op, "workspace.replace"); @@ -192,6 +197,13 @@ test("hwpod-cli dry-run invokes hwpod-compiler-cli and exposes hwpod-node-ops pl const uartSequence = JSON.parse(uart.payload.plan.ops[0].args.argv[2]); assert.deepEqual(uartSequence[0], ["bun", "scripts/serial-monitor-cli.ts", "monitor", "start", "-p", "COM9", "-b", "115200"]); assert.deepEqual(uartSequence[1].slice(0, 5), ["bun", "scripts/serial-monitor-cli.ts", "fetch", "-l", "7"]); + assert.equal(uartSequence[1].includes("--session-only"), false); + assert.equal(uart.payload.plan.ops[0].args.commandBinding.sessionOnly, false); + + const uartSessionOnly = await runHwpodCli(["uart", "read", "--spec", specPath, "--port", "uart1", "--max-bytes", "512", "--session-only", "--dry-run"], { now: () => NOW }); + const uartSessionSequence = JSON.parse(uartSessionOnly.payload.plan.ops[0].args.argv[2]); + assert.equal(uartSessionSequence[1].includes("--session-only"), true); + assert.equal(uartSessionOnly.payload.plan.ops[0].args.commandBinding.sessionOnly, true); } finally { await rm(root, { recursive: true, force: true }); } diff --git a/tools/src/hwlab-caserun-lib.ts b/tools/src/hwlab-caserun-lib.ts index bb666e8a..b5deacd0 100644 --- a/tools/src/hwlab-caserun-lib.ts +++ b/tools/src/hwlab-caserun-lib.ts @@ -876,11 +876,11 @@ function agentTraceLookup(input: { baseUrl: string; traceId: string; sessionId?: const traceId = input.traceId; const base = input.baseUrl; const commands = clean({ - result: `hwlab-cli client agent result ${traceId} --base-url ${base}`, - trace: `hwlab-cli client agent trace ${traceId} --base-url ${base} --render web`, - traceFull: `hwlab-cli client agent trace ${traceId} --base-url ${base} --render web --full`, - inspect: `hwlab-cli client agent inspect --trace-id ${traceId} --base-url ${base}`, - sessionStatus: input.sessionId ? `hwlab-cli client agent session status ${input.sessionId} --base-url ${base}` : undefined + result: `hwlab-cli client agent result ${traceId}`, + trace: `hwlab-cli client agent trace ${traceId} --render web`, + traceFull: `hwlab-cli client agent trace ${traceId} --render web --full`, + inspect: `hwlab-cli client agent inspect --trace-id ${traceId}`, + sessionStatus: input.sessionId ? `hwlab-cli client agent session status ${input.sessionId}` : undefined }); return clean({ source: "hwlab-cli.client.agent", diff --git a/tools/src/hwpod-harness-lib.ts b/tools/src/hwpod-harness-lib.ts index fc0e2bc0..896b3b7c 100644 --- a/tools/src/hwpod-harness-lib.ts +++ b/tools/src/hwpod-harness-lib.ts @@ -14,7 +14,7 @@ const COMPILER_NAME = "hwpod-compiler-cli"; const CTL_NAME = "hwpod-ctl"; const CLI_NAME = "hwpod-cli"; const DEFAULT_TIMEOUT_MS = 30000; -const BOOLEAN_OPTIONS = new Set(["all", "allowMultiple", "dryRun", "finalNewline", "force", "full", "help", "h", "json", "noAuth", "wait"]); +const BOOLEAN_OPTIONS = new Set(["all", "allowMultiple", "dryRun", "finalNewline", "force", "full", "help", "h", "json", "noAuth", "sessionOnly", "wait"]); const DEFAULT_KEIL_COMMAND_TIMEOUT_MS = 30000; type EnvLike = Record; type ParsedArgs = Record & { _: string[] }; @@ -424,7 +424,7 @@ function commandToIntent(parsed: ParsedArgs, stdinText?: string) { if (subcommand === "apply-patch") return { intent: "workspace.apply-patch", args: { patch: patchText(parsed.patch ?? parsed.patchText ?? stdinText), patchBase64: text(parsed.patchBase64), reason: text(parsed.reason) } }; if (subcommand === "write") return { intent: "workspace.write", args: clean({ path: requiredWorkspacePath(parsed, 2), content: patchText(parsed.content ?? parsed.contentText ?? stdinText), contentBase64: text(parsed.contentBase64), expectedSha: text(parsed.expectedSha), lineEnding: text(parsed.lineEnding), finalNewline: parsed.finalNewline === true, dryRun: parsed.dryRun === true, reason: text(parsed.reason) }) }; if (subcommand === "replace") return { intent: "workspace.replace", args: cleanTextEditArgs({ path: requiredWorkspacePath(parsed, 2), find: requiredRawString(parsed.find ?? parsed._[3], "find"), replace: rawString(parsed.replace ?? parsed.replacement ?? parsed._[4]), expectedSha: text(parsed.expectedSha), all: parsed.all === true, dryRun: parsed.dryRun === true, reason: text(parsed.reason) }) }; - if (subcommand === "insert-after") return { intent: "workspace.insert-after", args: cleanTextEditArgs({ path: requiredWorkspacePath(parsed, 2), anchor: requiredRawString(parsed.anchor ?? parsed._[3], "anchor"), line: requiredRawString(parsed.line ?? parsed.insert ?? parsed.text ?? parsed._[4], "line"), expectedSha: text(parsed.expectedSha), allowMultiple: parsed.allowMultiple === true, dryRun: parsed.dryRun === true, reason: text(parsed.reason) }) }; + if (subcommand === "insert-after") return { intent: "workspace.insert-after", args: cleanTextEditArgs({ path: requiredWorkspacePath(parsed, 2), anchor: requiredRawString(parsed.anchor ?? parsed.marker ?? parsed._[3], "anchor"), line: requiredRawString(parsed.line ?? parsed.content ?? parsed.insert ?? parsed.text ?? parsed._[4], "line"), expectedSha: text(parsed.expectedSha), allowMultiple: parsed.allowMultiple === true, dryRun: parsed.dryRun === true, reason: text(parsed.reason) }) }; throw cliError("unsupported_workspace_command", `unsupported workspace command: ${subcommand}`); } if (command === "build") return { intent: "debug.build", args: clean({ target: text(parsed.target), command: text(parsed.command ?? parsed.commandLine), wait: parsed.wait === true, timeoutMs: numberValue(parsed.timeoutMs), reason: text(parsed.reason) }) }; @@ -437,7 +437,7 @@ function commandToIntent(parsed: ParsedArgs, stdinText?: string) { if (command === "reset") return { intent: "debug.reset", args: clean({ mode: text(parsed.mode ?? parsed._[1]), command: text(parsed.command ?? parsed.commandLine), reason: text(parsed.reason) }) }; if (command === "uart") { const subcommand = parsed._[1] || "read"; - if (subcommand === "read") return { intent: "io.uart.read", args: clean({ port: text(parsed.port ?? parsed._[2] ?? "uart1"), maxBytes: numberValue(parsed.maxBytes), limit: numberValue(parsed.limit), since: text(parsed.since), timeoutMs: numberValue(parsed.timeoutMs), serialMonitorDir: text(parsed.serialMonitorDir), serialMonitorCommand: text(parsed.serialMonitorCommand), reason: text(parsed.reason) }) }; + if (subcommand === "read") return { intent: "io.uart.read", args: clean({ port: text(parsed.port ?? parsed._[2] ?? "uart1"), maxBytes: numberValue(parsed.maxBytes), limit: numberValue(parsed.limit), since: text(parsed.since), sessionOnly: parsed.sessionOnly === true, timeoutMs: numberValue(parsed.timeoutMs), serialMonitorDir: text(parsed.serialMonitorDir), serialMonitorCommand: text(parsed.serialMonitorCommand), reason: text(parsed.reason) }) }; if (subcommand === "write") return { intent: "io.uart.write", args: clean({ port: text(parsed.port ?? parsed._[2] ?? "uart1"), data: requiredText(parsed.data ?? parsed._[3] ?? stdinText, "data") }) }; throw cliError("unsupported_uart_command", `unsupported uart command: ${subcommand}`); } @@ -685,7 +685,7 @@ function serialMonitorReadCommand(args: any, document: any) { const commandBase = serialMonitorCommandBaseForCompiler(args, tooling, ioProbe); const timeoutMs = numberValue(args.timeoutMs) ?? 30000; const limit = Math.max(1, Math.min(numberValue(args.limit) ?? Math.ceil((numberValue(args.maxBytes) ?? 4096) / 80), 200)); - const fetchArgs = ["fetch", "-l", String(limit), "--session-only", ...(text(args.since) ? ["-s", text(args.since)] : [])]; + const fetchArgs = ["fetch", "-l", String(limit), ...(args.sessionOnly === true ? ["--session-only"] : []), ...(text(args.since) ? ["-s", text(args.since)] : [])]; const sequenceRun = jsonCliSequenceCommandRun([ [...commandBase, "monitor", "start", "-p", physicalPort, "-b", String(baudRate)], [...commandBase, ...fetchArgs] @@ -695,7 +695,7 @@ function serialMonitorReadCommand(args: any, document: any) { command: sequenceRun.commandLine, commandRun: sequenceRun.commandRun, timeoutMs, - binding: clean({ kind: "serial-monitor-cli", source: "hwpod-compiler.serial-monitor", action: "uart-read", requestedPort, physicalPort, baudRate, serialMonitorDir }) + binding: clean({ kind: "serial-monitor-cli", source: "hwpod-compiler.serial-monitor", action: "uart-read", requestedPort, physicalPort, baudRate, serialMonitorDir, sessionOnly: args.sessionOnly === true }) }; }