fix: 增强 AgentRun control-plane status drill-down

This commit is contained in:
Codex
2026-06-10 18:31:02 +00:00
parent 6c39fab601
commit ef5c1c0680
3 changed files with 188 additions and 16 deletions
+176 -7
View File
@@ -48,6 +48,8 @@ export function agentRunHelp(): unknown {
"bun scripts/cli.ts agentrun v01 sessions read <sessionId> --reader-id cli",
"bun scripts/cli.ts agentrun v01 control-plane status",
"bun scripts/cli.ts agentrun v01 control-plane status --full",
"bun scripts/cli.ts agentrun v01 control-plane status --pipeline-run agentrun-v01-ci-<short-sha>",
"bun scripts/cli.ts agentrun v01 control-plane status --source-commit <full-sha>",
"bun scripts/cli.ts agentrun v01 control-plane trigger-current --dry-run",
"bun scripts/cli.ts agentrun v01 control-plane trigger-current --confirm",
"bun scripts/cli.ts agentrun v01 control-plane refresh --dry-run",
@@ -69,7 +71,7 @@ export async function runAgentRunCommand(config: UniDeskConfig, args: string[]):
const [lane, group, action] = args;
if (lane !== "v01") return unsupported(args);
if (group === "control-plane") {
if (action === "status") return await status(config, parseDisclosureOptions(args.slice(3)));
if (action === "status") return await status(config, parseStatusOptions(args.slice(3)));
if (action === "trigger-current") return await triggerCurrent(config, parseTriggerOptions(args.slice(3)));
if (action === "refresh") return await refresh(config, parseConfirmOptions(args.slice(3)));
if (action === "cleanup-runs") return await cleanupRuns(config, parseCleanupRunsOptions(args.slice(3)));
@@ -124,6 +126,12 @@ interface DisclosureOptions {
raw: boolean;
}
interface StatusOptions extends DisclosureOptions {
sourceCommit: string | null;
pipelineRun: string | null;
targetMode: "latest-source-head" | "source-commit" | "pipeline-run";
}
interface TimedValue<T> {
value: T;
elapsedMs: number;
@@ -137,6 +145,50 @@ function parseDisclosureOptions(args: string[]): DisclosureOptions {
return { full: raw || args.includes("--full"), raw };
}
function parseStatusOptions(args: string[]): StatusOptions {
let sourceCommit: string | null = null;
let pipelineRun: string | null = null;
let full = false;
let raw = false;
for (let index = 0; index < args.length; index += 1) {
const arg = args[index];
if (arg === "--full") {
full = true;
continue;
}
if (arg === "--raw") {
raw = true;
full = true;
continue;
}
if (arg === "--source-commit") {
const value = args[index + 1];
if (value === undefined || value.startsWith("--")) throw new Error("--source-commit requires a value");
if (!isGitSha(value)) throw new Error("--source-commit must be a full 40-character git SHA");
sourceCommit = value;
index += 1;
continue;
}
if (arg === "--pipeline-run") {
const value = args[index + 1];
if (value === undefined || value.startsWith("--")) throw new Error("--pipeline-run requires a value");
if (!isAgentRunPipelineRunName(value)) throw new Error("--pipeline-run must be an agentrun-v01-ci-<12+ hex> PipelineRun name");
pipelineRun = value;
index += 1;
continue;
}
throw new Error(`unsupported status option: ${arg}`);
}
if (sourceCommit !== null && pipelineRun !== null) throw new Error("control-plane status accepts only one of --source-commit or --pipeline-run");
return {
full,
raw,
sourceCommit,
pipelineRun,
targetMode: pipelineRun !== null ? "pipeline-run" : sourceCommit !== null ? "source-commit" : "latest-source-head",
};
}
function parseTriggerOptions(args: string[]): TriggerOptions {
return parseConfirmOptions(args);
}
@@ -208,7 +260,7 @@ function positiveIntegerOption(args: string[], name: string, defaultValue: numbe
return Math.min(value, maxValue);
}
async function status(config: UniDeskConfig, options: DisclosureOptions): Promise<Record<string, unknown>> {
async function status(config: UniDeskConfig, options: StatusOptions): Promise<Record<string, unknown>> {
const sourceProbe = await timedStatusStage("source", () => capture(config, g14SourceRoute, ["script", "--", [
"cd /root/agentrun-v01",
"git fetch origin v0.1 >/dev/null 2>&1 || true",
@@ -223,9 +275,17 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
const source = sourceProbe.value;
const localSourceCommit = matchLine(source.stdout, "sourceCommit=");
const originSourceCommit = matchLine(source.stdout, "originV01=");
const sourceCommit = isGitSha(originSourceCommit ?? "") ? originSourceCommit : localSourceCommit;
const latestSourceCommit = isGitSha(originSourceCommit ?? "") ? originSourceCommit : localSourceCommit;
const gitopsLatest = matchLine(source.stdout, "gitopsLatest=");
const pipelineRun = sourceCommit ? pipelineRunName(sourceCommit) : null;
let sourceCommit = options.sourceCommit ?? (options.pipelineRun !== null ? null : latestSourceCommit);
let sourceCommitSource = options.sourceCommit !== null
? "option"
: options.pipelineRun !== null
? "pipeline-run-param"
: sourceCommit === originSourceCommit
? "origin/v0.1"
: "local-head";
const pipelineRun = options.pipelineRun ?? (sourceCommit ? pipelineRunName(sourceCommit) : null);
const [runtimeProbe, mirrorProbe] = await Promise.all([
timedStatusStage("runtime", () => capture(config, g14K3sRoute, ["script", "--", statusScript(pipelineRun)])),
timedStatusStage("git-mirror", () => readGitMirrorStatus(config)),
@@ -237,6 +297,22 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
const pipelineRunCondition = labeledJson(k3s.stdout, "pipelineRunCondition");
const managerImage = parseManagerImage(k3s.stdout);
const mirrorSummary = mirror.summary;
const pipelineRunSourceCommit = stringOrNull(pipelineRunCondition.sourceCommit);
if (options.pipelineRun !== null && sourceCommit === null && pipelineRunSourceCommit !== null && isGitSha(pipelineRunSourceCommit)) {
sourceCommit = pipelineRunSourceCommit;
sourceCommitSource = "pipeline-run-param";
}
const target = {
mode: options.targetMode,
sourceCommit,
sourceCommitSource,
pipelineRun,
pipelineRunSourceCommit,
latestSourceCommit,
latestPipelineRun: latestSourceCommit ? pipelineRunName(latestSourceCommit) : null,
isLatestSource: Boolean(sourceCommit && latestSourceCommit && sourceCommit === latestSourceCommit),
sourceMatchesPipelineRun: sourceCommit === null || pipelineRunSourceCommit === null ? null : sourceCommit === pipelineRunSourceCommit,
};
const runtimeAlignment = {
localHeadMatchesOrigin: Boolean(localSourceCommit && originSourceCommit && localSourceCommit === originSourceCommit),
argoRevision: argo.revision,
@@ -246,7 +322,15 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
managerSourceCommit: managerImage.sourceCommit,
managerSourceMatchesExpected: Boolean(sourceCommit && managerImage.sourceCommit === sourceCommit),
};
const targetValidation = buildAgentRunTargetValidation({
pipelineRun,
pipelineRunCondition,
sourceCommit,
targetIsLatestSource: target.isLatestSource,
managerSourceMatchesExpected: runtimeAlignment.managerSourceMatchesExpected,
});
const summary = {
target,
sourceCommit,
expectedPipelineRun: pipelineRun,
pipelineRun: {
@@ -254,6 +338,7 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
reason: pipelineRunCondition.reason ?? null,
completionTime: pipelineRunCondition.completionTime ?? null,
},
targetValidation,
argo,
managerImage,
gitMirror: {
@@ -278,8 +363,10 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
command: "agentrun v01 control-plane status",
lane: "v0.1",
summary,
target,
sourceCommit,
sourceCommitSource: sourceCommit === originSourceCommit ? "origin/v0.1" : "local-head",
sourceCommitSource,
latestSourceCommit,
localSourceCommit,
originSourceCommit,
gitopsLatest,
@@ -303,16 +390,21 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
...(options.raw ? { raw: mirror.raw } : {}),
},
runtimeAlignment,
targetValidation,
disclosure: {
defaultView: "compact-low-noise",
full: options.full,
raw: options.raw,
stdoutTailOmitted: !(options.full || options.raw),
rawGitMirrorOmitted: !options.raw,
expandWith: "bun scripts/cli.ts agentrun v01 control-plane status --full",
rawWith: "bun scripts/cli.ts agentrun v01 control-plane status --raw",
expandWith: `bun scripts/cli.ts agentrun v01 control-plane status${statusTargetArg(options, target)} --full`,
rawWith: `bun scripts/cli.ts agentrun v01 control-plane status${statusTargetArg(options, target)} --raw`,
},
next: {
statusByPipelineRun: pipelineRun ? `bun scripts/cli.ts agentrun v01 control-plane status --pipeline-run ${pipelineRun} --full` : null,
statusBySourceCommit: sourceCommit ? `bun scripts/cli.ts agentrun v01 control-plane status --source-commit ${sourceCommit} --full` : null,
taskRuns: pipelineRun ? `trans G14:k3s kubectl get taskrun -n ${ciNamespace} -l tekton.dev/pipelineRun=${pipelineRun} -o wide` : null,
logs: pipelineRun ? `trans G14:k3s logs -n ${ciNamespace} -l tekton.dev/pipelineRun=${pipelineRun} --tail 120` : null,
triggerCurrent: "bun scripts/cli.ts agentrun v01 control-plane trigger-current --confirm",
refresh: "bun scripts/cli.ts agentrun v01 control-plane refresh --confirm",
},
@@ -935,8 +1027,12 @@ function statusScript(pipelineRun: string | null): string {
"const fs = require('node:fs');",
"const pr = JSON.parse(fs.readFileSync('/tmp/agentrun-v01-pipelinerun.json', 'utf8'));",
"const condition = pr?.status?.conditions?.[0] || {};",
"const params = Array.isArray(pr?.spec?.params) ? pr.spec.params : [];",
"const sourceCommit = params.find((entry) => entry?.name === 'revision')?.value || null;",
"console.log(JSON.stringify({",
" name: pr?.metadata?.name || null,",
" sourceCommit,",
" createdAt: pr?.metadata?.creationTimestamp || null,",
" status: condition.status || null,",
" reason: condition.reason || null,",
" message: condition.message || null,",
@@ -1709,6 +1805,79 @@ function pipelineRunName(sourceCommit: string): string {
return `agentrun-v01-ci-${sourceCommit.slice(0, 12)}`;
}
function isAgentRunPipelineRunName(value: string): boolean {
return /^agentrun-v01-ci-[0-9a-f]{12,40}(?:-[a-z0-9-]+)?$/u.test(value);
}
function statusTargetArg(options: StatusOptions, target: Record<string, unknown>): string {
if (options.pipelineRun !== null) return ` --pipeline-run ${options.pipelineRun}`;
if (options.sourceCommit !== null) return ` --source-commit ${options.sourceCommit}`;
const sourceCommit = stringOrNull(target.sourceCommit);
return sourceCommit ? ` --source-commit ${sourceCommit}` : "";
}
function buildAgentRunTargetValidation(input: {
pipelineRun: string | null;
pipelineRunCondition: Record<string, unknown>;
sourceCommit: string | null;
targetIsLatestSource: boolean;
managerSourceMatchesExpected: boolean;
}): Record<string, unknown> {
const pipelineRunExists = input.pipelineRun !== null && stringOrNull(input.pipelineRunCondition.name) !== null;
const pipelineStatus = stringOrNull(input.pipelineRunCondition.status);
const pipelineReason = stringOrNull(input.pipelineRunCondition.reason);
const pipelineRunSourceCommit = stringOrNull(input.pipelineRunCondition.sourceCommit);
const sourceMatchesPipelineRun = input.sourceCommit === null || pipelineRunSourceCommit === null ? null : input.sourceCommit === pipelineRunSourceCommit;
let state = "unknown";
const blockers: string[] = [];
const warnings: string[] = [];
if (input.pipelineRun === null) {
blockers.push("pipeline-run-unresolved");
} else if (!pipelineRunExists) {
state = "missing-pipelinerun";
blockers.push("pipeline-run-not-found");
} else if (sourceMatchesPipelineRun === false) {
state = "source-mismatch";
blockers.push("source-commit-does-not-match-pipelinerun-param");
} else if (pipelineStatus === "True") {
state = input.targetIsLatestSource && input.managerSourceMatchesExpected ? "current-succeeded" : "historical-succeeded";
} else if (pipelineStatus === "False") {
state = "pipeline-failed";
blockers.push(pipelineReason ?? "pipeline-failed");
} else if (pipelineStatus === "Unknown") {
state = "pipeline-running";
} else {
state = "pipeline-status-unknown";
warnings.push("pipeline-condition-missing");
}
if (pipelineRunExists && input.targetIsLatestSource && !input.managerSourceMatchesExpected) {
warnings.push("runtime-manager-source-not-yet-aligned");
}
if (pipelineRunExists && !input.targetIsLatestSource) {
warnings.push("target-is-not-latest-source-head");
}
return {
state,
pipelineRunExists,
pipelineStatus,
pipelineReason,
sourceMatchesPipelineRun,
targetIsLatestSource: input.targetIsLatestSource,
managerSourceMatchesExpected: input.managerSourceMatchesExpected,
blockers,
warnings,
interruptedOrUnknown: state === "pipeline-status-unknown" || state === "missing-pipelinerun",
nextActions: {
inspectPipelineRun: input.pipelineRun ? `bun scripts/cli.ts agentrun v01 control-plane status --pipeline-run ${input.pipelineRun} --full` : null,
inspectSourceCommit: input.sourceCommit ? `bun scripts/cli.ts agentrun v01 control-plane status --source-commit ${input.sourceCommit} --full` : null,
triggerCurrent: "bun scripts/cli.ts agentrun v01 control-plane trigger-current --confirm",
},
};
}
function matchLine(text: string, prefix: string): string | null {
const line = text.split(/\r?\n/u).find((item) => item.startsWith(prefix));
return line ? line.slice(prefix.length).trim() || null : null;