From 5fd3a1f07d81f4a476f932e170686a0a8853bb1f Mon Sep 17 00:00:00 2001 From: AgentRun Codex Date: Sun, 12 Jul 2026 22:08:08 +0000 Subject: [PATCH] fix: compile recovered analyze heredoc --- .../web-probe-observe-options.test.ts | 17 +++++++++++++++++ scripts/src/hwlab-node/web-probe-observe.ts | 5 ++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/src/hwlab-node/web-probe-observe-options.test.ts b/scripts/src/hwlab-node/web-probe-observe-options.test.ts index 31cb2d8d..7e89ca24 100644 --- a/scripts/src/hwlab-node/web-probe-observe-options.test.ts +++ b/scripts/src/hwlab-node/web-probe-observe-options.test.ts @@ -1,4 +1,8 @@ import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import { test } from "bun:test"; import { hwlabRuntimeLaneSpecForNode } from "../hwlab-node-lanes"; @@ -25,8 +29,21 @@ test("artifact recovery helper projects report time without filesystem mtime", ( }); assert.equal(recovered?.reportUpdatedAt, "2026-07-12T00:00:00.000Z"); assert.match(recoveryScript, /const reportJson = objectOrNull\(readJson\(reportJsonPath\)\) \|\| \{\};/u); + assert.equal((recoveryScript.match(/const reportJson =/gu) ?? []).length, 1); assert.match(recoveryScript, /source\.reportUpdatedAt \?\? source\.updatedAt \?\? source\.analyzedAt \?\? reportJson\.reportUpdatedAt \?\? reportJson\.updatedAt \?\? reportJson\.analyzedAt/u); assert.doesNotMatch(recoveryScript, /mtime/u); + const marker = "UNIDESK_WEB_OBSERVE_RECOVER_ANALYZE_ARTIFACT"; + const start = recoveryScript.indexOf(`<<'${marker}'\n`) + marker.length + 5; + const end = recoveryScript.indexOf(`\n${marker}`, start); + const directory = mkdtempSync(join(tmpdir(), "web-observe-recovery-")); + try { + const programPath = join(directory, "recovery.js"); + writeFileSync(programPath, recoveryScript.slice(start, end)); + const syntax = spawnSync(process.execPath, ["--check", programPath], { encoding: "utf8" }); + assert.equal(syntax.status, 0, syntax.stderr); + } finally { + rmSync(directory, { recursive: true, force: true }); + } }); test("validateRealtimeFanout parses the YAML profile and two independent prompts", () => { diff --git a/scripts/src/hwlab-node/web-probe-observe.ts b/scripts/src/hwlab-node/web-probe-observe.ts index d6bc72ac..379db79e 100644 --- a/scripts/src/hwlab-node/web-probe-observe.ts +++ b/scripts/src/hwlab-node/web-probe-observe.ts @@ -2451,7 +2451,6 @@ export function recoverWebObserveAnalyzeFromArtifacts(options: NodeWebProbeObser "const statSize = (path) => { try { return fs.statSync(path).size; } catch { return 0; } };", "const sha256 = (path) => { const text = readText(path); return text ? 'sha256:' + crypto.createHash('sha256').update(text).digest('hex') : null; };", "const objectOrNull = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : null;", - "const reportJson = objectOrNull(readJson(reportJsonPath)) || {};", "const arr = (value) => Array.isArray(value) ? value : [];", "const clip = (value, limit = 160) => value === null || value === undefined ? null : String(value).slice(0, limit);", "const numberish = (...values) => { for (const value of values) { const n = Number(value); if (Number.isFinite(n)) return value; } return null; };", @@ -2471,7 +2470,7 @@ export function recoverWebObserveAnalyzeFromArtifacts(options: NodeWebProbeObser "const compactTraceOrder = (value) => { const v = objectOrNull(value); if (!v) return null; const s = objectOrNull(v.summary) || {}; return { summary: { sampleCount: v.sampleCount ?? s.sampleCount ?? null, traceRowCount: v.traceRowCount ?? s.traceRowCount ?? null, orderAnomalyCount: v.orderAnomalyCount ?? s.orderAnomalyCount ?? arr(v.orderAnomalies).length, completionNotLastCount: v.completionNotLastCount ?? s.completionNotLastCount ?? arr(v.completionNotLast).length }, orderAnomalies: arr(v.orderAnomalies).slice(0, 8) }; };", "const compactMetrics = (value) => { const v = objectOrNull(value) || {}; const s = objectOrNull(v.summary) || {}; return { sampleCount: numberish(v.sampleCount, s.sampleCount), rounds: arr(v.rounds ?? v.roundItems).slice(-8).map(slimRound), roundItems: arr(v.roundItems ?? v.rounds).slice(-8).map(slimRound), turnColumns: arr(v.turnColumns).slice(-12).map(slimTurnColumn), turnTimingRows: numberish(v.turnTimingRows, s.turnTimingRows), turnTimingNonMonotonicCount: numberish(v.turnTimingNonMonotonicCount, s.turnTimingNonMonotonicCount), turnTimingTotalElapsedDecreaseCount: numberish(v.turnTimingTotalElapsedDecreaseCount, s.turnTimingTotalElapsedDecreaseCount), turnTimingTotalElapsedZeroResetCount: numberish(v.turnTimingTotalElapsedZeroResetCount, s.turnTimingTotalElapsedZeroResetCount), turnTimingTotalElapsedForwardJumpCount: numberish(v.turnTimingTotalElapsedForwardJumpCount, s.turnTimingTotalElapsedForwardJumpCount), turnTimingTerminalElapsedGrowthCount: numberish(v.turnTimingTerminalElapsedGrowthCount, s.turnTimingTerminalElapsedGrowthCount), turnTimingRecentUpdateJumpCount: numberish(v.turnTimingRecentUpdateJumpCount, v.turnTimingRecentUpdateSawtoothJumpCount, s.turnTimingRecentUpdateJumpCount, s.turnTimingRecentUpdateSawtoothJumpCount), turnTimingRecentUpdateMaxIncreaseSeconds: numberish(v.turnTimingRecentUpdateMaxIncreaseSeconds, s.turnTimingRecentUpdateMaxIncreaseSeconds), loading: compactLoading(v.loading), traceOrder: compactTraceOrder(v.traceOrder), sessionRailTitles: compactSessionRailTitles(v.sessionRailTitles), promptSegments: numberish(v.promptSegments, s.promptSegments), loadingSampleCount: numberish(v.loadingSampleCount, s.loadingSampleCount), loadingMaxCount: numberish(v.loadingMaxCount, s.loadingMaxCount), loadingMaxOwnerCount: numberish(v.loadingMaxOwnerCount, s.loadingMaxOwnerCount), loadingOwnerCount: numberish(v.loadingOwnerCount, s.loadingOwnerCount), loadingLongestContinuousSeconds: numberish(v.loadingLongestContinuousSeconds, s.loadingLongestContinuousSeconds), loadingCurrentContinuousSeconds: numberish(v.loadingCurrentContinuousSeconds, s.loadingCurrentContinuousSeconds), loadingOverFiveSecondSegmentCount: numberish(v.loadingOverFiveSecondSegmentCount, s.loadingOverFiveSecondSegmentCount) }; };", "const stdoutJson = readJson(stdoutPath);", - "const reportJson = readJson(reportJsonPath);", + "const reportJson = objectOrNull(readJson(reportJsonPath)) || {};", "const source = objectOrNull(stdoutJson) || objectOrNull(reportJson);", "if (!source) { console.log(JSON.stringify({ ok: false, command: 'web-probe-observe analyze', stateDir, error: 'web-probe-analyzer-artifacts-missing', analyzer: { recoveredFrom: 'analysis-artifact-after-transport-timeout', stdoutPath, stderrPath, reportJsonPath, reportMdPath, stdoutBytes: statSize(stdoutPath), stderrBytes: statSize(stderrPath), reportJsonBytes: statSize(reportJsonPath), reportMdBytes: statSize(reportMdPath), transportExitCode: Number(transportExitRaw), transportTimedOut: transportTimedOutRaw === 'true', valuesRedacted: true }, valuesRedacted: true })); process.exit(0); }", "const recent = objectOrNull(source.windows?.recent) || {};", @@ -2489,7 +2488,7 @@ export function recoverWebObserveAnalyzeFromArtifacts(options: NodeWebProbeObser "const archiveSummary = objectOrNull(source.archiveSummary) || {};", "const archiveSampleMetrics = objectOrNull(archiveSummary.sampleMetrics) || objectOrNull(source.sampleMetrics?.summary) || objectOrNull(srcMetrics.summary) || {};", "const slowApis = arr(source.pagePerformanceSlowApi).length > 0 ? arr(source.pagePerformanceSlowApi) : arr(pagePerformance.sameOriginApiByPath).filter((item) => Number(item?.overBudgetCount ?? item?.overFiveSecondCount ?? 0) > 0);", - "const compact = { ok: source.ok === true, command: source.command ?? 'web-probe-observe analyze', stateDir: source.stateDir ?? stateDir, jsonlScope: source.jsonlScope ?? null, alertThresholds: source.alertThresholds ?? null, counts: source.counts ?? null, archiveSummary: { ...archiveSummary, sampleMetrics: archiveSampleMetrics, pagePerformance: objectOrNull(archiveSummary.pagePerformance) || objectOrNull(pagePerformance.summary) || {}, runtimeAlerts: objectOrNull(archiveSummary.runtimeAlerts) || objectOrNull(runtimeAlerts.summary) || {}, browserProcess: objectOrNull(archiveSummary.browserProcess) || objectOrNull(browserProcess.summary) || {}, requestRate: objectOrNull(archiveSummary.requestRate) || requestRateSummary || {}, frontendPerformance: objectOrNull(archiveSummary.frontendPerformance) || frontendPerformanceSummary || {}, redFindings: arr(archiveSummary.redFindings).slice(0, 12).map(slimFinding) }, analysisWindow: source.analysisWindow ?? objectOrNull(recent.summary), sampleMetrics: compactMetrics(srcMetrics), pageProvenance: objectOrNull(source.pageProvenance?.summary) || source.pageProvenance ?? null, pagePerformance: objectOrNull(pagePerformance.summary) || pagePerformance, projectManagement: objectOrNull(source.projectManagement) || null, promptNetwork: objectOrNull(promptNetwork.summary) || promptNetwork, requestRate: requestRateSummary, requestRateCurve, requestRatePeaks, frontendPerformance: frontendPerformanceSummary, frontendPerformanceHotspots, runtimeAlerts: objectOrNull(runtimeAlerts.summary) || runtimeAlerts, browserProcess: objectOrNull(browserProcess.summary) || browserProcess, runnerErrors: arr(source.runnerErrors).slice(-8), commandFailures: arr(source.commandFailures).slice(-8), commandState: objectOrNull(source.commandState) || null, toolFindings: arr(source.toolFindings).slice(0, 8).map(slimFinding), httpErrorGroups: arr(source.httpErrorGroups ?? runtimeAlerts.networkHttpErrorsByPath).slice(0, 8).map(slimGroup), requestFailedGroups: arr(source.requestFailedGroups ?? runtimeAlerts.networkRequestFailedByPath).slice(0, 8).map(slimGroup), domDiagnosticGroups: arr(source.domDiagnosticGroups ?? runtimeAlerts.domDiagnosticsByText).slice(0, 5).map(slimGroup), domDiagnosticSamples: arr(source.domDiagnosticSamples ?? runtimeAlerts.domDiagnostics).slice(0, 8).map(slimGroup), consoleAlertGroups: arr(source.consoleAlertGroups ?? runtimeAlerts.consoleAlertsByPath).slice(0, 8).map(slimGroup), consoleAlertSamples: arr(source.consoleAlertSamples ?? runtimeAlerts.consoleAlerts).slice(0, 8).map(slimGroup), turnTimingRecentUpdateJumps: arr(source.turnTimingRecentUpdateJumps ?? srcMetrics.turnTimingRecentUpdateSawtoothJumps).slice(0, 8), turnTimingElapsedZeroResets: arr(source.turnTimingElapsedZeroResets ?? srcMetrics.turnTimingElapsedZeroResets).slice(0, 8), turnTimingTotalElapsedForwardJumps: arr(source.turnTimingTotalElapsedForwardJumps ?? srcMetrics.turnTimingTotalElapsedForwardJumps).slice(0, 8), pagePerformanceSlowApi: slowApis.slice(0, 8).map(slimSlowApi), archivePagePerformanceSlowApi: arr(source.archivePagePerformanceSlowApi).slice(0, 8).map(slimSlowApi), pagePerformancePartialApi: arr(source.pagePerformancePartialApi).slice(0, 8), pagePerformanceSseStreams: arr(source.pagePerformanceSseStreams).slice(0, 8), findings: arr(source.findings).slice(0, 12).map(slimFinding), archiveRedFindings: arr(source.archiveRedFindings ?? archiveSummary.redFindings).slice(0, 12).map(slimFinding), reportJsonPath: source.reportJsonPath ?? reportJsonPath, reportJsonSha256: source.reportJsonSha256 ?? sha256(reportJsonPath), reportMdPath: source.reportMdPath ?? reportMdPath, reportMdSha256: source.reportMdSha256 ?? sha256(reportMdPath), analyzer: { ...(objectOrNull(source.analyzer) || {}), recoveredFrom: 'analysis-artifact-after-transport-timeout', stdoutPath, stderrPath, stdoutBytes: statSize(stdoutPath), stderrBytes: statSize(stderrPath), reportJsonBytes: statSize(reportJsonPath), reportMdBytes: statSize(reportMdPath), transportExitCode: Number(transportExitRaw), transportTimedOut: transportTimedOutRaw === 'true', valuesRedacted: true }, valuesRedacted: true };", + "const compact = { ok: source.ok === true, command: source.command ?? 'web-probe-observe analyze', stateDir: source.stateDir ?? stateDir, jsonlScope: source.jsonlScope ?? null, alertThresholds: source.alertThresholds ?? null, counts: source.counts ?? null, archiveSummary: { ...archiveSummary, sampleMetrics: archiveSampleMetrics, pagePerformance: objectOrNull(archiveSummary.pagePerformance) || objectOrNull(pagePerformance.summary) || {}, runtimeAlerts: objectOrNull(archiveSummary.runtimeAlerts) || objectOrNull(runtimeAlerts.summary) || {}, browserProcess: objectOrNull(archiveSummary.browserProcess) || objectOrNull(browserProcess.summary) || {}, requestRate: objectOrNull(archiveSummary.requestRate) || requestRateSummary || {}, frontendPerformance: objectOrNull(archiveSummary.frontendPerformance) || frontendPerformanceSummary || {}, redFindings: arr(archiveSummary.redFindings).slice(0, 12).map(slimFinding) }, analysisWindow: source.analysisWindow ?? objectOrNull(recent.summary), sampleMetrics: compactMetrics(srcMetrics), pageProvenance: objectOrNull(source.pageProvenance?.summary) || source.pageProvenance || null, pagePerformance: objectOrNull(pagePerformance.summary) || pagePerformance, projectManagement: objectOrNull(source.projectManagement) || null, promptNetwork: objectOrNull(promptNetwork.summary) || promptNetwork, requestRate: requestRateSummary, requestRateCurve, requestRatePeaks, frontendPerformance: frontendPerformanceSummary, frontendPerformanceHotspots, runtimeAlerts: objectOrNull(runtimeAlerts.summary) || runtimeAlerts, browserProcess: objectOrNull(browserProcess.summary) || browserProcess, runnerErrors: arr(source.runnerErrors).slice(-8), commandFailures: arr(source.commandFailures).slice(-8), commandState: objectOrNull(source.commandState) || null, toolFindings: arr(source.toolFindings).slice(0, 8).map(slimFinding), httpErrorGroups: arr(source.httpErrorGroups ?? runtimeAlerts.networkHttpErrorsByPath).slice(0, 8).map(slimGroup), requestFailedGroups: arr(source.requestFailedGroups ?? runtimeAlerts.networkRequestFailedByPath).slice(0, 8).map(slimGroup), domDiagnosticGroups: arr(source.domDiagnosticGroups ?? runtimeAlerts.domDiagnosticsByText).slice(0, 5).map(slimGroup), domDiagnosticSamples: arr(source.domDiagnosticSamples ?? runtimeAlerts.domDiagnostics).slice(0, 8).map(slimGroup), consoleAlertGroups: arr(source.consoleAlertGroups ?? runtimeAlerts.consoleAlertsByPath).slice(0, 8).map(slimGroup), consoleAlertSamples: arr(source.consoleAlertSamples ?? runtimeAlerts.consoleAlerts).slice(0, 8).map(slimGroup), turnTimingRecentUpdateJumps: arr(source.turnTimingRecentUpdateJumps ?? srcMetrics.turnTimingRecentUpdateSawtoothJumps).slice(0, 8), turnTimingElapsedZeroResets: arr(source.turnTimingElapsedZeroResets ?? srcMetrics.turnTimingElapsedZeroResets).slice(0, 8), turnTimingTotalElapsedForwardJumps: arr(source.turnTimingTotalElapsedForwardJumps ?? srcMetrics.turnTimingTotalElapsedForwardJumps).slice(0, 8), pagePerformanceSlowApi: slowApis.slice(0, 8).map(slimSlowApi), archivePagePerformanceSlowApi: arr(source.archivePagePerformanceSlowApi).slice(0, 8).map(slimSlowApi), pagePerformancePartialApi: arr(source.pagePerformancePartialApi).slice(0, 8), pagePerformanceSseStreams: arr(source.pagePerformanceSseStreams).slice(0, 8), findings: arr(source.findings).slice(0, 12).map(slimFinding), archiveRedFindings: arr(source.archiveRedFindings ?? archiveSummary.redFindings).slice(0, 12).map(slimFinding), reportJsonPath: source.reportJsonPath ?? reportJsonPath, reportJsonSha256: source.reportJsonSha256 ?? sha256(reportJsonPath), reportMdPath: source.reportMdPath ?? reportMdPath, reportMdSha256: source.reportMdSha256 ?? sha256(reportMdPath), analyzer: { ...(objectOrNull(source.analyzer) || {}), recoveredFrom: 'analysis-artifact-after-transport-timeout', stdoutPath, stderrPath, stdoutBytes: statSize(stdoutPath), stderrBytes: statSize(stderrPath), reportJsonBytes: statSize(reportJsonPath), reportMdBytes: statSize(reportMdPath), transportExitCode: Number(transportExitRaw), transportTimedOut: transportTimedOutRaw === 'true', valuesRedacted: true }, valuesRedacted: true };", "compact.reportUpdatedAt = source.reportUpdatedAt ?? source.updatedAt ?? source.analyzedAt ?? reportJson.reportUpdatedAt ?? reportJson.updatedAt ?? reportJson.analyzedAt ?? null;", "console.log(JSON.stringify(compact));", "UNIDESK_WEB_OBSERVE_RECOVER_ANALYZE_ARTIFACT",