293 lines
13 KiB
TypeScript
293 lines
13 KiB
TypeScript
// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. apply-status-scripts module for scripts/src/platform-infra-observability.ts.
|
|
|
|
// Moved mechanically from scripts/src/platform-infra-observability.ts:1300-1574 for #903.
|
|
|
|
// SPEC: PJ2026-01060501 OTel追踪 draft-2026-06-19-p0.
|
|
// Responsibility: YAML-first platform-infra OpenTelemetry tracing control commands.
|
|
import { Buffer } from "node:buffer";
|
|
import { readFileSync } from "node:fs";
|
|
import type { UniDeskConfig } from "../config";
|
|
import { rootPath } from "../config";
|
|
import type { RenderedCliResult } from "../output";
|
|
import {
|
|
compactCapture,
|
|
createYamlFieldReader,
|
|
numberField,
|
|
parseJsonOutput,
|
|
redactSensitiveUnknown,
|
|
shQuote,
|
|
capture,
|
|
} from "../platform-infra-ops-library";
|
|
|
|
import { isoFromUnixNano, shortenEnd, textValue } from "./manifest";
|
|
import { trace } from "./render";
|
|
import { asArray, limitArray } from "./trace-script";
|
|
|
|
export function compactSearchTrace(value: unknown): Record<string, unknown> {
|
|
const trace = asPlainRecord(value) ?? {};
|
|
const meta = compactMetaRecord(trace.meta);
|
|
const startTimeUnixNano = trace.startTimeUnixNano ?? meta?.startTimeUnixNano ?? null;
|
|
return {
|
|
traceId: trace.traceId ?? null,
|
|
startAt: isoFromUnixNano(startTimeUnixNano),
|
|
durationMs: trace.durationMs ?? meta?.durationMs ?? null,
|
|
traceCommand: trace.traceCommand ?? null,
|
|
grepCommand: trace.grepCommand ?? null,
|
|
meta,
|
|
queryOk: trace.queryOk ?? null,
|
|
parseOk: trace.parseOk ?? null,
|
|
rawMatched: trace.rawMatched ?? null,
|
|
matchingActive: trace.matchingActive ?? null,
|
|
bodyBytes: trace.bodyBytes ?? null,
|
|
spanCount: trace.spanCount ?? null,
|
|
serviceCount: trace.serviceCount ?? null,
|
|
services: trace.services ?? null,
|
|
businessTraceIds: trace.businessTraceIds ?? null,
|
|
errorSpanCount: trace.errorSpanCount ?? null,
|
|
matchedSpanCount: trace.matchedSpanCount ?? null,
|
|
spanNameCounts: compactNameCounts(trace.spanNameCounts, 6),
|
|
errorSpanNames: compactSpanNames(trace.errorSpans, 5),
|
|
matchedSpanNames: compactSpanNames(trace.matchedSpans, 8),
|
|
stderrTail: trace.stderrTail ?? null,
|
|
};
|
|
}
|
|
|
|
export function compactDiagnoseCodeAgentResult(value: unknown): Record<string, unknown> {
|
|
const source = asPlainRecord(value) ?? {};
|
|
const mapping = asPlainRecord(source.mapping);
|
|
const evidence = asPlainRecord(source.evidence);
|
|
return {
|
|
ok: source.ok === true,
|
|
mapping: mapping === null ? null : {
|
|
mode: mapping.mode ?? null,
|
|
businessTraceId: mapping.businessTraceId ?? null,
|
|
runId: mapping.runId ?? null,
|
|
commandId: mapping.commandId ?? null,
|
|
sessionId: mapping.sessionId ?? null,
|
|
runnerJobId: mapping.runnerJobId ?? null,
|
|
otelTraceId: mapping.otelTraceId ?? null,
|
|
searchQuery: mapping.searchQuery ?? null,
|
|
searchOk: mapping.searchOk ?? null,
|
|
searchParseOk: mapping.searchParseOk ?? null,
|
|
candidateTraceCount: mapping.candidateTraceCount ?? null,
|
|
scannedCandidateCount: mapping.scannedCandidateCount ?? null,
|
|
candidateSelectionMode: mapping.candidateSelectionMode ?? null,
|
|
selectedScore: mapping.selectedScore ?? null,
|
|
selectedQuality: mapping.selectedQuality ?? null,
|
|
selectedLowConfidence: mapping.selectedLowConfidence ?? null,
|
|
selectedRejectedReason: mapping.selectedRejectedReason ?? null,
|
|
selectedReasons: limitArray(mapping.selectedReasons, 4),
|
|
selectedMeta: compactMetaRecord(mapping.selectedMeta),
|
|
bestCandidate: asArray(mapping.candidatePreview).slice(0, 1).map(compactDiagnoseCandidate)[0] ?? null,
|
|
searchStderrTail: mapping.searchStderrTail ?? null,
|
|
},
|
|
tracePath: source.tracePath ?? null,
|
|
bodyBytes: source.bodyBytes ?? null,
|
|
traceParseOk: source.traceParseOk ?? null,
|
|
spanCount: source.spanCount ?? null,
|
|
services: source.services ?? null,
|
|
servicePath: source.servicePath ?? null,
|
|
observabilityGap: source.observabilityGap ?? null,
|
|
businessTraceIds: source.businessTraceIds ?? null,
|
|
businessTraceScope: source.businessTraceScope ?? null,
|
|
providerDecision: source.providerDecision ?? null,
|
|
identity: compactDiagnoseIdentity(source.identity),
|
|
agentrun: compactDiagnoseAgentRun(source.agentrun),
|
|
hwlabReadModel: source.hwlabReadModel ?? null,
|
|
http: compactDiagnoseHttp(source.http),
|
|
projectionLag: source.projectionLag ?? null,
|
|
summary: source.summary ?? null,
|
|
rootCauseCandidates: asArray(source.rootCauseCandidates).slice(0, 3).map(compactRootCauseCandidate),
|
|
spanNameCounts: compactNameCounts(source.spanNameCounts, 6),
|
|
evidence: evidence === null ? null : {
|
|
httpProblemSpanCount: evidence.httpProblemSpanCount ?? null,
|
|
terminalSpanCount: evidence.terminalSpanCount ?? null,
|
|
turnStatusReadSpanCount: evidence.turnStatusReadSpanCount ?? null,
|
|
projectionSpanCount: evidence.projectionSpanCount ?? null,
|
|
errorSpanCount: evidence.errorSpanCount ?? null,
|
|
errorSpanSampleNames: evidence.errorSpanSampleNames ?? null,
|
|
idleWarningSpanCount: evidence.idleWarningSpanCount ?? null,
|
|
idleWarningSpanTail: compactSpanList(evidence.idleWarningSpanTail, 1),
|
|
},
|
|
next: compactDiagnoseNext(source.next),
|
|
stderrTail: source.stderrTail ?? null,
|
|
disclosure: {
|
|
defaultView: "compact diagnosis; --full is bounded JSON, not a span dump",
|
|
expand: "use trace --grep <span> --full for span evidence; use diagnose-code-agent --raw only for parser/envelope debugging",
|
|
},
|
|
};
|
|
}
|
|
|
|
export function compactDiagnoseIdentity(value: unknown): Record<string, unknown> | null {
|
|
return compactRecord(value, [
|
|
"runId",
|
|
"commandId",
|
|
"sessionId",
|
|
"turnId",
|
|
"runnerJobId",
|
|
"runnerId",
|
|
"backendProfile",
|
|
"sourceCommit",
|
|
"jobName",
|
|
"namespace",
|
|
]);
|
|
}
|
|
|
|
export function compactDiagnoseHttp(value: unknown): Record<string, unknown> | null {
|
|
const http = asPlainRecord(value);
|
|
if (http === null) return null;
|
|
return {
|
|
problemCounts: http.problemCounts ?? null,
|
|
problemSpanCount: http.problemSpanCount ?? null,
|
|
statusCounts: asArray(http.statusCounts).slice(0, 4),
|
|
};
|
|
}
|
|
|
|
export function compactDiagnoseAgentRun(value: unknown): Record<string, unknown> | null {
|
|
const agentrun = asPlainRecord(value);
|
|
if (agentrun === null) return null;
|
|
return {
|
|
terminalStatus: agentrun.terminalStatus ?? null,
|
|
terminalSource: agentrun.terminalSource ?? null,
|
|
latestSeq: agentrun.latestSeq ?? null,
|
|
terminalEventType: agentrun.terminalEventType ?? null,
|
|
failureKind: agentrun.failureKind ?? null,
|
|
observedFailureKinds: limitArray(agentrun.observedFailureKinds, 5),
|
|
runnerProviderClassification: agentrun.runnerProviderClassification ?? null,
|
|
authority: compactAgentRunAuthority(agentrun.authority),
|
|
};
|
|
}
|
|
|
|
export function compactAgentRunAuthority(value: unknown): Record<string, unknown> | null {
|
|
const authority = asPlainRecord(value);
|
|
if (authority === null) return null;
|
|
const result: Record<string, unknown> = {
|
|
queried: authority.queried ?? null,
|
|
namespace: authority.namespace ?? null,
|
|
ok: authority.ok ?? null,
|
|
commandOk: authority.commandOk ?? null,
|
|
resultOk: authority.resultOk ?? null,
|
|
eventsOk: authority.eventsOk ?? null,
|
|
commandState: authority.commandState ?? null,
|
|
commandStatus: authority.commandStatus ?? null,
|
|
resultTerminalStatus: authority.resultTerminalStatus ?? null,
|
|
resultStatus: authority.resultStatus ?? null,
|
|
terminalStatus: authority.terminalStatus ?? null,
|
|
terminalSource: authority.terminalSource ?? null,
|
|
terminalEventSeq: authority.terminalEventSeq ?? null,
|
|
terminalEventType: authority.terminalEventType ?? null,
|
|
failureKind: authority.failureKind ?? null,
|
|
latestSeq: authority.latestSeq ?? null,
|
|
eventCount: authority.eventCount ?? null,
|
|
fallback: authority.fallback ?? null,
|
|
error: authority.error ?? null,
|
|
warnings: limitArray(authority.warnings, 3),
|
|
};
|
|
const ok = authority.ok === true && authority.commandOk === true && authority.resultOk === true && authority.eventsOk === true;
|
|
if (!ok) {
|
|
result.attempts = compactAgentRunAuthorityAttempts(authority.attempts);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export function compactAgentRunAuthorityAttempts(value: unknown): unknown[] {
|
|
return asArray(value).slice(0, 3).map((item) => {
|
|
const attempt = asPlainRecord(item) ?? {};
|
|
return {
|
|
name: attempt.name ?? attempt.section ?? attempt.kind ?? null,
|
|
ok: attempt.ok ?? null,
|
|
status: attempt.status ?? null,
|
|
exitCode: attempt.exitCode ?? null,
|
|
url: attempt.url ?? null,
|
|
error: shortenEnd(textValue(attempt.error), 240),
|
|
stderrTail: shortenEnd(textValue(attempt.stderrTail ?? attempt.stderr), 500),
|
|
};
|
|
});
|
|
}
|
|
|
|
export function compactDiagnoseCandidate(value: unknown): Record<string, unknown> {
|
|
const candidate = asPlainRecord(value) ?? {};
|
|
return {
|
|
traceId: candidate.traceId ?? null,
|
|
score: candidate.score ?? null,
|
|
reasons: limitArray(candidate.reasons, 3),
|
|
parseOk: candidate.parseOk ?? null,
|
|
queryOk: candidate.queryOk ?? null,
|
|
spanCount: candidate.spanCount ?? null,
|
|
services: candidate.services ?? null,
|
|
servicePath: candidate.servicePath ?? null,
|
|
identity: compactDiagnoseIdentity(candidate.identity),
|
|
terminalStatus: candidate.terminalStatus ?? null,
|
|
errorSpanCount: candidate.errorSpanCount ?? null,
|
|
candidateQuality: candidate.candidateQuality ?? null,
|
|
lowConfidence: candidate.lowConfidence ?? null,
|
|
spanNamePreview: limitArray(candidate.spanNamePreview, 4),
|
|
};
|
|
}
|
|
|
|
export function compactDiagnoseNext(value: unknown): Record<string, unknown> | null {
|
|
const next = asPlainRecord(value);
|
|
if (next === null) return null;
|
|
return {
|
|
diagnoseFull: next.diagnoseFull ?? null,
|
|
traceSummary: next.traceSummary ?? null,
|
|
traceReads: next.traceReads ?? null,
|
|
};
|
|
}
|
|
|
|
export function compactRootCauseCandidate(value: unknown): Record<string, unknown> {
|
|
const candidate = asPlainRecord(value) ?? {};
|
|
return {
|
|
code: candidate.code ?? null,
|
|
label: candidate.label ?? null,
|
|
confidence: candidate.confidence ?? null,
|
|
summary: candidate.summary ?? null,
|
|
evidenceCount: Array.isArray(candidate.evidence) ? candidate.evidence.length : candidate.evidence === undefined || candidate.evidence === null ? 0 : 1,
|
|
};
|
|
}
|
|
|
|
export function compactSpanList(value: unknown, limit: number): unknown[] {
|
|
return asArray(value).slice(0, limit).map((item) => {
|
|
const span = asPlainRecord(item) ?? {};
|
|
const attrs = asPlainRecord(span.attributes) ?? {};
|
|
return {
|
|
name: span.name ?? null,
|
|
service: span.service ?? null,
|
|
attributes: compactRecord(attrs, ["failureKind", "terminalStatus", "status", "eventType", "idleMs", "waitingFor", "lastEventLabel", "http.route", "http.status_code", "http.response.status_code", "workbench.session_id", "workbench.trace_id", "workbench.turn_id", "workbench.read_model.route", "workbench.read_model.count", "workbench.read_model.family", "workbench.read_model.status", "workbench.read_model.reason", "hwlab.http.stage", "hwlab.http.phase", "hwlab.http.phase.outcome", "hwlab.live_builds.service_id", "hwlab.live_builds.service_kind", "hwlab.live_builds.external", "hwlab.live_builds.deploy_manifest_status", "hwlab.live_builds.artifact_catalog_status", "providerProfile", "model", "modelId", "providerModel", "code_agent.stage", "agent.chat.session_id", "agent.chat.provider_profile", "agent.chat.provider_profile_source", "agent.chat.model", "agent.chat.model_id", "agent.chat.provider_model", "defaultProviderProfile", "adapter", "adapterEnabled", "agentRunManagerHost", "agentRunRunnerNamespace", "agentRunSourceCommitPresent"]),
|
|
};
|
|
});
|
|
}
|
|
|
|
export function compactSpanNames(value: unknown, limit: number): string[] {
|
|
return asArray(value).slice(0, limit).map((item) => {
|
|
const span = asPlainRecord(item);
|
|
return String(span?.name ?? "<unknown>");
|
|
});
|
|
}
|
|
|
|
export function compactNameCounts(value: unknown, limit: number): unknown[] {
|
|
return asArray(value).slice(0, limit).map((item) => {
|
|
const record = asPlainRecord(item) ?? {};
|
|
return { name: record.name ?? null, count: record.count ?? null };
|
|
});
|
|
}
|
|
|
|
export function compactMetaRecord(value: unknown): Record<string, unknown> | null {
|
|
return compactRecord(value, ["traceID", "traceId", "rootServiceName", "rootTraceName", "startTimeUnixNano", "durationMs"]);
|
|
}
|
|
|
|
export function compactRecord(value: unknown, keys: string[]): Record<string, unknown> | null {
|
|
const source = asPlainRecord(value);
|
|
if (source === null) return null;
|
|
const result: Record<string, unknown> = {};
|
|
for (const key of keys) {
|
|
if (Object.prototype.hasOwnProperty.call(source, key)) result[key] = source[key];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export function asPlainRecord(value: unknown): Record<string, unknown> | null {
|
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
return value as Record<string, unknown>;
|
|
}
|