fix: compact noisy cli health outputs
This commit is contained in:
@@ -87,6 +87,14 @@ function bool(value: unknown): boolean {
|
||||
return value === true;
|
||||
}
|
||||
|
||||
function hasFlag(args: string[], name: string): boolean {
|
||||
return args.includes(name);
|
||||
}
|
||||
|
||||
function shellQuote(value: string): string {
|
||||
return `'${value.replace(/'/g, `'\\''`)}'`;
|
||||
}
|
||||
|
||||
function lower(value: unknown): string {
|
||||
return String(value ?? "").toLowerCase();
|
||||
}
|
||||
@@ -292,6 +300,127 @@ function observedErrorSignal(message: string, scope: ProviderSignalScope): Provi
|
||||
return signal("observed-error", scope, "failed", message, { message, runnerErrorClassification: classifyRunnerError(message) }, scope !== "runner-local");
|
||||
}
|
||||
|
||||
function compactStringList(value: unknown, limit = 6): Record<string, unknown> {
|
||||
const all = Array.from(new Set(asArray(value).map((item) => String(item ?? "")).filter(Boolean)));
|
||||
return {
|
||||
items: all.slice(0, limit),
|
||||
count: all.length,
|
||||
truncated: all.length > limit,
|
||||
omitted: Math.max(0, all.length - limit),
|
||||
};
|
||||
}
|
||||
|
||||
function compactEvidence(value: unknown): unknown {
|
||||
const record = asRecord(value);
|
||||
if (record === null) return value;
|
||||
const body = bodyOf(value);
|
||||
const devReady = asRecord(record.devReady) ?? asRecord(body?.devReady);
|
||||
const diagnostics = asRecord(record.executionDiagnostics) ?? asRecord(body?.executionDiagnostics) ?? asRecord(asRecord(value)?.diagnostics);
|
||||
return {
|
||||
upstream: record.upstream ?? (body === null ? null : { ok: asRecord(value)?.ok ?? null, status: asRecord(value)?.status ?? null }),
|
||||
status: record.status ?? body?.status ?? null,
|
||||
ok: record.ok ?? body?.ok ?? null,
|
||||
serviceId: record.serviceId ?? body?.serviceId ?? null,
|
||||
providerGatewayVersion: record.providerGatewayVersion ?? null,
|
||||
hostSshConfigured: record.hostSshConfigured ?? null,
|
||||
taskId: record.taskId ?? null,
|
||||
taskStatus: record.taskStatus ?? null,
|
||||
exitCode: record.exitCode ?? null,
|
||||
devReady: devReady === null ? null : {
|
||||
ok: devReady.ok ?? null,
|
||||
missingTools: compactStringList(devReady.missingTools),
|
||||
},
|
||||
executionDiagnostics: diagnostics === null ? null : {
|
||||
state: diagnostics.state ?? null,
|
||||
effectiveLiveness: diagnostics.effectiveLiveness ?? null,
|
||||
recommendedAction: diagnostics.recommendedAction ?? null,
|
||||
splitBrainLive: diagnostics.splitBrainLive ?? null,
|
||||
heartbeatFreshTaskIds: compactStringList(diagnostics.heartbeatFreshTaskIds),
|
||||
heartbeatRiskTaskIds: compactStringList(diagnostics.heartbeatRiskTaskIds),
|
||||
},
|
||||
fallback: record.fallback === undefined ? null : record.fallback,
|
||||
error: record.error ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function providerTriageCommand(providerId: string, args: string[], mode: "--full" | "--raw"): string {
|
||||
const kept: string[] = [];
|
||||
const valueOptions = new Set(["--observed-error", "--observed-scope", "--microservice", "--service", "--microservices"]);
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index] ?? "";
|
||||
if (arg === "--full" || arg === "--raw") continue;
|
||||
if (valueOptions.has(arg)) {
|
||||
const value = args[index + 1];
|
||||
if (value !== undefined) {
|
||||
kept.push(arg, shellQuote(value));
|
||||
index += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
kept.push(arg);
|
||||
}
|
||||
return [`${commandPrefix} provider triage ${providerId}`, ...kept, mode].filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export function compactProviderTriageResult(result: ProviderTriageResult, args: string[] = []): Record<string, unknown> {
|
||||
const issueSignals = result.signals
|
||||
.filter((item) => item.status === "failed" || item.status === "degraded" || item.status === "unknown")
|
||||
.sort((left, right) => {
|
||||
const rank: Record<ProviderSignalStatus, number> = { failed: 0, degraded: 1, unknown: 2, ok: 3 };
|
||||
return rank[left.status] - rank[right.status];
|
||||
});
|
||||
const sourceSignals = issueSignals.length > 0 ? issueSignals : [];
|
||||
const signalLimit = issueSignals.length > 0 ? 8 : 0;
|
||||
const visibleSignals = sourceSignals.slice(0, signalLimit);
|
||||
const okSignalCount = result.signals.filter((item) => item.status === "ok").length;
|
||||
const issueSignalCount = issueSignals.length;
|
||||
return {
|
||||
ok: result.ok,
|
||||
providerId: result.providerId,
|
||||
decision: result.decision,
|
||||
scope: result.scope,
|
||||
retryable: result.retryable,
|
||||
blockingDisposition: result.blockingDisposition,
|
||||
observedAt: result.observedAt,
|
||||
failedScopes: result.failedScopes,
|
||||
degradedScopes: result.degradedScopes,
|
||||
healthyScopes: result.healthyScopes,
|
||||
failedIndependentScopes: result.failedIndependentScopes,
|
||||
healthyIndependentScopes: result.healthyIndependentScopes,
|
||||
rationale: result.rationale,
|
||||
signalCounts: {
|
||||
total: result.signals.length,
|
||||
returned: visibleSignals.length,
|
||||
limit: signalLimit,
|
||||
ok: okSignalCount,
|
||||
degraded: result.signals.filter((item) => item.status === "degraded").length,
|
||||
failed: result.signals.filter((item) => item.status === "failed").length,
|
||||
unknown: result.signals.filter((item) => item.status === "unknown").length,
|
||||
omittedOkSignals: Math.max(0, result.signals.filter((item) => item.status === "ok").length - visibleSignals.filter((item) => item.status === "ok").length),
|
||||
omittedIssueSignals: Math.max(0, issueSignalCount - visibleSignals.filter((item) => item.status === "failed" || item.status === "degraded" || item.status === "unknown").length),
|
||||
omittedSignals: Math.max(0, sourceSignals.length - visibleSignals.length),
|
||||
},
|
||||
signals: visibleSignals.map((item) => ({
|
||||
id: item.id,
|
||||
scope: item.scope,
|
||||
status: item.status,
|
||||
independentPath: item.independentPath,
|
||||
observedAt: item.observedAt,
|
||||
summary: item.summary,
|
||||
evidenceSummary: compactEvidence(item.evidence),
|
||||
})),
|
||||
recommendedCrossChecks: result.recommendedCrossChecks.slice(0, 8),
|
||||
outputPolicy: {
|
||||
default: "compact-triage-summary",
|
||||
signalLimit,
|
||||
full: providerTriageCommand(result.providerId, args, "--full"),
|
||||
raw: providerTriageCommand(result.providerId, args, "--raw"),
|
||||
note: "Default output returns prioritized failed/degraded/unknown signals plus bounded evidence. Use --full or --raw only when complete evidence is required.",
|
||||
},
|
||||
contract: result.contract,
|
||||
};
|
||||
}
|
||||
|
||||
export function providerTriageRecommendedCrossChecks(providerId: string): string[] {
|
||||
return [
|
||||
`${commandPrefix} provider triage ${providerId}`,
|
||||
@@ -429,10 +558,12 @@ function optionValue(args: string[], name: string): string | undefined {
|
||||
}
|
||||
|
||||
function assertKnownOptions(args: string[]): void {
|
||||
const flags = new Set(["--full", "--raw"]);
|
||||
const valueOptions = new Set(["--observed-error", "--observed-scope", "--microservice", "--service", "--microservices"]);
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index] ?? "";
|
||||
if (!arg.startsWith("--")) continue;
|
||||
if (flags.has(arg)) continue;
|
||||
if (!valueOptions.has(arg)) throw new Error(`unsupported provider triage option: ${arg}`);
|
||||
const value = args[index + 1];
|
||||
if (value === undefined || value.startsWith("--")) throw new Error(`${arg} requires a value`);
|
||||
@@ -440,7 +571,7 @@ function assertKnownOptions(args: string[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
export async function runProviderTriage(config: UniDeskConfig, providerId: string, args: string[] = []): Promise<ProviderTriageResult> {
|
||||
export async function runProviderTriage(config: UniDeskConfig, providerId: string, args: string[] = []): Promise<unknown> {
|
||||
if (!/^[A-Za-z0-9_.-]{1,64}$/u.test(providerId)) throw new Error("provider triage requires a safe provider id such as D601");
|
||||
assertKnownOptions(args);
|
||||
const observedAt = isoNow();
|
||||
@@ -497,5 +628,6 @@ export async function runProviderTriage(config: UniDeskConfig, providerId: strin
|
||||
}
|
||||
}
|
||||
|
||||
return buildProviderTriageResult(providerId, signals, observedAt);
|
||||
const result = buildProviderTriageResult(providerId, signals, observedAt);
|
||||
return hasFlag(args, "--full") || hasFlag(args, "--raw") ? result : compactProviderTriageResult(result, args);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user