From 766dd16828bf88ba2fd48cacb973d26465c0b1f3 Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Tue, 23 Jun 2026 20:46:56 +0800 Subject: [PATCH] fix(web-probe): recover run reports with compact summaries (#751) Co-authored-by: Codex --- scripts/src/hwlab-node-impl.ts | 122 ++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 8 deletions(-) diff --git a/scripts/src/hwlab-node-impl.ts b/scripts/src/hwlab-node-impl.ts index 1059e17e..43f39015 100644 --- a/scripts/src/hwlab-node-impl.ts +++ b/scripts/src/hwlab-node-impl.ts @@ -7446,7 +7446,7 @@ function runNodeWebProbe(options: NodeWebProbeOptions): Record : 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 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 | 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, renderedText: s }; } +function renderWebProbeRunResult(result: Record): Record { + 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): Record { const observer = record(result.observer); const heartbeat = record(observer.heartbeat);