feat: add project-management web-probe observe
This commit is contained in:
@@ -8,7 +8,7 @@ import { runCommand, type CommandResult } from "./command";
|
||||
import { startJob } from "./jobs";
|
||||
import { classifySshTcpPoolFailure } from "./ssh";
|
||||
import { HWLAB_NODE_CONTROL_PLANE_CONFIG_PATH, hwlabNodeControlPlaneInfraHelp, runHwlabNodeControlPlaneInfra } from "./hwlab-node-control-plane";
|
||||
import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, hwlabRuntimeLaneSpecForNode, hwlabRuntimeNodeIds, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec, type HwlabRuntimeObservabilityRecordingRuleSpec, type HwlabRuntimeObservabilitySpec, type HwlabRuntimeObservabilityWarningAlertSpec, type HwlabRuntimePublicExposureSpec, type HwlabRuntimeWebProbeAlertThresholdsSpec } from "./hwlab-node-lanes";
|
||||
import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, hwlabRuntimeLaneSpecForNode, hwlabRuntimeNodeIds, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec, type HwlabRuntimeObservabilityRecordingRuleSpec, type HwlabRuntimeObservabilitySpec, type HwlabRuntimeObservabilityWarningAlertSpec, type HwlabRuntimePublicExposureSpec, type HwlabRuntimeWebProbeAlertThresholdsSpec, type HwlabRuntimeWebProbeProjectManagementSpec } from "./hwlab-node-lanes";
|
||||
import { nodeWebProbeScriptRunnerSource } from "./hwlab-node-web-probe-runner-source";
|
||||
import { nodeWebObserveAnalyzerSource } from "./hwlab-node-web-observe-analyzer-source";
|
||||
import { nodeWebObserveRunnerSource } from "./hwlab-node-web-observe-runner-source";
|
||||
@@ -64,7 +64,7 @@ interface NodeWebProbeScriptOptions {
|
||||
}
|
||||
|
||||
type NodeWebProbeObserveAction = "start" | "status" | "command" | "stop" | "collect" | "analyze";
|
||||
type NodeWebProbeObserveCommandType = "login" | "preflight" | "goto" | "newSession" | "sendPrompt" | "steer" | "cancel" | "selectProvider" | "clickSession" | "screenshot" | "mark" | "stop";
|
||||
type NodeWebProbeObserveCommandType = "login" | "preflight" | "goto" | "newSession" | "sendPrompt" | "steer" | "cancel" | "selectProvider" | "clickSession" | "selectProjectSource" | "selectMdtodoFile" | "selectMdtodoTask" | "launchWorkbenchFromTask" | "screenshot" | "mark" | "stop";
|
||||
|
||||
interface NodeWebProbeObserveOptions {
|
||||
action: "observe";
|
||||
@@ -104,6 +104,9 @@ interface NodeWebProbeObserveOptions {
|
||||
commandLabel: string | null;
|
||||
commandSessionId: string | null;
|
||||
commandProvider: string | null;
|
||||
commandSourceId: string | null;
|
||||
commandFileRef: string | null;
|
||||
commandTaskRef: string | null;
|
||||
}
|
||||
|
||||
type NodeWebProbeOptions = NodeWebProbeRunOptions | NodeWebProbeScriptOptions | NodeWebProbeObserveOptions;
|
||||
@@ -7385,6 +7388,9 @@ function parseNodeWebProbeObserveOptions(
|
||||
"--label",
|
||||
"--session-id",
|
||||
"--provider",
|
||||
"--source-id",
|
||||
"--file-ref",
|
||||
"--task-ref",
|
||||
]), new Set(["--force", "--full", "--text-stdin"]));
|
||||
const commandTypeRaw = optionValue(args, "--type") ?? null;
|
||||
const commandType = commandTypeRaw === null ? null : parseNodeWebProbeObserveCommandType(commandTypeRaw);
|
||||
@@ -7428,6 +7434,12 @@ function parseNodeWebProbeObserveOptions(
|
||||
throw new Error("web-probe observe command accepts either --text or --text-stdin, not both");
|
||||
}
|
||||
const commandText = commandTextFromStdin ? readFileSync(0, "utf8") : commandTextOption;
|
||||
const commandSourceId = optionValue(args, "--source-id") ?? null;
|
||||
const commandFileRef = optionValue(args, "--file-ref") ?? null;
|
||||
const commandTaskRef = optionValue(args, "--task-ref") ?? null;
|
||||
for (const [label, value] of [["--source-id", commandSourceId], ["--file-ref", commandFileRef], ["--task-ref", commandTaskRef]] as const) {
|
||||
if (value !== null && (value.includes("\0") || value.length > 500)) throw new Error(`unsafe web-probe observe ${label}: expected 1-500 non-NUL chars`);
|
||||
}
|
||||
return {
|
||||
action: "observe",
|
||||
observeAction: observeActionRaw,
|
||||
@@ -7466,6 +7478,9 @@ function parseNodeWebProbeObserveOptions(
|
||||
commandLabel: optionValue(args, "--label") ?? null,
|
||||
commandSessionId: optionValue(args, "--session-id") ?? null,
|
||||
commandProvider: optionValue(args, "--provider") ?? null,
|
||||
commandSourceId,
|
||||
commandFileRef,
|
||||
commandTaskRef,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7480,11 +7495,15 @@ function parseNodeWebProbeObserveCommandType(value: string): NodeWebProbeObserve
|
||||
|| value === "cancel"
|
||||
|| value === "selectProvider"
|
||||
|| value === "clickSession"
|
||||
|| value === "selectProjectSource"
|
||||
|| value === "selectMdtodoFile"
|
||||
|| value === "selectMdtodoTask"
|
||||
|| value === "launchWorkbenchFromTask"
|
||||
|| value === "screenshot"
|
||||
|| value === "mark"
|
||||
|| value === "stop"
|
||||
) return value;
|
||||
throw new Error(`web-probe observe command --type must be login, preflight, goto, newSession, sendPrompt, steer, cancel, selectProvider, clickSession, screenshot, mark, or stop; got ${value}`);
|
||||
throw new Error(`web-probe observe command --type must be login, preflight, goto, newSession, sendPrompt, steer, cancel, selectProvider, clickSession, selectProjectSource, selectMdtodoFile, selectMdtodoTask, launchWorkbenchFromTask, screenshot, mark, or stop; got ${value}`);
|
||||
}
|
||||
|
||||
function parseWebProbeBrowserProxyMode(value: string | undefined): WebProbeBrowserProxyMode {
|
||||
@@ -7843,6 +7862,10 @@ function nodeWebProbeAlertThresholds(spec: HwlabRuntimeLaneSpec): HwlabRuntimeWe
|
||||
return thresholds;
|
||||
}
|
||||
|
||||
function nodeWebProbeProjectManagementConfig(spec: HwlabRuntimeLaneSpec): HwlabRuntimeWebProbeProjectManagementSpec | null {
|
||||
return spec.webProbe?.projectManagement ?? null;
|
||||
}
|
||||
|
||||
interface NodeWebProbeHostProxyEnv {
|
||||
readonly envAssignments: string[];
|
||||
readonly summary: Record<string, unknown>;
|
||||
@@ -7999,6 +8022,7 @@ function runNodeWebProbeObserveStart(
|
||||
const runnerB64Body = runnerB64.match(/.{1,76}/gu)?.join("\n") ?? runnerB64;
|
||||
const webProbeProxy = nodeWebProbeHostProxyEnv(spec, options.browserProxyMode);
|
||||
const alertThresholds = nodeWebProbeAlertThresholds(spec);
|
||||
const projectManagement = nodeWebProbeProjectManagementConfig(spec);
|
||||
const runnerEnvAssignments = [
|
||||
...webProbeProxy.envAssignments,
|
||||
`HWLAB_WEB_BASE_URL=${shellQuote(options.url)}`,
|
||||
@@ -8014,6 +8038,7 @@ function runNodeWebProbeObserveStart(
|
||||
`UNIDESK_WEB_OBSERVE_VIEWPORT=${shellQuote(options.viewport)}`,
|
||||
`UNIDESK_WEB_OBSERVE_BROWSER_PROXY_MODE=${shellQuote(options.browserProxyMode)}`,
|
||||
`UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON=${shellQuote(JSON.stringify(alertThresholds))}`,
|
||||
`UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON=${shellQuote(JSON.stringify(projectManagement))}`,
|
||||
].join(" ");
|
||||
const script = [
|
||||
"set -eu",
|
||||
@@ -8062,6 +8087,7 @@ function runNodeWebProbeObserveStart(
|
||||
url: options.url,
|
||||
network: webProbeProxy.summary,
|
||||
alertThresholds,
|
||||
projectManagement,
|
||||
targetPath: options.targetPath,
|
||||
id: observerId,
|
||||
credential,
|
||||
@@ -8149,6 +8175,9 @@ function runNodeWebProbeObserveCommand(options: NodeWebProbeObserveOptions, spec
|
||||
label: options.commandLabel,
|
||||
sessionId: options.commandSessionId,
|
||||
provider: options.commandProvider,
|
||||
sourceId: options.commandSourceId,
|
||||
fileRef: options.commandFileRef,
|
||||
taskRef: options.commandTaskRef,
|
||||
};
|
||||
const preStopStatus = options.force && stopCommand
|
||||
? readNodeWebProbeObserveRemoteStatus(options, spec, 1, Math.min(options.commandTimeoutSeconds, 30))
|
||||
@@ -8279,6 +8308,7 @@ function runNodeWebProbeObserveCollect(options: NodeWebProbeObserveOptions, spec
|
||||
function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec: HwlabRuntimeLaneSpec): Record<string, unknown> | RenderedCliResult {
|
||||
const analyzerB64 = Buffer.from(nodeWebObserveAnalyzerSource(), "utf8").toString("base64");
|
||||
const alertThresholds = nodeWebProbeAlertThresholds(spec);
|
||||
const projectManagement = nodeWebProbeProjectManagementConfig(spec);
|
||||
const script = [
|
||||
"set -eu",
|
||||
nodeWebObserveResolveStateDirShell(options),
|
||||
@@ -8297,7 +8327,8 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
`UNIDESK_WEB_OBSERVE_ANALYZE_ARCHIVE_PREFIX=${shellQuote(options.analyzeArchivePrefix ?? "")}`,
|
||||
`UNIDESK_WEB_OBSERVE_ANALYZE_TAIL_SAMPLES=${shellQuote(options.analyzeTailSamples === null ? "" : String(options.analyzeTailSamples))}`,
|
||||
`UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON=${shellQuote(JSON.stringify(alertThresholds))}`,
|
||||
"UNIDESK_WEB_OBSERVE_STATE_DIR=\"$state_dir\" UNIDESK_WEB_OBSERVE_ANALYZE_ARCHIVE_PREFIX=\"$UNIDESK_WEB_OBSERVE_ANALYZE_ARCHIVE_PREFIX\" UNIDESK_WEB_OBSERVE_ANALYZE_TAIL_SAMPLES=\"$UNIDESK_WEB_OBSERVE_ANALYZE_TAIL_SAMPLES\" UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON=\"$UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON\" node \"$analyzer\" >\"$analysis_stdout\" 2>\"$analysis_stderr\"",
|
||||
`UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON=${shellQuote(JSON.stringify(projectManagement))}`,
|
||||
"UNIDESK_WEB_OBSERVE_STATE_DIR=\"$state_dir\" UNIDESK_WEB_OBSERVE_ANALYZE_ARCHIVE_PREFIX=\"$UNIDESK_WEB_OBSERVE_ANALYZE_ARCHIVE_PREFIX\" UNIDESK_WEB_OBSERVE_ANALYZE_TAIL_SAMPLES=\"$UNIDESK_WEB_OBSERVE_ANALYZE_TAIL_SAMPLES\" UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON=\"$UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON\" UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON=\"$UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON\" node \"$analyzer\" >\"$analysis_stdout\" 2>\"$analysis_stderr\"",
|
||||
"analyzer_exit=$?",
|
||||
"set -e",
|
||||
"report_json=\"$state_dir/analysis/report.json\"",
|
||||
@@ -8319,7 +8350,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
"const readJsonlTail = (path, limit) => readText(path).split(/\\r?\\n/).filter(Boolean).slice(-limit).map((line) => { try { return JSON.parse(line); } catch { return null; } }).filter(Boolean);",
|
||||
"const mergeArrays = (...values) => { const out = []; const seen = new Set(); for (const value of values) { if (!Array.isArray(value)) continue; for (const item of value) { const key = JSON.stringify([item?.id ?? item?.kind ?? item?.code ?? item?.columnLabel ?? item?.traceId ?? null, item?.path ?? item?.urlPath ?? null, item?.method ?? null, item?.status ?? null, item?.summary ?? item?.message ?? item?.fromSeq ?? item?.firstAt ?? null, item?.toSeq ?? item?.lastAt ?? null]); if (seen.has(key)) continue; seen.add(key); out.push(item); } } return out; };",
|
||||
"const clip = (value, limit = 160) => value === null || value === undefined ? null : String(value).slice(0, limit);",
|
||||
"const findingRank = (item) => { const id = String(item?.id ?? item?.kind ?? item?.code ?? ''); if (id === 'observer-command-failed') return 0; if (id === 'page-performance-slow-same-origin-api') return 1; if (id === 'session-rail-title-fallback-majority') return 2; if (id.startsWith('turn-timing-total-elapsed')) return 3; if (id.startsWith('turn-timing-recent-update')) return 4; if (id.includes('runtime-execution') || id.includes('prompt-chat-submit-failed')) return 5; return 10; };",
|
||||
"const findingRank = (item) => { const id = String(item?.id ?? item?.kind ?? item?.code ?? ''); if (id.startsWith('project-management-') || id.startsWith('mdtodo-') || id === 'workbench-launch-button-unavailable') return 0; if (id === 'observer-command-failed') return 0.2; if (id === 'page-performance-slow-same-origin-api') return 1; if (id === 'session-rail-title-fallback-majority') return 2; if (id.startsWith('turn-timing-total-elapsed')) return 3; if (id.startsWith('turn-timing-recent-update')) return 4; if (id.includes('runtime-execution') || id.includes('prompt-chat-submit-failed')) return 5; return 10; };",
|
||||
"const severityRank = (item) => { const severity = String(item?.severity ?? item?.level ?? '').toLowerCase(); if (severity === 'red') return 0; if (severity === 'amber' || severity === 'warning') return 1; if (severity === 'info') return 3; return 2; };",
|
||||
"const sortFindings = (items) => (Array.isArray(items) ? items : []).slice().sort((a, b) => findingRank(a) - findingRank(b) || severityRank(a) - severityRank(b));",
|
||||
"const stdoutJson = readJson(stdoutPath);",
|
||||
@@ -8332,6 +8363,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
"const slimSlowSample = (item) => { const v = objectOrNull(item) || {}; return { ts: v.ts ?? null, seq: v.seq ?? null, path: clip(v.path ?? v.rawPath, 96), initiatorType: clip(v.initiatorType, 24), durationMs: v.durationMs ?? null, requestToResponseStartMs: v.requestToResponseStartMs ?? v.streamOpenMs ?? null, responseTransferMs: v.responseTransferMs ?? null, nextHopProtocol: clip(v.nextHopProtocol, 24), timingStatus: clip(v.timingStatus, 16), serverTimingNames: Array.isArray(v.serverTimingNames) ? v.serverTimingNames.slice(0, 4).map((x) => clip(x, 32)) : [], otelTraceId: clip(v.otelTraceId, 32) }; };",
|
||||
"const slimSlowApi = (item) => { const v = objectOrNull(item) || {}; return { path: clip(v.path ?? v.route, 96), route: clip(v.route ?? v.path, 96), sampleCount: v.sampleCount ?? null, p95Ms: v.p95Ms ?? v.p95 ?? null, maxMs: v.maxMs ?? v.max ?? null, budgetMs: v.budgetMs ?? null, overBudgetCount: v.overBudgetCount ?? null, overFiveSecondCount: v.overFiveSecondCount ?? null, slowSamples: Array.isArray(v.slowSamples) ? v.slowSamples.slice(0, 3).map(slimSlowSample) : [] }; };",
|
||||
"const slimFinding = (item) => { const v = objectOrNull(item) || {}; return { kind: clip(v.kind ?? v.id ?? v.code, 48), code: clip(v.code ?? v.id ?? v.kind, 48), severity: clip(v.severity ?? v.level, 24), level: clip(v.level ?? v.severity, 24), count: v.count ?? v.sampleCount ?? null, sampleCount: v.sampleCount ?? v.count ?? null, summary: clip(v.summary ?? v.message, 180), message: clip(v.message ?? v.summary, 180) }; };",
|
||||
"const slimProjectManagement = (value) => { const v = objectOrNull(value); if (!v) return null; const s = objectOrNull(v.summary) || v; return { summary: { enabled: s.enabled === true, projectSampleCount: s.projectSampleCount ?? null, mdtodoSampleCount: s.mdtodoSampleCount ?? null, latestPageKind: clip(s.latestPageKind, 48), latestPath: clip(s.latestPath, 96), latestSourceCount: s.latestSourceCount ?? null, latestFileCount: s.latestFileCount ?? null, latestTaskCount: s.latestTaskCount ?? null, latestSelectedTaskRefHash: clip(s.latestSelectedTaskRefHash, 80), launchCommandCount: s.launchCommandCount ?? null, launchSuccessCount: s.launchSuccessCount ?? null, launchFailureCount: s.launchFailureCount ?? null, launchWithOtelTraceHeaderCount: s.launchWithOtelTraceHeaderCount ?? null, projectApiResponseCount: s.projectApiResponseCount ?? null, projectApiFailureCount: s.projectApiFailureCount ?? null, projectApiSlowPathCount: s.projectApiSlowPathCount ?? null, slowApiBudgetMs: s.slowApiBudgetMs ?? null }, commands: takeTail(v.commands, 8).map((item) => { const row = objectOrNull(item) || {}; return { ts: row.ts ?? null, phase: clip(row.phase, 16), type: clip(row.type, 32), commandId: clip(row.commandId, 80), launchStatus: row.launchStatus ?? null, sessionId: clip(row.sessionId, 80), workbenchUrl: clip(row.workbenchUrl, 120), otelTraceId: clip(row.otelTraceId, 32), selectedTaskRefHash: clip(row.selectedTaskRefHash, 80) }; }), samples: takeTail(v.samples, 8).map((item) => { const row = objectOrNull(item) || {}; return { seq: row.seq ?? null, ts: row.ts ?? null, pageRole: clip(row.pageRole, 24), path: clip(row.path, 96), pageKind: clip(row.pageKind, 48), sourceCount: row.sourceCount ?? null, fileCount: row.fileCount ?? null, taskCount: row.taskCount ?? null, selectedTaskRefHash: clip(row.selectedTaskRefHash, 80), launchButtonEnabled: row.launchButtonEnabled === true, workbenchLinkCount: row.workbenchLinkCount ?? null }; }), projectApiByPath: takeHead(v.projectApiByPath, 8).map(slimNetworkGroup), valuesRedacted: true }; };",
|
||||
"const slimDomGroup = (item) => { const v = objectOrNull(item) || {}; return { count: v.count ?? null, firstAt: v.firstAt ?? null, lastAt: v.lastAt ?? null, text: clip(v.text ?? v.preview, 180) }; };",
|
||||
"const slimNetworkGroup = (item) => { const v = objectOrNull(item) || {}; return { count: v.count ?? null, method: clip(v.method, 12), status: v.status ?? null, path: clip(v.path ?? v.urlPath, 96), firstAt: v.firstAt ?? null, lastAt: v.lastAt ?? null, promptIndexes: Array.isArray(v.promptIndexes) ? v.promptIndexes.slice(0, 6) : [], failureKinds: Array.isArray(v.failureKinds) ? v.failureKinds.slice(0, 4).map((x) => clip(x, 48)) : [] }; };",
|
||||
"const slimDomSample = (item) => { const v = objectOrNull(item) || {}; return { seq: v.seq ?? null, ts: v.ts ?? null, source: clip(v.source, 32), diagnosticCode: clip(v.diagnosticCode, 48), traceId: clip(v.traceId, 64), httpStatus: v.httpStatus ?? null, idleSeconds: v.idleSeconds ?? null, waitingFor: clip(v.waitingFor, 48), lastEventLabel: clip(v.lastEventLabel, 80), text: clip(v.text ?? v.preview, 180) }; };",
|
||||
@@ -8400,6 +8432,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
"const commandFailuresFromFindings = [...allFindingsForCommands, ...archiveRedFindings].flatMap((item) => Array.isArray(item?.commands) ? item.commands : []);",
|
||||
"const srcPromptNetwork = objectOrNull(source?.promptNetwork);",
|
||||
"const promptNetwork = srcPromptNetwork ? { promptSegments: srcPromptNetwork.promptSegments ?? null } : null;",
|
||||
"const projectManagement = slimProjectManagement(source?.projectManagement || fullSource?.projectManagement);",
|
||||
"const runnerErrorsFromJsonl = readJsonlTail(reportJsonPath.replace(/\\/analysis\\/report\\.json$/u, '/errors.jsonl'), 8).filter((item) => item?.type === 'runner-error').map(slimRunnerErrorFromJsonl);",
|
||||
"const compact = source ? {",
|
||||
" ok: analyzerExit === 0,",
|
||||
@@ -8410,6 +8443,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" sampleMetrics: metrics,",
|
||||
" runtimeAlerts,",
|
||||
" pagePerformance,",
|
||||
" projectManagement,",
|
||||
" promptNetwork,",
|
||||
" pagePerformanceSlowApi: takeHead(sourceSlowApi, 4).map(slimSlowApi),",
|
||||
" archivePagePerformanceSlowApi: takeHead(archiveSlowApi, 8).map(slimSlowApi),",
|
||||
@@ -8498,6 +8532,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" } : null,",
|
||||
" runtimeAlerts: compact.runtimeAlerts ?? null,",
|
||||
" pagePerformance: compact.pagePerformance ?? null,",
|
||||
" projectManagement: compact.projectManagement ?? null,",
|
||||
" promptNetwork: compact.promptNetwork ?? null,",
|
||||
" toolFindings: Array.isArray(compact.toolFindings) ? compact.toolFindings.slice(0, 8) : [],",
|
||||
" commandState: compact.commandState ?? null,",
|
||||
@@ -8561,6 +8596,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" } : null,",
|
||||
" runtimeAlerts: compact.runtimeAlerts ?? null,",
|
||||
" pagePerformance: compact.pagePerformance ?? null,",
|
||||
" projectManagement: compact.projectManagement ? { summary: compact.projectManagement.summary ?? compact.projectManagement, samples: Array.isArray(compact.projectManagement.samples) ? compact.projectManagement.samples.slice(-4) : [], commands: Array.isArray(compact.projectManagement.commands) ? compact.projectManagement.commands.slice(-4) : [], launchCommands: Array.isArray(compact.projectManagement.launchCommands) ? compact.projectManagement.launchCommands.slice(-4) : [], projectApiByPath: Array.isArray(compact.projectManagement.projectApiByPath) ? compact.projectManagement.projectApiByPath.slice(0, 4) : [], slowProjectApiPerformance: Array.isArray(compact.projectManagement.slowProjectApiPerformance) ? compact.projectManagement.slowProjectApiPerformance.slice(0, 4) : [], valuesRedacted: true } : null,",
|
||||
" toolFindings: Array.isArray(compact.toolFindings) ? compact.toolFindings.slice(0, 8) : [],",
|
||||
" commandState: compact.commandState ?? null,",
|
||||
" findings: Array.isArray(compact.findings) ? compact.findings.slice(0, 4) : [],",
|
||||
@@ -8601,6 +8637,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" } : null,",
|
||||
" runtimeAlerts: compact.runtimeAlerts ?? null,",
|
||||
" pagePerformance: compact.pagePerformance ?? null,",
|
||||
" projectManagement: compact.projectManagement ? { summary: compact.projectManagement.summary ?? compact.projectManagement, samples: Array.isArray(compact.projectManagement.samples) ? compact.projectManagement.samples.slice(-3) : [], commands: Array.isArray(compact.projectManagement.commands) ? compact.projectManagement.commands.slice(-3) : [], launchCommands: Array.isArray(compact.projectManagement.launchCommands) ? compact.projectManagement.launchCommands.slice(-3) : [], projectApiByPath: Array.isArray(compact.projectManagement.projectApiByPath) ? compact.projectManagement.projectApiByPath.slice(0, 3) : [], slowProjectApiPerformance: Array.isArray(compact.projectManagement.slowProjectApiPerformance) ? compact.projectManagement.slowProjectApiPerformance.slice(0, 2) : [], valuesRedacted: true } : null,",
|
||||
" toolFindings: Array.isArray(compact.toolFindings) ? compact.toolFindings.slice(0, 8) : [],",
|
||||
" commandState: compact.commandState ?? null,",
|
||||
" findings: Array.isArray(compact.findings) ? compact.findings.slice(0, 4).map((item) => ({ kind: item.kind ?? item.code ?? null, severity: item.severity ?? item.level ?? null, count: item.count ?? item.sampleCount ?? null, summary: clip(item.summary ?? item.message, 120) })) : [],",
|
||||
@@ -8624,7 +8661,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" output = compactOutput(ultratiny);",
|
||||
" }",
|
||||
" if (Buffer.byteLength(output, 'utf8') > compactStdoutLimitBytes) {",
|
||||
" output = compactOutput({ ok: compact.ok, counts: compact.counts ?? null, jsonlScope: compact.jsonlScope ?? null, analysisWindow: compact.analysisWindow ?? null, archiveSummary: compact.archiveSummary ? { redFindingCount: compact.archiveSummary.redFindingCount ?? null, findingCount: compact.archiveSummary.findingCount ?? null, sampleCount: compact.archiveSummary.sampleMetrics?.sampleCount ?? null, slowPathCount: compact.archiveSummary.pagePerformance?.slowPathCount ?? null } : null, toolFindings: Array.isArray(compact.toolFindings) ? compact.toolFindings.slice(0, 8) : [], commandState: compact.commandState ? { pendingCount: compact.commandState.pendingCount ?? null, processingCount: compact.commandState.processingCount ?? null, abandonedCount: compact.commandState.abandonedCount ?? null, failedCount: compact.commandState.failedCount ?? null } : null, findings: Array.isArray(compact.findings) ? compact.findings.slice(0, 4).map((item) => ({ kind: item.kind ?? item.code ?? null, severity: item.severity ?? item.level ?? null, count: item.count ?? item.sampleCount ?? null, summary: clip(item.summary ?? item.message, 120) })) : [], archiveRedFindings: Array.isArray(compact.archiveRedFindings) ? compact.archiveRedFindings.slice(0, 4).map((item) => ({ kind: item.kind ?? item.code ?? null, severity: item.severity ?? item.level ?? null, count: item.count ?? item.sampleCount ?? null, summary: clip(item.summary ?? item.message, 120) })) : [], commandFailures: Array.isArray(compact.commandFailures) ? compact.commandFailures.slice(-3).map((item) => ({ ts: item.ts ?? null, type: item.type ?? null, commandId: item.commandId ?? null, durationMs: item.durationMs ?? null, sampleSeq: item.sampleSeq ?? null, beforePath: item.beforePath ?? null, afterPath: item.afterPath ?? null, message: clip(item.message ?? item.failureKind ?? item.name, 120) })) : [], archivePagePerformanceSlowApi: Array.isArray(compact.archivePagePerformanceSlowApi) ? compact.archivePagePerformanceSlowApi.slice(0, 4).map((item) => ({ path: item.path ?? item.route ?? null, sampleCount: item.sampleCount ?? null, p95Ms: item.p95Ms ?? null, maxMs: item.maxMs ?? null, overFiveSecondCount: item.overFiveSecondCount ?? null })) : [], reportJsonPath: compact.reportJsonPath ?? reportJsonPath, reportJsonSha256: compact.reportJsonSha256 ?? sha256(reportJsonPath), reportMdPath: compact.reportMdPath ?? reportMdPath, reportMdSha256: compact.reportMdSha256 ?? sha256(reportMdPath), analyzer: { ...(compact.analyzer ?? {}), compactStdoutLimited: true, ultratiny: true, hardFallback: true, compactStdoutLimitBytes }, valuesRedacted: true });",
|
||||
" output = compactOutput({ ok: compact.ok, counts: compact.counts ?? null, jsonlScope: compact.jsonlScope ?? null, analysisWindow: compact.analysisWindow ?? null, archiveSummary: compact.archiveSummary ? { redFindingCount: compact.archiveSummary.redFindingCount ?? null, findingCount: compact.archiveSummary.findingCount ?? null, sampleCount: compact.archiveSummary.sampleMetrics?.sampleCount ?? null, slowPathCount: compact.archiveSummary.pagePerformance?.slowPathCount ?? null } : null, projectManagement: compact.projectManagement ? { summary: compact.projectManagement.summary ?? compact.projectManagement, samples: Array.isArray(compact.projectManagement.samples) ? compact.projectManagement.samples.slice(-2) : [], commands: Array.isArray(compact.projectManagement.commands) ? compact.projectManagement.commands.slice(-2) : [], launchCommands: Array.isArray(compact.projectManagement.launchCommands) ? compact.projectManagement.launchCommands.slice(-2) : [], projectApiByPath: Array.isArray(compact.projectManagement.projectApiByPath) ? compact.projectManagement.projectApiByPath.slice(0, 2) : [], valuesRedacted: true } : null, toolFindings: Array.isArray(compact.toolFindings) ? compact.toolFindings.slice(0, 8) : [], commandState: compact.commandState ? { pendingCount: compact.commandState.pendingCount ?? null, processingCount: compact.commandState.processingCount ?? null, abandonedCount: compact.commandState.abandonedCount ?? null, failedCount: compact.commandState.failedCount ?? null } : null, findings: Array.isArray(compact.findings) ? compact.findings.slice(0, 4).map((item) => ({ kind: item.kind ?? item.code ?? null, severity: item.severity ?? item.level ?? null, count: item.count ?? item.sampleCount ?? null, summary: clip(item.summary ?? item.message, 120) })) : [], archiveRedFindings: Array.isArray(compact.archiveRedFindings) ? compact.archiveRedFindings.slice(0, 4).map((item) => ({ kind: item.kind ?? item.code ?? null, severity: item.severity ?? item.level ?? null, count: item.count ?? item.sampleCount ?? null, summary: clip(item.summary ?? item.message, 120) })) : [], commandFailures: Array.isArray(compact.commandFailures) ? compact.commandFailures.slice(-3).map((item) => ({ ts: item.ts ?? null, type: item.type ?? null, commandId: item.commandId ?? null, durationMs: item.durationMs ?? null, sampleSeq: item.sampleSeq ?? null, beforePath: item.beforePath ?? null, afterPath: item.afterPath ?? null, message: clip(item.message ?? item.failureKind ?? item.name, 120) })) : [], archivePagePerformanceSlowApi: Array.isArray(compact.archivePagePerformanceSlowApi) ? compact.archivePagePerformanceSlowApi.slice(0, 4).map((item) => ({ path: item.path ?? item.route ?? null, sampleCount: item.sampleCount ?? null, p95Ms: item.p95Ms ?? null, maxMs: item.maxMs ?? null, overFiveSecondCount: item.overFiveSecondCount ?? null })) : [], reportJsonPath: compact.reportJsonPath ?? reportJsonPath, reportJsonSha256: compact.reportJsonSha256 ?? sha256(reportJsonPath), reportMdPath: compact.reportMdPath ?? reportMdPath, reportMdSha256: compact.reportMdSha256 ?? sha256(reportMdPath), analyzer: { ...(compact.analyzer ?? {}), compactStdoutLimited: true, ultratiny: true, hardFallback: true, compactStdoutLimitBytes }, valuesRedacted: true });",
|
||||
" }",
|
||||
" }",
|
||||
"}",
|
||||
@@ -8750,7 +8787,7 @@ function recoverWebObserveAnalyzeFromArtifacts(options: NodeWebProbeObserveOptio
|
||||
"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) || {}, 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, promptNetwork: objectOrNull(promptNetwork.summary) || promptNetwork, runtimeAlerts: objectOrNull(runtimeAlerts.summary) || runtimeAlerts, 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) || {}, 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, runtimeAlerts: objectOrNull(runtimeAlerts.summary) || runtimeAlerts, 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 };",
|
||||
"console.log(JSON.stringify(compact));",
|
||||
"UNIDESK_WEB_OBSERVE_RECOVER_ANALYZE_ARTIFACT",
|
||||
].join("\n");
|
||||
@@ -8840,6 +8877,15 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
const runtimeAlerts = record(analysis?.runtimeAlerts);
|
||||
const pagePerformance = record(analysis?.pagePerformance);
|
||||
const pagePerformanceSummary = record(pagePerformance?.summary);
|
||||
const projectManagement = record(analysis?.projectManagement) ?? record(value.projectManagement);
|
||||
const projectManagementSummary = record(projectManagement?.summary) ?? projectManagement;
|
||||
const projectManagementCommandsSource = Array.isArray(projectManagement?.launchCommands) && projectManagement.launchCommands.length > 0
|
||||
? projectManagement.launchCommands
|
||||
: projectManagement?.commands;
|
||||
const projectManagementCommands = webObserveArray(projectManagementCommandsSource).map(record).filter((item): item is Record<string, unknown> => item !== null).slice(-8);
|
||||
const projectManagementSamples = webObserveArray(projectManagement?.samples).map(record).filter((item): item is Record<string, unknown> => item !== null).slice(-8);
|
||||
const projectManagementApiByPath = webObserveArray(projectManagement?.projectApiByPath).map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8);
|
||||
const projectManagementSlowApis = webObserveArray(projectManagement?.slowProjectApiPerformance).map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8);
|
||||
const alertThresholds = record(analysis?.alertThresholds ?? pagePerformanceSummary?.alertThresholds ?? value.alertThresholds);
|
||||
const budgetLabel = (rawValue: unknown): string => {
|
||||
const parsed = Number(rawValue);
|
||||
@@ -8848,6 +8894,7 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
return ms % 1000 === 0 ? `${Math.round(ms / 1000)}s` : `${ms}ms`;
|
||||
};
|
||||
const sameOriginApiBudgetLabel = budgetLabel(alertThresholds?.sameOriginApiSlowMs ?? pagePerformanceSummary?.budgetMs);
|
||||
const projectManagementApiBudgetLabel = budgetLabel(projectManagementSummary?.slowApiBudgetMs ?? alertThresholds?.sameOriginApiSlowMs ?? pagePerformanceSummary?.budgetMs);
|
||||
const partialApiBudgetLabel = budgetLabel(alertThresholds?.partialApiSlowMs);
|
||||
const streamOpenBudgetLabel = budgetLabel(alertThresholds?.longLivedStreamOpenSlowMs);
|
||||
const loadingBudgetLabel = budgetLabel(alertThresholds?.visibleLoadingSlowMs);
|
||||
@@ -8993,6 +9040,47 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
webObserveShort(webObserveText(item.lastAt ?? item.firstAt), 24),
|
||||
webObserveShort(webObserveArray(item.failureKinds).join(",") || "-", 40),
|
||||
]);
|
||||
const projectManagementCommandRows = projectManagementCommands.map((item) => [
|
||||
webObserveShort(webObserveText(item.ts), 24),
|
||||
webObserveShort(webObserveText(item.phase), 12),
|
||||
webObserveShort(webObserveText(item.type), 26),
|
||||
webObserveText(item.launchStatus ?? item.status),
|
||||
webObserveShort(webObserveText(item.sessionId), 24),
|
||||
webObserveShort(webObserveText(item.otelTraceId), 18),
|
||||
webObserveShort(webObserveText(item.selectedTaskRefHash ?? item.taskHash), 18),
|
||||
webObserveShort(webObserveText(item.workbenchUrl ?? item.afterPath), 44),
|
||||
webObserveShort(webObserveText(item.message ?? item.errorMessageHash), 72),
|
||||
]);
|
||||
const projectManagementSampleRows = projectManagementSamples.map((item) => [
|
||||
webObserveText(item.seq),
|
||||
webObserveShort(webObserveText(item.ts), 24),
|
||||
webObserveShort(webObserveText(item.pageRole), 12),
|
||||
webObserveShort(webObserveText(item.pageKind), 28),
|
||||
webObserveShort(webObserveText(item.path), 36),
|
||||
webObserveText(item.sourceCount),
|
||||
webObserveText(item.fileCount),
|
||||
webObserveText(item.taskCount),
|
||||
webObserveShort(webObserveText(item.selectedTaskRefHash), 18),
|
||||
webObserveText(item.launchButtonEnabled),
|
||||
webObserveText(item.workbenchLinkCount),
|
||||
]);
|
||||
const projectManagementApiRows = projectManagementApiByPath.map((item) => [
|
||||
webObserveText(item.count ?? item.sampleCount),
|
||||
webObserveShort(webObserveText(item.type), 12),
|
||||
webObserveText(item.method),
|
||||
webObserveText(item.status),
|
||||
webObserveShort(webObserveText(item.path ?? item.urlPath), 52),
|
||||
webObserveShort(webObserveText(item.lastAt ?? item.firstAt), 24),
|
||||
webObserveShort(webObserveArray(item.failureKinds).join(",") || "-", 40),
|
||||
]);
|
||||
const projectManagementSlowRows = projectManagementSlowApis.map((item) => [
|
||||
webObserveShort(webObserveText(item.path ?? item.route), 52),
|
||||
webObserveText(item.sampleCount),
|
||||
webObserveText(item.p95Ms ?? item.p95),
|
||||
webObserveText(item.maxMs ?? item.max),
|
||||
webObserveText(item.overBudgetCount ?? item.overFiveSecondCount),
|
||||
webObserveShort(webObserveArray(item.slowSamples).map((sample) => webObserveText(record(sample)?.otelTraceId)).filter((text) => text !== "-").join(",") || "-", 36),
|
||||
]);
|
||||
const lines = [
|
||||
`hwlab nodes web-probe observe analyze (${webObserveText(value.status)})`,
|
||||
"",
|
||||
@@ -9047,6 +9135,34 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
["md", webObserveShort(webObserveText(analysis?.reportMdPath), 96), webObserveShort(webObserveText(analysis?.reportMdSha256), 24)],
|
||||
]),
|
||||
"",
|
||||
"Project management:",
|
||||
webObserveTable(["ENABLED", "SAMPLES", "MDTODO", "LATEST", "SRC", "FILES", "TASKS", "SELECTED_TASK", "LAUNCH", "OTEL", "API", `SLOW>${projectManagementApiBudgetLabel}`], [[
|
||||
webObserveText(projectManagementSummary?.enabled),
|
||||
webObserveText(projectManagementSummary?.projectSampleCount),
|
||||
webObserveText(projectManagementSummary?.mdtodoSampleCount),
|
||||
webObserveShort([webObserveText(projectManagementSummary?.latestPageKind), webObserveText(projectManagementSummary?.latestPath)].filter((part) => part !== "-" && part !== "").join(" "), 52),
|
||||
webObserveText(projectManagementSummary?.latestSourceCount),
|
||||
webObserveText(projectManagementSummary?.latestFileCount),
|
||||
webObserveText(projectManagementSummary?.latestTaskCount),
|
||||
webObserveShort(webObserveText(projectManagementSummary?.latestSelectedTaskRefHash), 18),
|
||||
`ok=${webObserveText(projectManagementSummary?.launchSuccessCount)} fail=${webObserveText(projectManagementSummary?.launchFailureCount)} total=${webObserveText(projectManagementSummary?.launchCommandCount)}`,
|
||||
`${webObserveText(projectManagementSummary?.launchWithOtelTraceHeaderCount)}/${webObserveText(projectManagementSummary?.launchSuccessCount)}`,
|
||||
`resp=${webObserveText(projectManagementSummary?.projectApiResponseCount)} fail=${webObserveText(projectManagementSummary?.projectApiFailureCount ?? 0)}/${webObserveText(projectManagementSummary?.projectApiRequestFailedCount ?? 0)}`,
|
||||
webObserveText(projectManagementSummary?.projectApiSlowPathCount),
|
||||
]]),
|
||||
"",
|
||||
"Project management samples:",
|
||||
webObserveTable(["SEQ", "TS", "ROLE", "KIND", "PATH", "SRC", "FILES", "TASKS", "SELECTED", "LAUNCH", "LINKS"], projectManagementSampleRows.length > 0 ? projectManagementSampleRows : [["-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"]]),
|
||||
"",
|
||||
"Project management commands:",
|
||||
webObserveTable(["TS", "PHASE", "TYPE", "STATUS", "SESSION", "OTEL", "TASK", "URL", "MESSAGE"], projectManagementCommandRows.length > 0 ? projectManagementCommandRows : [["-", "-", "-", "-", "-", "-", "-", "-", "-"]]),
|
||||
"",
|
||||
"Project management API:",
|
||||
webObserveTable(["COUNT", "TYPE", "METHOD", "STATUS", "PATH", "LAST", "FAILURE"], projectManagementApiRows.length > 0 ? projectManagementApiRows : [["-", "-", "-", "-", "-", "-", "-"]]),
|
||||
"",
|
||||
`Project management slow API (>${projectManagementApiBudgetLabel}):`,
|
||||
webObserveTable(["PATH", "SAMPLES", "P95", "MAX", "OVER_BUDGET", "OTEL"], projectManagementSlowRows.length > 0 ? projectManagementSlowRows : [["-", "-", "-", "-", "-", "-"]]),
|
||||
"",
|
||||
"Turn timing:",
|
||||
webObserveTable(["ROUNDS", "TURNS", "ROWS", "NON_MONO", "ELAPSED_DEC", "ZERO_RESET", "ELAPSED_JUMP", "TERMINAL_GROWTH", "RECENT_JUMP", "MAX_RECENT_STEP"], [[
|
||||
webObserveText(roundCount),
|
||||
@@ -10477,6 +10593,13 @@ function nodeWebObserveWaitCommandShell(commandId: string, waitMs: number): stri
|
||||
|
||||
function commandSummaryForOutput(payload: Record<string, unknown>): Record<string, unknown> {
|
||||
const text = typeof payload.text === "string" ? payload.text : null;
|
||||
const opaque = (value: unknown) => typeof value === "string" && value.length > 0
|
||||
? {
|
||||
hash: `sha256:${createHash("sha256").update(value).digest("hex")}`,
|
||||
preview: value.length <= 18 ? value : `${value.slice(0, 10)}...${value.slice(-5)}`,
|
||||
bytes: Buffer.byteLength(value),
|
||||
}
|
||||
: null;
|
||||
return {
|
||||
id: payload.id,
|
||||
type: payload.type,
|
||||
@@ -10484,6 +10607,9 @@ function commandSummaryForOutput(payload: Record<string, unknown>): Record<strin
|
||||
label: payload.label ?? null,
|
||||
sessionId: payload.sessionId ?? null,
|
||||
provider: payload.provider ?? null,
|
||||
sourceId: opaque(payload.sourceId),
|
||||
fileRef: opaque(payload.fileRef),
|
||||
taskRef: opaque(payload.taskRef),
|
||||
textHash: text === null ? null : `sha256:${createHash("sha256").update(text).digest("hex")}`,
|
||||
textBytes: text === null ? null : Buffer.byteLength(text),
|
||||
valuesRedacted: true,
|
||||
|
||||
Reference in New Issue
Block a user