fix(web-probe): recover run reports with compact summaries (#751)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-23 20:46:56 +08:00
committed by GitHub
parent 1f4c520054
commit 766dd16828
+114 -8
View File
@@ -7446,7 +7446,7 @@ function runNodeWebProbe(options: NodeWebProbeOptions): Record<string, unknown>
: typeof probe?.degradedReason === "string"
? probe.degradedReason
: null;
return {
return renderWebProbeRunResult({
ok: passed,
status: passed ? "pass" : "blocked",
command: `hwlab nodes web-probe run --node ${options.node} --lane ${options.lane}`,
@@ -7468,7 +7468,7 @@ function runNodeWebProbe(options: NodeWebProbeOptions): Record<string, unknown>
probe,
result: compactCommandResult(result),
valuesRedacted: true,
};
});
}
function nodeWebProbeRunArgs(options: NodeWebProbeRunOptions, command: "run" | "start"): string[] {
@@ -7507,7 +7507,7 @@ function runNodeWebProbeAsync(
const start = parseJsonObject(startResult.stdout);
const jobId = typeof start?.jobId === "string" ? start.jobId : null;
if (startResult.exitCode !== 0 || jobId === null) {
return {
return renderWebProbeRunResult({
ok: false,
status: "blocked",
command: `hwlab nodes web-probe run --node ${options.node} --lane ${options.lane}`,
@@ -7523,7 +7523,7 @@ function runNodeWebProbeAsync(
start: start ?? null,
result: compactCommandResultRedacted(startResult, [material.password ?? ""]),
valuesRedacted: true,
};
});
}
const poll = pollNodeWebProbeJob(options, spec, jobId);
const statusReport = record(record(poll.status).report);
@@ -7532,6 +7532,7 @@ function runNodeWebProbeAsync(
? { source: "status", report: statusReport, result: null as CommandResult | null, degradedReason: null as string | null, path: null as string | null }
: readNodeWebProbeReport(options, spec, reportPath);
const report = reportLoad.report ?? {};
const reportRecovered = Object.keys(report).length > 0;
const probe = compactWebProbeResult(Object.keys(report).length > 0 ? report : null);
const passed = probe?.status === "pass";
const summary = nullableRecord(probe?.summary);
@@ -7540,9 +7541,11 @@ function runNodeWebProbeAsync(
: typeof probe?.degradedReason === "string"
? probe.degradedReason
: poll.status === null
? reportLoad.degradedReason ?? "web-probe-async-status-failed"
? reportRecovered
? reportLoad.degradedReason
: reportLoad.degradedReason ?? "web-probe-async-status-failed"
: reportLoad.degradedReason;
return {
return renderWebProbeRunResult({
ok: passed,
status: passed ? "pass" : "blocked",
command: `hwlab nodes web-probe run --node ${options.node} --lane ${options.lane}`,
@@ -7581,7 +7584,7 @@ function runNodeWebProbeAsync(
degradedReason: reportLoad.degradedReason,
},
valuesRedacted: true,
};
});
}
function readNodeWebProbeReport(
@@ -7591,7 +7594,52 @@ function readNodeWebProbeReport(
): { source: string; report: Record<string, unknown> | null; result: CommandResult | null; degradedReason: string | null; path: string | null } {
if (!reportPath) return { source: "missing", report: null, result: null, degradedReason: "web-probe-report-path-missing", path: null };
if (!isSafeWebProbeReportPath(reportPath)) return { source: "unsafe-path", report: null, result: null, degradedReason: "web-probe-report-path-invalid", path: reportPath };
const result = runTransWorkspaceStdinScript(options.node, spec.workspace, `cat ${shellQuote(reportPath)}`, 55);
const script = [
"set -eu",
`test -f ${shellQuote(reportPath)}`,
`node - ${shellQuote(reportPath)} <<'NODE'`,
"const fs = require('fs');",
"const reportPath = process.argv[2];",
"const report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));",
"function rec(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }",
"function compact(value, depth = 0) {",
" if (value === null || value === undefined) return value ?? null;",
" if (typeof value === 'string') return value.replace(/\\s+/gu, ' ').trim().slice(0, 600);",
" if (typeof value === 'number' || typeof value === 'boolean') return value;",
" if (depth >= 6) return '[max-depth]';",
" if (Array.isArray(value)) return value.slice(0, 16).map((item) => compact(item, depth + 1));",
" if (typeof value === 'object') {",
" const out = {};",
" for (const [key, nested] of Object.entries(value).slice(0, 32)) out[key] = compact(nested, depth + 1);",
" return out;",
" }",
" return String(value).slice(0, 600);",
"}",
"const artifacts = rec(report.artifacts);",
"const compactReport = {",
" ok: report.ok === true,",
" status: typeof report.status === 'string' ? report.status : null,",
" finalUrl: typeof report.finalUrl === 'string' ? report.finalUrl : null,",
" error: typeof report.error === 'string' ? report.error : null,",
" degradedReason: typeof report.degradedReason === 'string' ? report.degradedReason : null,",
" actions: compact(report.actions),",
" session: compact(report.session),",
" trace: compact(report.trace),",
" promptValidation: compact(report.promptValidation),",
" performance: compact(report.performance),",
" traceSamples: compact(report.traceSamples),",
" dom: compact(report.dom),",
" failureDom: compact(report.failureDom),",
" artifacts: {",
" ...compact(artifacts),",
" reportPath: typeof artifacts.reportPath === 'string' ? artifacts.reportPath : reportPath,",
" },",
" safety: compact(report.safety),",
"};",
"console.log(JSON.stringify(compactReport));",
"NODE",
].join("\n");
const result = runTransWorkspaceStdinScript(options.node, spec.workspace, script, 55);
if (result.exitCode !== 0 || result.timedOut) return { source: "report-file", report: null, result, degradedReason: result.timedOut ? "web-probe-command-timeout" : "web-probe-report-read-failed", path: reportPath };
const report = parseJsonObject(result.stdout);
return {
@@ -9563,6 +9611,64 @@ function withWebObserveRendered(result: Record<string, unknown>, renderedText: s
};
}
function renderWebProbeRunResult(result: Record<string, unknown>): Record<string, unknown> {
const summary = record(result.summary);
const probe = record(result.probe);
const trace = record(probe.trace);
const session = record(probe.session);
const artifacts = record(probe.artifacts);
const reportLoad = record(result.reportLoad);
const commandTimeout = record(result.commandTimeout);
const job = record(result.job);
const start = record(result.start);
const commandResult = record(result.result ?? result.statusResult);
const reportPath = reportLoad.path ?? summary.reportPath ?? artifacts.reportPath ?? start.reportPath ?? "-";
const reportSource = reportLoad.source ?? (result.mode === "async" ? "-" : "stdout");
const blockedRows = result.ok === true ? [] : [
"",
webObserveTable(
["BLOCKED_FIELD", "VALUE"],
[
["failureKind", result.failureKind ?? summary.failureKind ?? "-"],
["failedCondition", summary.failedCondition ?? result.degradedReason ?? "-"],
["nextAction", summary.nextAction ?? "-"],
["exitCode", commandResult.exitCode ?? "-"],
["timedOut", commandResult.timedOut ?? commandTimeout.timedOut ?? "-"],
],
),
];
const renderedText = [
webObserveTable(
["WEB_PROBE_RUN", "NODE", "LANE", "STATUS", "OK", "DEGRADED", "URL"],
[[result.command ?? "web-probe run", result.node, result.lane, result.status, result.ok, result.degradedReason ?? "-", result.url]],
),
"",
webObserveTable(
["FIELD", "VALUE"],
[
["mode", result.mode ?? (result.reportLoad ? "async" : "direct")],
["traceId", summary.traceId ?? trace.traceId ?? "-"],
["sessionId", summary.sessionId ?? session.sessionId ?? "-"],
["conversationId", summary.conversationId ?? session.conversationId ?? "-"],
["agentStatus", summary.agentStatus ?? trace.finalAgentStatus ?? "-"],
["traceStatus", summary.traceStatus ?? trace.finalTraceStatus ?? "-"],
["messageCount", summary.messageCount ?? session.messageCount ?? "-"],
["reportSource", reportSource],
["reportPath", reportPath],
["reportSha256", summary.reportSha256 ?? artifacts.reportSha256 ?? "-"],
["timeout", `${commandTimeout.seconds ?? "-"}s timedOut=${commandTimeout.timedOut ?? "-"}`],
["job", job.jobId ?? "-"],
],
),
...blockedRows,
"",
"NEXT",
` report: ${reportPath}`,
` rerun: ${result.command ?? `hwlab nodes web-probe run --node ${result.node ?? "-"} --lane ${result.lane ?? "-"}`}`,
].join("\n");
return withWebObserveRendered(result, renderedText);
}
function renderWebObserveStartResult(result: Record<string, unknown>): Record<string, unknown> {
const observer = record(result.observer);
const heartbeat = record(observer.heartbeat);