Files
pikasTech-unidesk/scripts/src/agentrun/public-exposure.ts
T
2026-07-01 11:03:18 +00:00

193 lines
8.9 KiB
TypeScript

// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. public-exposure module for scripts/src/agentrun.ts.
// Moved mechanically from scripts/src/agentrun.ts:2593-2746 for #903.
// SPEC: PJ2026-01020108 cancel lifecycle + PJ2026-01020205 AipodSpec binding + PJ2026-01020302 session policy + PJ2026-01020305 cancel control + PJ2026-01060305/06 YAML execution policy and bounded output draft-2026-06-25-p0.
// Exposes AgentRun lane-scoped policy, AipodSpec SecretRef binding, cancel lifecycle, and bounded default output in the UniDesk CLI.
import { chmodSync, copyFileSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
import { rootPath, type UniDeskConfig } from "../config";
import type { RenderedCliResult } from "../output";
import { applyLocalCaddyManagedSite } from "../pk01-caddy";
import { runSshCommandCapture, type SshCaptureResult } from "../ssh";
import { runRemoteSshCommandCapture } from "../remote";
import { startJob } from "../jobs";
import {
AGENTRUN_CONFIG_PATH,
agentRunLaneSummary,
agentRunPipelineRunName,
agentRunProviderCredentialRefs,
resolveAgentRunLaneTarget,
type AgentRunCancelLifecycleSpec,
type AgentRunLaneSpec,
} from "../agentrun-lanes";
import {
agentRunImageArtifact,
placeholderAgentRunImage,
renderedFilesDigest,
renderedObjectsDigest,
renderAgentRunControlPlaneManifests,
renderAgentRunGitopsFiles,
type AgentRunArtifactService,
} from "../agentrun-manifests";
import { sha256Fingerprint } from "../platform-infra-ops-library";
import type { StatusOptions } from "./options";
import { displayValue, renderTable } from "./options";
import { renderedCliResult } from "./render";
import { renderNextObjectLines, shortSha, yesNo } from "./trigger";
import { record } from "./utils";
export function agentRunControlPlaneStatusCommand(spec: AgentRunLaneSpec, options: Pick<StatusOptions, "sourceCommit" | "pipelineRun">, full: boolean): string {
return [
"bun scripts/cli.ts agentrun control-plane status",
`--node ${spec.nodeId}`,
`--lane ${spec.lane}`,
options.pipelineRun === null ? null : `--pipeline-run ${options.pipelineRun}`,
options.sourceCommit === null ? null : `--source-commit ${options.sourceCommit}`,
full ? "--full" : null,
].filter(Boolean).join(" ");
}
export function renderAgentRunControlPlaneStatusSummary(result: Record<string, unknown>): RenderedCliResult {
const summary = record(result.summary);
const target = record(result.target);
const node = record(target.node);
const runtimeTarget = record(target.runtime);
const ciTarget = record(target.ci);
const source = record(summary.source);
const gitMirror = record(summary.gitMirror);
const ci = record(summary.ci);
const ciRun = record(ci.pipelineRun);
const argo = record(summary.argo);
const runtime = record(summary.runtime);
const runtimeManager = record(runtime.manager);
const nextAction = record(summary.nextAction);
const next = record(result.next);
const disclosure = record(result.disclosure);
const timings = record(result.timings);
const blockers = Array.isArray(summary.blockers) ? summary.blockers.map(String) : [];
const warnings = Array.isArray(summary.warnings) ? summary.warnings.map(String) : [];
const sourceSnapshotMode = source.sourceAuthority === "git-mirror-snapshot" || source.snapshotPresent !== null;
const sourceStatus = sourceSnapshotMode ? yesNo(source.snapshotPresent) : yesNo(source.workspaceClean);
const sourceDetail = sourceSnapshotMode
? `branch=${displayValue(source.branch)} commit=${shortSha(source.remoteBranchCommit)} snapshot=${yesNo(source.snapshotPresent)} stage=${displayValue(source.sourceStageRef ?? "-")}`
: `branch=${displayValue(source.branch)} commit=${shortSha(source.remoteBranchCommit)} detached=${yesNo(source.workspaceDetached)}`;
const lines = [
"AGENTRUN CONTROL-PLANE STATUS",
renderTable(
["NODE", "LANE", "NAMESPACE", "SOURCE", "PIPELINE", "ALIGNED", "RUNTIME", "BLOCKERS"],
[[
displayValue(node.id ?? target.node ?? "-"),
displayValue(target.lane ?? "-"),
displayValue(runtimeTarget.namespace ?? "-"),
shortSha(summary.sourceCommit),
displayValue(summary.expectedPipelineRun ?? "-"),
yesNo(summary.aligned),
yesNo(summary.runtimeAligned),
blockers.length === 0 ? "-" : blockers.join(","),
]],
),
"",
renderTable(
["COMPONENT", "STATUS", "DETAIL"],
[
["source", sourceStatus, sourceDetail],
["git-mirror", yesNo(gitMirror.alreadySynced), `read=${yesNo(gitMirror.readReady)} write=${yesNo(gitMirror.writeReady)} gitops=${shortSha(gitMirror.gitopsCommit)}`],
["ci", yesNo(ciRun.status === "True"), `run=${displayValue(ciRun.name)} status=${displayValue(ciRun.status)} reason=${displayValue(ciRun.reason)} evidenceMissing=${yesNo(ci.evidenceMissing)}`],
["argo", yesNo(argo.syncedToGitops), `sync=${displayValue(argo.syncStatus)} health=${displayValue(argo.healthStatus)} revision=${shortSha(argo.revision)}`],
["runtime", yesNo(runtimeManager.sourceMatchesExpected), `manager=${shortSha(runtimeManager.sourceCommit)} secrets=${yesNo(runtime.secretsReady)} dbSecret=${yesNo(runtime.databaseSecretPresent)} localPgAbsent=${yesNo(runtime.localPostgresAbsent)}`],
],
),
"",
warnings.length === 0 ? "WARNINGS\n-" : ["WARNINGS", ...warnings.map((warning) => `- ${warning}`)].join("\n"),
"",
"NEXT",
` action: ${displayValue(nextAction.summary ?? "-")}`,
nextAction.command ? ` run: ${displayValue(nextAction.command)}` : null,
` full: ${displayValue(next.statusFull ?? disclosure.fullCommand ?? "bun scripts/cli.ts agentrun control-plane status --full")}`,
"",
`TIMINGS source=${displayValue(timings.sourceMs)}ms runtime=${displayValue(timings.runtimeMs)}ms gitMirror=${displayValue(timings.gitMirrorMs)}ms total=${displayValue(timings.totalMs)}ms`,
].filter((line): line is string => line !== null);
return renderedCliResult(result.ok !== false, "agentrun control-plane status", `${lines.join("\n")}\n`);
}
export function renderAgentRunControlPlanePlanSummary(result: Record<string, unknown>): RenderedCliResult {
const target = record(result.target);
const node = record(target.node);
const runtime = record(target.runtime);
const source = record(target.source);
const checks = Array.isArray(result.plannedChecks) ? result.plannedChecks.map(String) : [];
const lines = [
"AGENTRUN CONTROL-PLANE PLAN",
renderTable(
["NODE", "LANE", "NAMESPACE", "SOURCE", "CHECKS", "MUTATION"],
[[
displayValue(node.id ?? target.node ?? "-"),
displayValue(target.lane ?? "-"),
displayValue(runtime.namespace ?? "-"),
displayValue(source.branch ?? "-"),
String(checks.length),
"false",
]],
),
"",
"CHECKS",
...(checks.length === 0 ? ["-"] : checks.slice(0, 12).map((check) => `- ${check}`)),
"",
"NEXT",
...renderNextObjectLines(record(result.next)),
"",
"DETAIL",
" use --full for rendered target details; valuesPrinted=false",
];
return renderedCliResult(result.ok !== false, "agentrun control-plane plan", `${lines.join("\n")}\n`);
}
export function renderAgentRunControlPlaneActionSummary(result: Record<string, unknown>, title: string): RenderedCliResult {
const target = record(result.target);
const node = record(target.node);
const runtime = record(target.runtime);
const summaryRows = [
["ok", String(result.ok !== false)],
["mode", displayValue(result.mode ?? "-")],
["dryRun", displayValue(result.dryRun ?? "-")],
["mutation", displayValue(result.mutation ?? "-")],
["namespace", displayValue(result.namespace ?? runtime.namespace ?? "-")],
];
const countKeys = [
"runnerJobCount",
"inactiveCandidateCount",
"selectedRunnerJobCount",
"protectedActiveRunnerCount",
"remainingAfterSelection",
"deletedRunnerJobCount",
"nonTerminalPodCount",
"forceActive",
];
const countRows = countKeys
.filter((key) => result[key] !== undefined)
.map((key) => [key, displayValue(result[key])]);
const lines = [
title,
renderTable(
["NODE", "LANE", "NAMESPACE", "MODE", "OK"],
[[
displayValue(node.id ?? target.node ?? "-"),
displayValue(target.lane ?? "-"),
displayValue(result.namespace ?? runtime.namespace ?? "-"),
displayValue(result.mode ?? "-"),
String(result.ok !== false),
]],
),
"",
renderTable(["FIELD", "VALUE"], summaryRows),
];
if (countRows.length > 0) lines.push("", renderTable(["COUNT", "VALUE"], countRows));
const nextLines = renderNextObjectLines(record(result.next ?? result.followUp));
if (nextLines.length > 0) lines.push("", "NEXT", ...nextLines);
lines.push("", "DETAIL", " use --full for capture/probe details; valuesPrinted=false");
return renderedCliResult(result.ok !== false, String(result.command ?? "agentrun control-plane"), `${lines.join("\n")}\n`);
}