Merge pull request #1443 from pikasTech/fix/webprobe-performance-summary-bounded
fix: 收敛 web-probe 性能 summary 输出
This commit is contained in:
@@ -724,20 +724,42 @@ function targetNodeFromStateDir(){
|
||||
const index=parts.lastIndexOf('web-observe');
|
||||
return index>=0&&parts[index+1]?parts[index+1]:null;
|
||||
}
|
||||
function perfFrameKey(frame){
|
||||
return {functionName:short(frame?.functionName||frame?.name||'(anonymous)',48),url:short(frame?.url||frame?.sourceURL||frame?.scriptUrl||'',72),scriptId:frame?.scriptId??null,lineNumber:frame?.lineNumber??frame?.line??null,columnNumber:frame?.columnNumber??frame?.column??null,valuesRedacted:true};
|
||||
}
|
||||
function compactProfileHotspot(item){
|
||||
return {functionName:short(item?.functionName||item?.name||'(anonymous)',64),url:short(item?.url||item?.sourceURL||item?.scriptUrl||'',88),scriptId:item?.scriptId??null,lineNumber:item?.lineNumber??item?.line??null,columnNumber:item?.columnNumber??item?.column??null,selfTimeMs:item?.selfTimeMs??item?.selfMs??null,totalTimeMs:item?.totalTimeMs??item?.totalMs??null,hitCount:item?.hitCount??item?.sampleCount??null,captureCount:item?.captureCount??null,valuesRedacted:true};
|
||||
}
|
||||
function compactProfileStack(item){
|
||||
const frames=Array.isArray(item?.frames)?item.frames.slice(0,3).map(perfFrameKey):[];
|
||||
return {functionName:short(item?.functionName||item?.name||'(anonymous)',64),url:short(item?.url||item?.sourceURL||item?.scriptUrl||'',88),scriptId:item?.scriptId??null,lineNumber:item?.lineNumber??item?.line??null,columnNumber:item?.columnNumber??item?.column??null,selfTimeMs:item?.selfTimeMs??item?.selfMs??null,totalTimeMs:item?.totalTimeMs??item?.totalMs??null,hitCount:item?.hitCount??item?.sampleCount??null,frameCount:Array.isArray(item?.frames)?item.frames.length:null,frames,framesOmitted:Math.max(0,(Array.isArray(item?.frames)?item.frames.length:0)-frames.length),valuesRedacted:true};
|
||||
}
|
||||
function compactScriptHotspot(item){
|
||||
return {sourceFunctionName:short(item?.sourceFunctionName||item?.functionName||item?.invoker||'(anonymous)',64),sourceURL:short(item?.sourceURL||item?.url||'',88),lineNumber:item?.lineNumber??item?.line??null,columnNumber:item?.columnNumber??item?.column??null,totalDurationMs:item?.totalDurationMs??item?.durationMs??null,count:item?.count??item?.hitCount??null,invoker:short(item?.invoker||'',64),valuesRedacted:true};
|
||||
}
|
||||
function compactPerfEvent(item){
|
||||
return {ts:item?.ts??null,kind:item?.kind??null,durationMs:item?.durationMs??null,pageRole:item?.pageRole??null,pageId:item?.pageId??null,sampleSeq:item?.sampleSeq??item?.seq??null,scriptCount:item?.scriptCount??(Array.isArray(item?.scripts)?item.scripts.length:null),url:short(item?.url||item?.sourceURL||'',88),valuesRedacted:true};
|
||||
}
|
||||
function compactPerfCapture(item){
|
||||
return {ts:item?.ts??null,commandId:item?.commandId??null,status:item?.status??null,durationMs:item?.durationMs??item?.profileDurationMs??null,sampleCount:item?.sampleCount??item?.samples??null,nodeCount:item?.nodeCount??item?.nodes??null,file:short(item?.file||item?.path||item?.relativePath||'',88),valuesRedacted:true};
|
||||
}
|
||||
function compactPerfFinding(item){
|
||||
return {severity:item?.severity??item?.level??null,id:short(item?.id||item?.kind||item?.code||'',56),count:item?.count??item?.sampleCount??null,rootCause:item?.rootCause??item?.rootCauseStatus??null,summary:short(item?.summary||item?.message||'',110),valuesRedacted:true};
|
||||
}
|
||||
function performanceSummaryFromReport(){
|
||||
const perf=report.frontendPerformance&&typeof report.frontendPerformance==='object'?report.frontendPerformance:{};
|
||||
const summary=perf.summary&&typeof perf.summary==='object'?perf.summary:{};
|
||||
const findings=Array.isArray(report.findings)?report.findings.filter((item)=>String(item?.id||item?.kind||'').match(/^frontend-(?:long|event-loop|cpu-profile|performance)/u)).slice(0,20):[];
|
||||
const findings=Array.isArray(report.findings)?report.findings.filter((item)=>String(item?.id||item?.kind||'').match(/^frontend-(?:long|event-loop|cpu-profile|performance)/u)).slice(0,6).map(compactPerfFinding):[];
|
||||
const captureRows=Array.isArray(perf.captures)?perf.captures:[];
|
||||
return {
|
||||
summary:{...summary, rawPerformanceRowCount:performanceRows.length, valuesRedacted:true},
|
||||
longTasks:Array.isArray(perf.longTasks)?perf.longTasks.slice(0,12):[],
|
||||
longAnimationFrames:Array.isArray(perf.longAnimationFrames)?perf.longAnimationFrames.slice(0,12):[],
|
||||
eventLoopGaps:Array.isArray(perf.eventLoopGaps)?perf.eventLoopGaps.slice(0,12):[],
|
||||
scriptHotspots:Array.isArray(perf.scriptHotspots)?perf.scriptHotspots.slice(0,12):[],
|
||||
profileHotspots:Array.isArray(perf.profileHotspots)?perf.profileHotspots.slice(0,12):[],
|
||||
profileStacks:Array.isArray(perf.profileStacks)?perf.profileStacks.slice(0,8):[],
|
||||
captures:captureRows.slice(-12),
|
||||
longTasks:Array.isArray(perf.longTasks)?perf.longTasks.slice(0,3).map(compactPerfEvent):[],
|
||||
longAnimationFrames:Array.isArray(perf.longAnimationFrames)?perf.longAnimationFrames.slice(0,3).map(compactPerfEvent):[],
|
||||
eventLoopGaps:Array.isArray(perf.eventLoopGaps)?perf.eventLoopGaps.slice(0,3).map(compactPerfEvent):[],
|
||||
scriptHotspots:Array.isArray(perf.scriptHotspots)?perf.scriptHotspots.slice(0,5).map(compactScriptHotspot):[],
|
||||
profileHotspots:Array.isArray(perf.profileHotspots)?perf.profileHotspots.slice(0,5).map(compactProfileHotspot):[],
|
||||
profileStacks:Array.isArray(perf.profileStacks)?perf.profileStacks.slice(0,3).map(compactProfileStack):[],
|
||||
captures:captureRows.slice(-3).map(compactPerfCapture),
|
||||
findings,
|
||||
valuesRedacted:true
|
||||
};
|
||||
@@ -750,6 +772,12 @@ function renderPerformanceSummary(perf){
|
||||
lines.push('','CPU profile hotspots');
|
||||
if(perf.profileHotspots.length===0) lines.push('-');
|
||||
for(const item of perf.profileHotspots.slice(0,10)) lines.push(String(item.selfTimeMs??0)+'ms self '+short(item.functionName||'(anonymous)',44)+' '+short(item.url||item.scriptId||'-',92)+' line='+String(item.lineNumber??'-')+' captures='+String(item.captureCount??'-'));
|
||||
lines.push('','CPU profile stacks');
|
||||
if(perf.profileStacks.length===0) lines.push('-');
|
||||
for(const item of perf.profileStacks.slice(0,5)){
|
||||
const frames=Array.isArray(item.frames)?item.frames.slice(0,5).map((frame)=>short(frame.functionName||'(anonymous)',36)+'@'+short(frame.url||frame.scriptId||'-',54)+':'+String(frame.lineNumber??'-')).join(' <- '):'-';
|
||||
lines.push(String(item.selfTimeMs??0)+'ms self '+short(item.functionName||'(anonymous)',44)+' line='+String(item.lineNumber??'-')+' frames='+frames+(item.framesOmitted?(' omitted='+String(item.framesOmitted)):''));
|
||||
}
|
||||
lines.push('','LoAF script hotspots');
|
||||
if(perf.scriptHotspots.length===0) lines.push('-');
|
||||
for(const item of perf.scriptHotspots.slice(0,10)) lines.push(String(item.totalDurationMs??0)+'ms total count='+String(item.count??0)+' '+short(item.sourceFunctionName||item.invoker||'(anonymous)',44)+' '+short(item.sourceURL||'-',92)+' line='+String(item.lineNumber??'-'));
|
||||
@@ -788,7 +816,7 @@ function renderProjectSummary(project){
|
||||
const rows=turnSummaryRows();
|
||||
if(view==='performance-summary'){
|
||||
const perf=performanceSummaryFromReport();
|
||||
console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',view,stateDir:dir,summary:perf.summary,profileHotspots:perf.profileHotspots,profileStacks:perf.profileStacks,scriptHotspots:perf.scriptHotspots,longTasks:perf.longTasks,longAnimationFrames:perf.longAnimationFrames,eventLoopGaps:perf.eventLoopGaps,captures:perf.captures,findings:perf.findings,artifactFileCount:files.length,skippedFileCount:skippedFiles.length,skippedFiles:skippedFiles.slice(0,20),renderedText:renderPerformanceSummary(perf),sourceFiles:['performance-events.jsonl','artifacts.jsonl','analysis/report.json'],valuesRedacted:true}));
|
||||
console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',view,stateDir:dir,summary:perf.summary,artifactFileCount:files.length,skippedFileCount:skippedFiles.length,renderedText:renderPerformanceSummary(perf),sourceFiles:['performance-events.jsonl','artifacts.jsonl','analysis/report.json'],drillDown:'bun scripts/cli.ts web-probe observe collect '+String(manifest.jobId||'<observer>')+' --view files --file analysis/report.json',valuesRedacted:true}));
|
||||
process.exit(0);
|
||||
}
|
||||
if(view==='project-summary'||view==='project-mdtodo-summary'){
|
||||
|
||||
@@ -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