refactor: split control-plane cli modules
This commit is contained in:
@@ -0,0 +1,478 @@
|
||||
// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. trigger module for scripts/src/agentrun.ts.
|
||||
|
||||
// Moved mechanically from scripts/src/agentrun.ts:2747-3186 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 { AgentRunPublicExposure } from "./config";
|
||||
import type { ConfirmOptions, LaneConfirmOptions, SecretSyncOptions, TriggerOptions } from "./options";
|
||||
import { triggerCurrentYamlLaneConfirmed } from "./cleanup-scripts";
|
||||
import { readAgentRunClientConfig } from "./config";
|
||||
import { displayValue } from "./options";
|
||||
import { pathValue } from "./render";
|
||||
import { startAsyncAgentRunJob } from "./rest-bridge";
|
||||
import { collectLaneSecretSources, readSecretSourceValue, restartYamlLaneScript, secretSyncScript } from "./secrets";
|
||||
import { capture, captureJsonPayload, compactCapture, isGitSha, stringOrNull } from "./utils";
|
||||
import { yamlLaneSourceBootstrapProbeScript } from "./yaml-lane";
|
||||
|
||||
export function renderNextObjectLines(next: Record<string, unknown>): string[] {
|
||||
return Object.values(next)
|
||||
.filter((value): value is string => typeof value === "string" && value.length > 0)
|
||||
.slice(0, 5)
|
||||
.map((line) => ` ${line}`);
|
||||
}
|
||||
|
||||
export function yesNo(value: unknown): string {
|
||||
if (value === true) return "yes";
|
||||
if (value === false) return "no";
|
||||
if (value === null || value === undefined) return "-";
|
||||
return displayValue(value);
|
||||
}
|
||||
|
||||
export function shortSha(value: unknown): string {
|
||||
const text = displayValue(value);
|
||||
if (/^[0-9a-f]{40}$/u.test(text)) return text.slice(0, 12);
|
||||
return text;
|
||||
}
|
||||
|
||||
export function compactLaneSecretsStatus(secrets: Record<string, unknown>): Record<string, unknown> {
|
||||
const items = Array.isArray(secrets.items) ? secrets.items.filter((item): item is Record<string, unknown> => typeof item === "object" && item !== null && !Array.isArray(item)) : [];
|
||||
const missing = items
|
||||
.filter((item) => item.present !== true || item.keyPresent !== true)
|
||||
.map((item) => ({
|
||||
namespace: item.namespace ?? null,
|
||||
name: item.name ?? null,
|
||||
key: item.key ?? null,
|
||||
present: item.present ?? false,
|
||||
keyPresent: item.keyPresent ?? false,
|
||||
}));
|
||||
return {
|
||||
ready: secrets.ready ?? false,
|
||||
count: secrets.count ?? items.length,
|
||||
missingCount: missing.length,
|
||||
missing,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function compactAgentRunLaneStatusTarget(spec: AgentRunLaneSpec): Record<string, unknown> {
|
||||
return {
|
||||
node: {
|
||||
id: spec.nodeId,
|
||||
route: spec.nodeRoute,
|
||||
kubeRoute: spec.nodeKubeRoute,
|
||||
},
|
||||
lane: spec.lane,
|
||||
version: spec.version,
|
||||
source: {
|
||||
repository: spec.source.repository,
|
||||
branch: spec.source.branch,
|
||||
},
|
||||
runtime: {
|
||||
namespace: spec.runtime.namespace,
|
||||
managerDeployment: spec.runtime.managerDeployment,
|
||||
managerService: spec.runtime.managerService,
|
||||
},
|
||||
ci: {
|
||||
namespace: spec.ci.namespace,
|
||||
pipeline: spec.ci.pipeline,
|
||||
pipelineRunPrefix: spec.ci.pipelineRunPrefix,
|
||||
},
|
||||
gitops: {
|
||||
branch: spec.gitops.branch,
|
||||
argoApplication: spec.gitops.argoApplication,
|
||||
},
|
||||
gitMirror: {
|
||||
namespace: spec.gitMirror.namespace,
|
||||
readService: spec.gitMirror.readService,
|
||||
writeService: spec.gitMirror.writeService,
|
||||
repositoryCount: spec.gitMirror.repositories.length,
|
||||
},
|
||||
database: {
|
||||
mode: spec.database.mode,
|
||||
provider: spec.database.provider,
|
||||
configRef: spec.database.configRef,
|
||||
localPostgresExpectedAbsent: spec.database.localPostgresExpectedAbsent,
|
||||
valuesPrinted: false,
|
||||
},
|
||||
secretCount: spec.secrets.length,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export async function restartYamlLane(config: UniDeskConfig, options: LaneConfirmOptions): Promise<Record<string, unknown>> {
|
||||
const { configPath, spec } = resolveAgentRunLaneTarget(options);
|
||||
const plan = {
|
||||
node: spec.nodeId,
|
||||
kubeRoute: spec.nodeKubeRoute,
|
||||
lane: spec.lane,
|
||||
namespace: spec.runtime.namespace,
|
||||
deployment: spec.runtime.managerDeployment,
|
||||
annotation: "kubectl.kubernetes.io/restartedAt",
|
||||
reason: "reload-lane-secrets-or-runtime-env",
|
||||
valuesPrinted: false,
|
||||
};
|
||||
if (options.dryRun || !options.confirm) {
|
||||
return {
|
||||
ok: true,
|
||||
command: "agentrun control-plane restart",
|
||||
mode: "dry-run",
|
||||
mutation: false,
|
||||
configPath,
|
||||
target: agentRunLaneSummary(spec),
|
||||
plan,
|
||||
next: {
|
||||
confirm: `bun scripts/cli.ts agentrun control-plane restart --node ${spec.nodeId} --lane ${spec.lane} --confirm`,
|
||||
status: `bun scripts/cli.ts agentrun control-plane status --node ${spec.nodeId} --lane ${spec.lane}`,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
const result = await capture(config, spec.nodeKubeRoute, ["sh", "--", restartYamlLaneScript(spec)]);
|
||||
const payload = captureJsonPayload(result);
|
||||
return {
|
||||
ok: result.exitCode === 0 && payload.ok !== false,
|
||||
command: "agentrun control-plane restart",
|
||||
mode: "confirmed-rollout-restart",
|
||||
mutation: true,
|
||||
configPath,
|
||||
target: agentRunLaneSummary(spec),
|
||||
plan,
|
||||
result: payload,
|
||||
capture: compactCapture(result, { full: result.exitCode !== 0, stdoutTailChars: 4000, stderrTailChars: 4000 }),
|
||||
next: {
|
||||
status: `bun scripts/cli.ts agentrun control-plane status --node ${spec.nodeId} --lane ${spec.lane}`,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export async function secretSync(config: UniDeskConfig, options: SecretSyncOptions): Promise<Record<string, unknown>> {
|
||||
const { configPath, spec } = resolveAgentRunLaneTarget(options);
|
||||
const sources = collectLaneSecretSources(spec);
|
||||
if (sources.length === 0) {
|
||||
return {
|
||||
ok: false,
|
||||
command: "agentrun control-plane secret-sync",
|
||||
mode: "yaml-declared-node-lane",
|
||||
configPath,
|
||||
target: agentRunLaneSummary(spec),
|
||||
degradedReason: "lane-secret-sources-not-declared",
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
const values = sources.map((source) => ({ spec: source, value: readSecretSourceValue(spec, source) }));
|
||||
const plan = values.map(({ spec: item, value }) => ({
|
||||
id: item.id,
|
||||
namespace: item.targetRef.namespace,
|
||||
secret: item.targetRef.name,
|
||||
key: item.targetRef.key,
|
||||
sourceRef: item.sourceRef,
|
||||
sourceMode: item.sourceMode,
|
||||
sourceKey: item.sourceKey,
|
||||
sourcePath: value.redactedPath,
|
||||
fingerprint: value.fingerprint,
|
||||
valueBytes: value.valueBytes,
|
||||
valuesPrinted: false,
|
||||
}));
|
||||
if (options.dryRun || !options.confirm) {
|
||||
return {
|
||||
ok: true,
|
||||
command: "agentrun control-plane secret-sync",
|
||||
mode: "dry-run",
|
||||
mutation: false,
|
||||
configPath,
|
||||
target: agentRunLaneSummary(spec),
|
||||
plan: { secretCount: plan.length, items: plan, valuesPrinted: false },
|
||||
next: {
|
||||
confirm: `bun scripts/cli.ts agentrun control-plane secret-sync --node ${spec.nodeId} --lane ${spec.lane} --confirm`,
|
||||
status: `bun scripts/cli.ts agentrun control-plane status --node ${spec.nodeId} --lane ${spec.lane}`,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
const result = await capture(config, spec.nodeKubeRoute, ["sh", "--", secretSyncScript(spec, values.map(({ spec: item, value }) => ({ targetRef: item.targetRef, value: value.value })))]);
|
||||
const payload = captureJsonPayload(result);
|
||||
return {
|
||||
ok: result.exitCode === 0 && payload.ok !== false,
|
||||
command: "agentrun control-plane secret-sync",
|
||||
mode: "confirmed-sync",
|
||||
mutation: true,
|
||||
configPath,
|
||||
target: agentRunLaneSummary(spec),
|
||||
plan: { secretCount: plan.length, items: plan, valuesPrinted: false },
|
||||
result: payload,
|
||||
capture: compactCapture(result, { full: result.exitCode !== 0, stdoutTailChars: 3000, stderrTailChars: 3000 }),
|
||||
next: {
|
||||
status: `bun scripts/cli.ts agentrun control-plane status --node ${spec.nodeId} --lane ${spec.lane}`,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export async function exposeAgentRun(_config: UniDeskConfig, options: ConfirmOptions): Promise<Record<string, unknown>> {
|
||||
const clientConfig = readAgentRunClientConfig();
|
||||
const exposure = clientConfig.publicExposure;
|
||||
if (!exposure?.enabled) {
|
||||
return {
|
||||
ok: true,
|
||||
command: "agentrun control-plane expose",
|
||||
action: "disabled-by-yaml",
|
||||
publicExposure: exposureSummary(exposure),
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
if (options.dryRun) {
|
||||
return {
|
||||
ok: true,
|
||||
command: "agentrun control-plane expose",
|
||||
dryRun: true,
|
||||
mutation: false,
|
||||
publicExposure: exposureSummary(exposure),
|
||||
plan: {
|
||||
masterFrps: {
|
||||
action: "ensure-allow-port",
|
||||
configPath: exposure.masterFrps.configPath,
|
||||
containerName: exposure.masterFrps.containerName,
|
||||
remotePort: exposure.remotePort,
|
||||
},
|
||||
masterCaddy: {
|
||||
action: "ensure-https-vhost",
|
||||
domain: exposure.masterCaddy.domain,
|
||||
configPath: exposure.masterCaddy.configPath,
|
||||
serviceName: exposure.masterCaddy.serviceName,
|
||||
upstreamBaseUrl: exposure.masterCaddy.upstreamBaseUrl,
|
||||
responseHeaderTimeoutSeconds: exposure.masterCaddy.responseHeaderTimeoutSeconds,
|
||||
},
|
||||
},
|
||||
next: { confirm: "bun scripts/cli.ts agentrun control-plane expose --confirm" },
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
const masterFrps = applyAgentRunMasterFrpsAllowPort(exposure);
|
||||
const masterCaddy = applyAgentRunMasterCaddySite(exposure);
|
||||
const ok = masterFrps.ok === true && masterCaddy.ok === true;
|
||||
return {
|
||||
ok,
|
||||
command: "agentrun control-plane expose",
|
||||
mutation: true,
|
||||
publicExposure: exposureSummary(exposure),
|
||||
masterFrps,
|
||||
masterCaddy,
|
||||
validation: {
|
||||
publicProbe: `curl -fsS ${exposure.publicBaseUrl.replace(/\/+$/u, "")}/health/readiness`,
|
||||
directClient: "bun scripts/cli.ts agentrun get tasks --queue commander -o json",
|
||||
valuesPrinted: false,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function exposureSummary(exposure: AgentRunPublicExposure | null | undefined): Record<string, unknown> {
|
||||
if (!exposure) return { enabled: false, valuesPrinted: false };
|
||||
return {
|
||||
enabled: exposure.enabled,
|
||||
proxyName: exposure.proxyName,
|
||||
remotePort: exposure.remotePort,
|
||||
publicBaseUrl: exposure.publicBaseUrl,
|
||||
masterBaseUrl: exposure.masterBaseUrl,
|
||||
frps: {
|
||||
configPath: exposure.masterFrps.configPath,
|
||||
containerName: exposure.masterFrps.containerName,
|
||||
},
|
||||
caddy: {
|
||||
enabled: exposure.masterCaddy.enabled,
|
||||
domain: exposure.masterCaddy.domain,
|
||||
configPath: exposure.masterCaddy.configPath,
|
||||
serviceName: exposure.masterCaddy.serviceName,
|
||||
upstreamBaseUrl: exposure.masterCaddy.upstreamBaseUrl,
|
||||
responseHeaderTimeoutSeconds: exposure.masterCaddy.responseHeaderTimeoutSeconds,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function applyAgentRunMasterFrpsAllowPort(exposure: AgentRunPublicExposure): Record<string, unknown> {
|
||||
const pathValue = exposure.masterFrps.configPath;
|
||||
const port = exposure.remotePort;
|
||||
const container = exposure.masterFrps.containerName;
|
||||
if (!existsSync(pathValue)) return { ok: false, error: "master-frps-config-missing", path: pathValue, valuesPrinted: false };
|
||||
const before = readFileSync(pathValue, "utf8");
|
||||
const alreadyAllowed = frpsAllowPortExists(before, port);
|
||||
let backupPath: string | null = null;
|
||||
let action = "kept-existing";
|
||||
if (!alreadyAllowed) {
|
||||
const stamp = new Date().toISOString().replace(/[-:]/gu, "").replace(/\..*$/u, "Z");
|
||||
backupPath = `${pathValue}.bak-agentrun-${stamp}`;
|
||||
copyFileSync(pathValue, backupPath);
|
||||
writeFileSync(pathValue, `${before.replace(/\s*$/u, "")}\n\n[[allowPorts]]\nstart = ${port}\nend = ${port}\n`, "utf8");
|
||||
chmodSync(pathValue, statSync(backupPath).mode & 0o777);
|
||||
action = "added-allow-port";
|
||||
}
|
||||
const restart = alreadyAllowed ? null : spawnSync("docker", ["restart", container], { encoding: "utf8" });
|
||||
const inspect = spawnSync("docker", ["inspect", "-f", "{{.State.Running}}", container], { encoding: "utf8" });
|
||||
const ok = alreadyAllowed || (restart?.status === 0 && inspect.status === 0 && String(inspect.stdout).trim() === "true");
|
||||
return {
|
||||
ok,
|
||||
action,
|
||||
path: pathValue,
|
||||
backupPath,
|
||||
remotePort: port,
|
||||
containerName: container,
|
||||
restart: restart === null ? null : { exitCode: restart.status, stdoutTail: String(restart.stdout).slice(-1000), stderrTail: String(restart.stderr).slice(-1000) },
|
||||
inspect: { exitCode: inspect.status, running: String(inspect.stdout).trim() === "true", stderrTail: String(inspect.stderr).slice(-1000) },
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function applyAgentRunMasterCaddySite(exposure: AgentRunPublicExposure): Record<string, unknown> {
|
||||
const caddy = exposure.masterCaddy;
|
||||
if (!caddy.enabled) return { ok: true, action: "disabled-by-yaml", valuesPrinted: false };
|
||||
const pathValue = caddy.configPath;
|
||||
if (!existsSync(pathValue)) return { ok: false, error: "master-caddy-config-missing", path: pathValue, valuesPrinted: false };
|
||||
const desiredBlock = renderAgentRunCaddySiteBlock(caddy.domain, caddy.upstreamBaseUrl, caddy.responseHeaderTimeoutSeconds);
|
||||
const applied = applyLocalCaddyManagedSite({
|
||||
serviceId: "agentrun",
|
||||
markerName: "agentrun",
|
||||
configPath: pathValue,
|
||||
serviceName: caddy.serviceName,
|
||||
siteBlock: desiredBlock,
|
||||
legacySiteDomain: caddy.domain,
|
||||
});
|
||||
return {
|
||||
...applied,
|
||||
domain: caddy.domain,
|
||||
upstreamBaseUrl: caddy.upstreamBaseUrl,
|
||||
responseHeaderTimeoutSeconds: caddy.responseHeaderTimeoutSeconds,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function renderAgentRunCaddySiteBlock(domain: string, upstreamBaseUrl: string, responseHeaderTimeoutSeconds: number): string {
|
||||
const upstream = new URL(upstreamBaseUrl);
|
||||
const upstreamHost = `${upstream.hostname}${upstream.port ? `:${upstream.port}` : ""}`;
|
||||
return `${domain} {
|
||||
encode zstd gzip
|
||||
reverse_proxy ${upstreamHost} {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
transport http {
|
||||
dial_timeout 5s
|
||||
response_header_timeout ${responseHeaderTimeoutSeconds}s
|
||||
}
|
||||
}
|
||||
}`;
|
||||
}
|
||||
|
||||
export function frpsAllowPortExists(toml: string, port: number): boolean {
|
||||
const sections = toml.split(/(?=\[\[allowPorts\]\])/u);
|
||||
return sections.some((section) => {
|
||||
if (!section.includes("[[allowPorts]]")) return false;
|
||||
const start = section.match(/^\s*start\s*=\s*(\d+)\s*$/mu);
|
||||
const end = section.match(/^\s*end\s*=\s*(\d+)\s*$/mu);
|
||||
return Number(start?.[1]) === port && Number(end?.[1]) === port;
|
||||
});
|
||||
}
|
||||
|
||||
export async function triggerCurrent(config: UniDeskConfig, options: TriggerOptions): Promise<Record<string, unknown>> {
|
||||
return await triggerCurrentYamlLane(config, options, resolveAgentRunLaneTarget(options));
|
||||
}
|
||||
|
||||
export async function triggerCurrentYamlLane(config: UniDeskConfig, options: TriggerOptions, target: { configPath: string; spec: AgentRunLaneSpec }): Promise<Record<string, unknown>> {
|
||||
const spec = target.spec;
|
||||
const probe = await capture(config, spec.nodeRoute, ["sh", "--", yamlLaneSourceBootstrapProbeScript(spec)]);
|
||||
const source = captureJsonPayload(probe);
|
||||
const sourceCommit = stringOrNull(source.sourceCommit);
|
||||
const remoteBranchExists = source.remoteBranchExists === true;
|
||||
const pipelineRun = sourceCommit !== null && isGitSha(sourceCommit) ? agentRunPipelineRunName(spec, sourceCommit) : null;
|
||||
const placeholderImage = sourceCommit === null ? null : placeholderAgentRunImage(spec, sourceCommit);
|
||||
const renderedFiles = placeholderImage === null ? [] : renderAgentRunGitopsFiles(spec, { sourceCommit, image: placeholderImage });
|
||||
const plan = {
|
||||
node: spec.nodeId,
|
||||
lane: spec.lane,
|
||||
version: spec.version,
|
||||
source: {
|
||||
workspace: spec.source.workspace,
|
||||
remote: spec.source.remote,
|
||||
branch: spec.source.branch,
|
||||
bootstrapFromBranch: spec.source.bootstrapFromBranch,
|
||||
bootstrapTimeoutSeconds: spec.source.bootstrapTimeoutSeconds,
|
||||
bootstrapPollSeconds: spec.source.bootstrapPollSeconds,
|
||||
remoteBranchExists,
|
||||
sourceCommit,
|
||||
},
|
||||
deploymentFormat: spec.deployment.format,
|
||||
deploymentTruth: "config/agentrun.yaml",
|
||||
removedServiceDeployJson: true,
|
||||
pipelineRun,
|
||||
imageBuild: {
|
||||
repository: `${spec.ci.registryPrefix}/${spec.deployment.manager.imageBuild.repository}`,
|
||||
containerfile: spec.deployment.manager.imageBuild.containerfile,
|
||||
buildArgNames: Object.keys(spec.deployment.manager.imageBuild.buildArgs).sort(),
|
||||
timeoutSeconds: spec.deployment.manager.imageBuild.timeoutSeconds,
|
||||
pollSeconds: spec.deployment.manager.imageBuild.pollSeconds,
|
||||
proxyConfigured: spec.deployment.manager.imageBuild.httpProxy !== null || spec.deployment.manager.imageBuild.httpsProxy !== null,
|
||||
},
|
||||
gitops: {
|
||||
branch: spec.gitops.branch,
|
||||
path: spec.gitops.path,
|
||||
renderedFileCount: renderedFiles.length,
|
||||
renderedFilesDigest: renderedFiles.length === 0 ? null : renderedFilesDigest(renderedFiles),
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
if (options.dryRun || !options.confirm) {
|
||||
return {
|
||||
ok: true,
|
||||
command: "agentrun control-plane trigger-current",
|
||||
mode: "dry-run",
|
||||
mutation: false,
|
||||
configPath: target.configPath,
|
||||
target: agentRunLaneSummary(spec),
|
||||
plan,
|
||||
sourceProbe: compactCapture(probe, { full: probe.exitCode !== 0, stdoutTailChars: 3000, stderrTailChars: 3000 }),
|
||||
next: {
|
||||
confirm: `bun scripts/cli.ts agentrun control-plane trigger-current --node ${spec.nodeId} --lane ${spec.lane} --confirm`,
|
||||
status: `bun scripts/cli.ts agentrun control-plane status --node ${spec.nodeId} --lane ${spec.lane}`,
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
if (options.wait) {
|
||||
return await triggerCurrentYamlLaneConfirmed(config, spec, target.configPath, true);
|
||||
}
|
||||
return startAsyncAgentRunJob(
|
||||
`agentrun_${spec.lane}_trigger_current`,
|
||||
["bun", "scripts/cli.ts", "agentrun", "control-plane", "trigger-current", "--node", spec.nodeId, "--lane", spec.lane, "--confirm", "--wait"],
|
||||
`Run AgentRun ${spec.version} YAML-only trigger-current on ${spec.nodeId}`,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user