fix: bound web probe performance summary collect
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
// SPEC: PJ2026-01040111 长程观测 draft-2026-06-20-p0-passive-web-probe-observer.
|
||||
// Responsibility: bounded raw payloads for web-probe observe collect views.
|
||||
|
||||
export function compactObserveCollectForRaw(collect: Record<string, unknown> | null): Record<string, unknown> | null {
|
||||
if (collect === null) return null;
|
||||
const rows = Array.isArray(collect.rows) ? collect.rows.map((item) => {
|
||||
const row = observeRecord(item);
|
||||
const finalResponse = observeRecord(row.finalResponse);
|
||||
return {
|
||||
round: row.round ?? null,
|
||||
commandId: row.commandId ?? null,
|
||||
userHash: row.userHash ?? null,
|
||||
userBytes: row.userBytes ?? null,
|
||||
traceId: row.traceId ?? null,
|
||||
status: row.status ?? null,
|
||||
elapsedSeconds: row.elapsedSeconds ?? null,
|
||||
recentUpdateSeconds: row.recentUpdateSeconds ?? null,
|
||||
marks: row.marks ?? null,
|
||||
firstSeq: row.firstSeq ?? null,
|
||||
lastSeq: row.lastSeq ?? null,
|
||||
lastTs: row.lastTs ?? null,
|
||||
finalResponse: {
|
||||
preview: finalResponse.preview ?? null,
|
||||
textHash: finalResponse.textHash ?? null,
|
||||
textBytes: finalResponse.textBytes ?? null,
|
||||
empty: finalResponse.empty === true,
|
||||
},
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}) : undefined;
|
||||
const timelineRows = Array.isArray(collect.timelineRows) ? collect.timelineRows.slice(0, 12).map((item) => {
|
||||
const row = observeRecord(item);
|
||||
return {
|
||||
ts: row.ts ?? null,
|
||||
seq: row.seq ?? null,
|
||||
kind: row.kind ?? null,
|
||||
phase: row.phase ?? null,
|
||||
type: row.type ?? null,
|
||||
commandId: row.commandId ?? null,
|
||||
sessionId: row.sessionId ?? null,
|
||||
traceId: row.traceId ?? null,
|
||||
summary: row.summary ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}) : undefined;
|
||||
const collectView = typeof collect.view === "string" ? collect.view : null;
|
||||
const slimNetworkEvidence = (value: unknown): Record<string, unknown> => {
|
||||
const source = observeRecord(value);
|
||||
const latestTurn = observeRecord(source.latestTurnResponse);
|
||||
const latestTrace = observeRecord(source.latestTraceEventsResponse);
|
||||
return {
|
||||
requestCount: source.requestCount ?? null,
|
||||
responseCount: source.responseCount ?? null,
|
||||
failedCount: source.failedCount ?? null,
|
||||
terminalResponseCount: source.terminalResponseCount ?? null,
|
||||
turnResponseCount: source.turnResponseCount ?? null,
|
||||
traceEventsResponseCount: source.traceEventsResponseCount ?? null,
|
||||
latestTurnResponse: {
|
||||
ts: latestTurn.ts ?? null,
|
||||
sampleSeq: latestTurn.sampleSeq ?? null,
|
||||
status: latestTurn.status ?? null,
|
||||
bodySummary: observeRecord(latestTurn.bodySummary),
|
||||
valuesRedacted: true,
|
||||
},
|
||||
latestTraceEventsResponse: {
|
||||
ts: latestTrace.ts ?? null,
|
||||
sampleSeq: latestTrace.sampleSeq ?? null,
|
||||
status: latestTrace.status ?? null,
|
||||
bodySummary: observeRecord(latestTrace.bodySummary),
|
||||
valuesRedacted: true,
|
||||
},
|
||||
valuesRedacted: true,
|
||||
};
|
||||
};
|
||||
const slimFreeze = (value: unknown): Record<string, unknown> => {
|
||||
const source = observeRecord(value);
|
||||
const blocker = observeRecord(source.latestBlocker);
|
||||
return {
|
||||
blockerCount: source.blockerCount ?? null,
|
||||
responsivenessEventCount: source.responsivenessEventCount ?? null,
|
||||
latestBlocker: {
|
||||
ts: blocker.ts ?? null,
|
||||
sampleSeq: blocker.sampleSeq ?? null,
|
||||
rootCause: blocker.rootCause ?? null,
|
||||
rootCauseStatus: blocker.rootCauseStatus ?? null,
|
||||
fallbackAllowed: blocker.fallbackAllowed === true,
|
||||
observed: observeRecord(blocker.observed),
|
||||
page: observeRecord(blocker.page),
|
||||
valuesRedacted: true,
|
||||
},
|
||||
valuesRedacted: true,
|
||||
};
|
||||
};
|
||||
const slimFindings = Array.isArray(collect.findings) ? collect.findings.slice(0, 6).map((item) => {
|
||||
const row = observeRecord(item);
|
||||
return {
|
||||
id: row.id ?? null,
|
||||
severity: row.severity ?? null,
|
||||
count: row.count ?? null,
|
||||
rootCause: row.rootCause ?? row.rootCauseStatus ?? null,
|
||||
summary: typeof row.summary === "string" ? row.summary.slice(0, 140) : row.summary ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}) : undefined;
|
||||
const slimPerfFrame = (value: unknown): Record<string, unknown> => {
|
||||
const row = observeRecord(value);
|
||||
return {
|
||||
functionName: row.functionName ?? null,
|
||||
url: row.url ?? null,
|
||||
scriptId: row.scriptId ?? null,
|
||||
lineNumber: row.lineNumber ?? null,
|
||||
columnNumber: row.columnNumber ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
};
|
||||
const slimPerfStack = (value: unknown): Record<string, unknown> => {
|
||||
const row = observeRecord(value);
|
||||
return {
|
||||
functionName: row.functionName ?? null,
|
||||
url: row.url ?? null,
|
||||
scriptId: row.scriptId ?? null,
|
||||
lineNumber: row.lineNumber ?? null,
|
||||
columnNumber: row.columnNumber ?? null,
|
||||
selfTimeMs: row.selfTimeMs ?? null,
|
||||
totalTimeMs: row.totalTimeMs ?? null,
|
||||
hitCount: row.hitCount ?? null,
|
||||
frameCount: row.frameCount ?? null,
|
||||
frames: Array.isArray(row.frames) ? row.frames.slice(0, 3).map(slimPerfFrame) : [],
|
||||
framesOmitted: row.framesOmitted ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
};
|
||||
const slimPerfHotspot = (value: unknown): Record<string, unknown> => {
|
||||
const row = observeRecord(value);
|
||||
return {
|
||||
functionName: row.functionName ?? row.sourceFunctionName ?? null,
|
||||
url: row.url ?? row.sourceURL ?? null,
|
||||
scriptId: row.scriptId ?? null,
|
||||
lineNumber: row.lineNumber ?? null,
|
||||
columnNumber: row.columnNumber ?? null,
|
||||
selfTimeMs: row.selfTimeMs ?? null,
|
||||
totalTimeMs: row.totalTimeMs ?? null,
|
||||
totalDurationMs: row.totalDurationMs ?? null,
|
||||
count: row.count ?? null,
|
||||
hitCount: row.hitCount ?? null,
|
||||
captureCount: row.captureCount ?? null,
|
||||
invoker: row.invoker ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
};
|
||||
const slimPerfEvent = (value: unknown): Record<string, unknown> => {
|
||||
const row = observeRecord(value);
|
||||
return {
|
||||
ts: row.ts ?? null,
|
||||
kind: row.kind ?? null,
|
||||
durationMs: row.durationMs ?? null,
|
||||
pageRole: row.pageRole ?? null,
|
||||
pageId: row.pageId ?? null,
|
||||
sampleSeq: row.sampleSeq ?? null,
|
||||
scriptCount: row.scriptCount ?? null,
|
||||
url: row.url ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
};
|
||||
const slimPerfCapture = (value: unknown): Record<string, unknown> => {
|
||||
const row = observeRecord(value);
|
||||
return {
|
||||
ts: row.ts ?? null,
|
||||
commandId: row.commandId ?? null,
|
||||
status: row.status ?? null,
|
||||
durationMs: row.durationMs ?? null,
|
||||
sampleCount: row.sampleCount ?? null,
|
||||
nodeCount: row.nodeCount ?? null,
|
||||
file: row.file ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
};
|
||||
const slimPerfFindings = Array.isArray(collect.findings) ? collect.findings.slice(0, 5).map((item) => {
|
||||
const row = observeRecord(item);
|
||||
return {
|
||||
id: row.id ?? null,
|
||||
severity: row.severity ?? null,
|
||||
count: row.count ?? null,
|
||||
rootCause: row.rootCause ?? row.rootCauseStatus ?? null,
|
||||
summary: typeof row.summary === "string" ? row.summary.slice(0, 110) : row.summary ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}) : undefined;
|
||||
const isPerformanceSummary = collectView === "performance-summary";
|
||||
return {
|
||||
ok: collect.ok !== false,
|
||||
command: collect.command,
|
||||
view: collect.view,
|
||||
stateDir: collect.stateDir,
|
||||
turnCount: collect.turnCount,
|
||||
artifactFileCount: collect.artifactFileCount ?? collect.fileCount,
|
||||
skippedFileCount: collect.skippedFileCount,
|
||||
skippedFiles: Array.isArray(collect.skippedFiles) ? collect.skippedFiles.slice(0, 8) : undefined,
|
||||
anchor: observeRecord(collect.anchor),
|
||||
window: observeRecord(collect.window),
|
||||
counts: observeRecord(collect.counts),
|
||||
summary: observeRecord(collect.summary),
|
||||
...(rows === undefined ? {} : { rows }),
|
||||
...(timelineRows === undefined ? {} : { timelineRows }),
|
||||
renderedText: collectView === "timeline" || collectView === "workbench-triad" ? undefined : typeof collect.renderedText === "string" ? collect.renderedText : undefined,
|
||||
sourceFiles: Array.isArray(collect.sourceFiles) ? collect.sourceFiles : undefined,
|
||||
blocker: collect.blocker,
|
||||
sampleSeq: collect.sampleSeq,
|
||||
traceId: collect.traceId,
|
||||
finalResponse: collect.finalResponse,
|
||||
statusGap: observeRecord(collect.statusGap),
|
||||
dom: observeRecord(collect.dom),
|
||||
networkEvidence: collectView === "workbench-triad" ? slimNetworkEvidence(collect.networkEvidence) : observeRecord(collect.networkEvidence),
|
||||
freeze: collectView === "workbench-triad" ? slimFreeze(collect.freeze) : observeRecord(collect.freeze),
|
||||
profileHotspots: Array.isArray(collect.profileHotspots) ? (isPerformanceSummary ? collect.profileHotspots.slice(0, 5).map(slimPerfHotspot) : collect.profileHotspots.slice(0, 8)) : undefined,
|
||||
profileStacks: Array.isArray(collect.profileStacks) ? (isPerformanceSummary ? collect.profileStacks.slice(0, 3).map(slimPerfStack) : collect.profileStacks.slice(0, 5)) : undefined,
|
||||
scriptHotspots: Array.isArray(collect.scriptHotspots) ? (isPerformanceSummary ? collect.scriptHotspots.slice(0, 5).map(slimPerfHotspot) : collect.scriptHotspots.slice(0, 8)) : undefined,
|
||||
longTasks: Array.isArray(collect.longTasks) ? (isPerformanceSummary ? collect.longTasks.slice(0, 3).map(slimPerfEvent) : collect.longTasks.slice(0, 8)) : undefined,
|
||||
longAnimationFrames: Array.isArray(collect.longAnimationFrames) ? (isPerformanceSummary ? collect.longAnimationFrames.slice(0, 3).map(slimPerfEvent) : collect.longAnimationFrames.slice(0, 8)) : undefined,
|
||||
eventLoopGaps: Array.isArray(collect.eventLoopGaps) ? (isPerformanceSummary ? collect.eventLoopGaps.slice(0, 3).map(slimPerfEvent) : collect.eventLoopGaps.slice(0, 8)) : undefined,
|
||||
captures: Array.isArray(collect.captures) ? (isPerformanceSummary ? collect.captures.slice(0, 3).map(slimPerfCapture) : collect.captures.slice(0, 8)) : undefined,
|
||||
findings: collectView === "workbench-triad" ? slimFindings : isPerformanceSummary ? slimPerfFindings : Array.isArray(collect.findings) ? collect.findings.slice(0, 8) : undefined,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}
|
||||
|
||||
function observeRecord(value: unknown): Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {};
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import { transPath } from "./runtime-common";
|
||||
import { assertLane, assertNodeId, compactCommandResult, compactCommandResultRedacted, compactCommandResultWithStdoutTail, nullableRecord, optionValue, parseJsonObject, positiveIntegerOption, record, requiredOption, shellQuote } from "./utils";
|
||||
import { nodeWebObserveResolveStateDirShell, recoverWebObserveAnalyzeTurnDetails, renderWebObserveStartResult, renderWebProbeRunResult, upsertWebObserveIndexEntry, webObserveCommandLabel, webObserveIdFromOptions, webObserveIdFromStatus, webObserveIndexEntryFromOptions, webObserveNextCommands, withWebObserveAnalyzeRendered, withWebObserveShortcuts } from "./web-observe-render";
|
||||
import { commandSummaryForOutput, isSafeWebObserveArchivePrefix, isSafeWebObserveCollectFile, isSafeWebObserveFindingId, isSafeWebObserveJobId, isSafeWebObserveStateDir, isSafeWebObserveTraceId, nodeWebObserveCollectNodeScript, nodeWebObserveForceStopNodeScript, nodeWebObserveStatusNodeScript, nodeWebObserveWaitCommandShell, runNodeWebProbeScript, safeWebObserveSegment, safeWebObserveTargetSegment } from "./web-observe-scripts";
|
||||
import { compactObserveCollectForRaw } from "./web-observe-collect-compact";
|
||||
import { displayRepoPath, readBootstrapAdminPasswordMaterial, sleepSync } from "./web-probe";
|
||||
|
||||
export function parseNodeWebProbeSentinelOptions(args: string[]): NodeWebProbeSentinelOptions {
|
||||
@@ -2063,148 +2064,6 @@ export function runNodeWebProbeObserveCollect(options: NodeWebProbeObserveOption
|
||||
return options.raw ? payload : withWebObserveCollectRendered(payload);
|
||||
}
|
||||
|
||||
function compactObserveCollectForRaw(collect: Record<string, unknown> | null): Record<string, unknown> | null {
|
||||
if (collect === null) return null;
|
||||
const rows = Array.isArray(collect.rows) ? collect.rows.map((item) => {
|
||||
const row = observeRecord(item);
|
||||
const finalResponse = observeRecord(row.finalResponse);
|
||||
return {
|
||||
round: row.round ?? null,
|
||||
commandId: row.commandId ?? null,
|
||||
userHash: row.userHash ?? null,
|
||||
userBytes: row.userBytes ?? null,
|
||||
traceId: row.traceId ?? null,
|
||||
status: row.status ?? null,
|
||||
elapsedSeconds: row.elapsedSeconds ?? null,
|
||||
recentUpdateSeconds: row.recentUpdateSeconds ?? null,
|
||||
marks: row.marks ?? null,
|
||||
firstSeq: row.firstSeq ?? null,
|
||||
lastSeq: row.lastSeq ?? null,
|
||||
lastTs: row.lastTs ?? null,
|
||||
finalResponse: {
|
||||
preview: finalResponse.preview ?? null,
|
||||
textHash: finalResponse.textHash ?? null,
|
||||
textBytes: finalResponse.textBytes ?? null,
|
||||
empty: finalResponse.empty === true,
|
||||
},
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}) : undefined;
|
||||
const timelineRows = Array.isArray(collect.timelineRows) ? collect.timelineRows.slice(0, 12).map((item) => {
|
||||
const row = observeRecord(item);
|
||||
return {
|
||||
ts: row.ts ?? null,
|
||||
seq: row.seq ?? null,
|
||||
kind: row.kind ?? null,
|
||||
phase: row.phase ?? null,
|
||||
type: row.type ?? null,
|
||||
commandId: row.commandId ?? null,
|
||||
sessionId: row.sessionId ?? null,
|
||||
traceId: row.traceId ?? null,
|
||||
summary: row.summary ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}) : undefined;
|
||||
const collectView = typeof collect.view === "string" ? collect.view : null;
|
||||
const slimNetworkEvidence = (value: unknown): Record<string, unknown> => {
|
||||
const source = observeRecord(value);
|
||||
const latestTurn = observeRecord(source.latestTurnResponse);
|
||||
const latestTrace = observeRecord(source.latestTraceEventsResponse);
|
||||
return {
|
||||
requestCount: source.requestCount ?? null,
|
||||
responseCount: source.responseCount ?? null,
|
||||
failedCount: source.failedCount ?? null,
|
||||
terminalResponseCount: source.terminalResponseCount ?? null,
|
||||
turnResponseCount: source.turnResponseCount ?? null,
|
||||
traceEventsResponseCount: source.traceEventsResponseCount ?? null,
|
||||
latestTurnResponse: {
|
||||
ts: latestTurn.ts ?? null,
|
||||
sampleSeq: latestTurn.sampleSeq ?? null,
|
||||
status: latestTurn.status ?? null,
|
||||
bodySummary: observeRecord(latestTurn.bodySummary),
|
||||
valuesRedacted: true,
|
||||
},
|
||||
latestTraceEventsResponse: {
|
||||
ts: latestTrace.ts ?? null,
|
||||
sampleSeq: latestTrace.sampleSeq ?? null,
|
||||
status: latestTrace.status ?? null,
|
||||
bodySummary: observeRecord(latestTrace.bodySummary),
|
||||
valuesRedacted: true,
|
||||
},
|
||||
valuesRedacted: true,
|
||||
};
|
||||
};
|
||||
const slimFreeze = (value: unknown): Record<string, unknown> => {
|
||||
const source = observeRecord(value);
|
||||
const blocker = observeRecord(source.latestBlocker);
|
||||
return {
|
||||
blockerCount: source.blockerCount ?? null,
|
||||
responsivenessEventCount: source.responsivenessEventCount ?? null,
|
||||
latestBlocker: {
|
||||
ts: blocker.ts ?? null,
|
||||
sampleSeq: blocker.sampleSeq ?? null,
|
||||
rootCause: blocker.rootCause ?? null,
|
||||
rootCauseStatus: blocker.rootCauseStatus ?? null,
|
||||
fallbackAllowed: blocker.fallbackAllowed === true,
|
||||
observed: observeRecord(blocker.observed),
|
||||
page: observeRecord(blocker.page),
|
||||
valuesRedacted: true,
|
||||
},
|
||||
valuesRedacted: true,
|
||||
};
|
||||
};
|
||||
const slimFindings = Array.isArray(collect.findings) ? collect.findings.slice(0, 6).map((item) => {
|
||||
const row = observeRecord(item);
|
||||
return {
|
||||
id: row.id ?? null,
|
||||
severity: row.severity ?? null,
|
||||
count: row.count ?? null,
|
||||
rootCause: row.rootCause ?? row.rootCauseStatus ?? null,
|
||||
summary: typeof row.summary === "string" ? row.summary.slice(0, 140) : row.summary ?? null,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}) : undefined;
|
||||
return {
|
||||
ok: collect.ok !== false,
|
||||
command: collect.command,
|
||||
view: collect.view,
|
||||
stateDir: collect.stateDir,
|
||||
turnCount: collect.turnCount,
|
||||
artifactFileCount: collect.artifactFileCount ?? collect.fileCount,
|
||||
skippedFileCount: collect.skippedFileCount,
|
||||
skippedFiles: Array.isArray(collect.skippedFiles) ? collect.skippedFiles.slice(0, 8) : undefined,
|
||||
anchor: observeRecord(collect.anchor),
|
||||
window: observeRecord(collect.window),
|
||||
counts: observeRecord(collect.counts),
|
||||
summary: observeRecord(collect.summary),
|
||||
...(rows === undefined ? {} : { rows }),
|
||||
...(timelineRows === undefined ? {} : { timelineRows }),
|
||||
renderedText: collectView === "timeline" || collectView === "workbench-triad" ? undefined : typeof collect.renderedText === "string" ? collect.renderedText : undefined,
|
||||
sourceFiles: Array.isArray(collect.sourceFiles) ? collect.sourceFiles : undefined,
|
||||
blocker: collect.blocker,
|
||||
sampleSeq: collect.sampleSeq,
|
||||
traceId: collect.traceId,
|
||||
finalResponse: collect.finalResponse,
|
||||
statusGap: observeRecord(collect.statusGap),
|
||||
dom: observeRecord(collect.dom),
|
||||
networkEvidence: collectView === "workbench-triad" ? slimNetworkEvidence(collect.networkEvidence) : observeRecord(collect.networkEvidence),
|
||||
freeze: collectView === "workbench-triad" ? slimFreeze(collect.freeze) : observeRecord(collect.freeze),
|
||||
profileHotspots: Array.isArray(collect.profileHotspots) ? collect.profileHotspots.slice(0, 8) : undefined,
|
||||
profileStacks: Array.isArray(collect.profileStacks) ? collect.profileStacks.slice(0, 5) : undefined,
|
||||
scriptHotspots: Array.isArray(collect.scriptHotspots) ? collect.scriptHotspots.slice(0, 8) : undefined,
|
||||
longTasks: Array.isArray(collect.longTasks) ? collect.longTasks.slice(0, 8) : undefined,
|
||||
longAnimationFrames: Array.isArray(collect.longAnimationFrames) ? collect.longAnimationFrames.slice(0, 8) : undefined,
|
||||
eventLoopGaps: Array.isArray(collect.eventLoopGaps) ? collect.eventLoopGaps.slice(0, 8) : undefined,
|
||||
captures: Array.isArray(collect.captures) ? collect.captures.slice(0, 8) : undefined,
|
||||
findings: collectView === "workbench-triad" ? slimFindings : Array.isArray(collect.findings) ? collect.findings.slice(0, 8) : undefined,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}
|
||||
|
||||
function observeRecord(value: unknown): Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : {};
|
||||
}
|
||||
|
||||
export function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec: HwlabRuntimeLaneSpec): Record<string, unknown> | RenderedCliResult {
|
||||
const analyzerB64 = Buffer.from(nodeWebObserveAnalyzerSource(), "utf8").toString("base64");
|
||||
const alertThresholds = nodeWebProbeAlertThresholds(spec);
|
||||
|
||||
Reference in New Issue
Block a user