273 lines
12 KiB
TypeScript
273 lines
12 KiB
TypeScript
import type { UniDeskConfig } from "../config";
|
|
import type { RenderedCliResult } from "../output";
|
|
import { desiredAccountNames } from "./accounts";
|
|
import { readCodexPoolConfig } from "./config";
|
|
import { isRecord } from "./config-utils";
|
|
import { protectedManualAccountsForTarget } from "./public-exposure";
|
|
import { renderSub2ApiTempUnschedulableCredentials } from "./redaction";
|
|
import { boolField, compactCapture, parseJsonOutput, runRemoteCodexPoolScript } from "./remote";
|
|
import { compactRuntimeErrors, renderRuntimeResult } from "./runtime-render";
|
|
import { runtimeScript } from "./runtime-remote-script";
|
|
import { parseRuntimeOptions, type RuntimeOptions } from "./runtime-options";
|
|
import { renderRuntimeMutationResult } from "./runtime-mutation-render";
|
|
import { codexPoolRuntimeTarget } from "./runtime-target";
|
|
export async function codexPoolRuntime(config: UniDeskConfig, args: string[]): Promise<Record<string, unknown> | RenderedCliResult> {
|
|
if (args.includes("--help") || args.includes("-h") || args[0] === "help") return renderRuntimeHelp(args);
|
|
const options = parseRuntimeOptions(args);
|
|
const pool = readCodexPoolConfig();
|
|
const target = codexPoolRuntimeTarget(options.targetId);
|
|
const selectedTemplate = options.kind !== "temp-unschedulable" || options.template === null ? null : pool.runtime.templatesById[options.template];
|
|
if (options.template !== null && selectedTemplate === undefined) {
|
|
throw new Error(`unknown runtime template ${options.template}; available: ${pool.runtime.templates.map((item) => item.id).join(", ")}`);
|
|
}
|
|
const payload = {
|
|
action: options.action,
|
|
account: options.account,
|
|
accounts: options.accounts,
|
|
selectConfiguredPolicy: options.selectConfiguredPolicy,
|
|
accountType: options.accountType,
|
|
excludeAccounts: options.excludeAccounts,
|
|
group: options.group,
|
|
allGroups: options.allGroups,
|
|
platform: options.platform,
|
|
pageToken: options.pageToken,
|
|
kind: options.kind,
|
|
priority: options.priority,
|
|
capacity: options.capacity,
|
|
name: options.name,
|
|
confirm: options.confirm,
|
|
full: options.full,
|
|
recent: options.recent,
|
|
since: options.since,
|
|
tail: options.tail,
|
|
groupName: pool.groupName,
|
|
adminEmailDefault: pool.adminEmailDefault,
|
|
managedAccountNames: desiredAccountNames(pool),
|
|
protectedManualAccountNames: protectedManualAccountsForTarget(pool, target).map((item) => item.accountName),
|
|
templates: pool.runtime.templates.map((template) => ({
|
|
id: template.id,
|
|
kind: template.kind,
|
|
description: template.description,
|
|
credentials: renderSub2ApiTempUnschedulableCredentials(template.tempUnschedulable),
|
|
})),
|
|
selectedTemplate: selectedTemplate === null || selectedTemplate === undefined ? null : {
|
|
id: selectedTemplate.id,
|
|
kind: selectedTemplate.kind,
|
|
credentials: renderSub2ApiTempUnschedulableCredentials(selectedTemplate.tempUnschedulable),
|
|
},
|
|
};
|
|
const result = await runRemoteCodexPoolScript(config, "runtime", runtimeScript(payload, target), target);
|
|
const parsed = parseJsonOutput(result.stdout);
|
|
const ok = result.exitCode === 0 && boolField(parsed, "ok", false);
|
|
if (!options.full && !options.raw && !options.json && (options.action === "apply" || options.action === "delete")) {
|
|
return renderRuntimeMutationResult(ok, options, parsed, result.exitCode);
|
|
}
|
|
if (options.raw) {
|
|
return {
|
|
ok,
|
|
action: "platform-infra-sub2api-codex-pool-runtime",
|
|
remote: compactCapture(result, { full: parsed === null || result.exitCode !== 0 }),
|
|
parsed,
|
|
valuesPrinted: false,
|
|
};
|
|
}
|
|
const response = {
|
|
ok,
|
|
action: "platform-infra-sub2api-codex-pool-runtime",
|
|
target: {
|
|
id: target.id,
|
|
route: target.route,
|
|
namespace: target.namespace,
|
|
runtimeMode: target.runtimeMode,
|
|
endpoint: target.serviceDns,
|
|
},
|
|
request: {
|
|
operation: options.action,
|
|
account: options.account,
|
|
accounts: options.accounts,
|
|
selectConfiguredPolicy: options.selectConfiguredPolicy,
|
|
accountType: options.accountType,
|
|
excludeAccounts: options.excludeAccounts,
|
|
group: options.group,
|
|
allGroups: options.allGroups,
|
|
platform: options.platform,
|
|
pageToken: options.pageToken,
|
|
template: options.template,
|
|
kind: options.kind,
|
|
priority: options.priority,
|
|
capacity: options.capacity,
|
|
name: options.name,
|
|
confirm: options.confirm,
|
|
recent: options.recent,
|
|
since: options.action === "errors" || options.action === "events" || options.action === "infrastructure" ? options.since : undefined,
|
|
tail: options.action === "errors" || options.action === "events" || options.action === "infrastructure" ? options.tail : undefined,
|
|
},
|
|
runtime: options.action === "events"
|
|
? compactRuntimeEvents(parsed)
|
|
: options.json && (options.action === "apply" || options.action === "delete")
|
|
? compactRuntimeMutationJson(parsed)
|
|
: compactRuntimeErrors(parsed, options),
|
|
remote: compactCapture(result, { full: result.exitCode !== 0 || parsed === null }),
|
|
disclosure: options.action === "list"
|
|
? {
|
|
get: "bun scripts/cli.ts platform-infra sub2api codex-pool runtime get --account <name-or-id>",
|
|
errors: "bun scripts/cli.ts platform-infra sub2api codex-pool runtime errors [--account <name-or-id>] [--since 24h]",
|
|
}
|
|
: undefined,
|
|
valuesPrinted: false,
|
|
};
|
|
return options.full || options.json ? response : renderRuntimeResult(response, options);
|
|
}
|
|
|
|
function renderRuntimeHelp(args: string[]): RenderedCliResult {
|
|
const actions = ["list", "get", "errors", "events", "infrastructure", "apply", "delete"] as const;
|
|
const requested = actions.includes(args[0] as typeof actions[number]) ? args[0] as typeof actions[number] : null;
|
|
const command = `platform-infra sub2api codex-pool runtime${requested === null ? "" : ` ${requested}`} --help`;
|
|
const common = [
|
|
" --target <id> YAML-selected Sub2API target",
|
|
" --json | --full | --raw Explicit structured or expanded disclosure",
|
|
];
|
|
const actionHelp: Record<typeof actions[number], string[]> = {
|
|
list: [
|
|
"Usage: bun scripts/cli.ts platform-infra sub2api codex-pool runtime list [--recent <duration>] [--target <id>] [--json|--full|--raw]",
|
|
"",
|
|
"Options:",
|
|
" --recent <duration> Show accounts created in the recent m/h/d window",
|
|
...common,
|
|
],
|
|
get: [
|
|
"Usage: bun scripts/cli.ts platform-infra sub2api codex-pool runtime get --account <name-or-id> [--target <id>] [--json|--full|--raw]",
|
|
"",
|
|
"Options:",
|
|
" --account <name-or-id> Exact account selector",
|
|
...common,
|
|
],
|
|
errors: [
|
|
"Usage: bun scripts/cli.ts platform-infra sub2api codex-pool runtime errors [--group <name-or-id>|--all-groups] [--account <name-or-id>] [--platform <name>] [--since <duration>] [--target <id>] [--json|--full|--raw]",
|
|
"",
|
|
"Options:",
|
|
" --group <name-or-id> Analyze one group",
|
|
" --all-groups List every group summary; use --page-token to continue",
|
|
" --account <name-or-id> Drill into one account after selecting a group",
|
|
" --platform <name> Filter by platform",
|
|
" --page-token <group-id> Continue --all-groups pagination",
|
|
" --since <duration> Analysis window such as 30m, 8h, or 7d",
|
|
" --tail <count> Bounded runtime event tail (100..200000)",
|
|
...common,
|
|
"",
|
|
"Next: rerun with --group <name-or-id>, then --account <name-or-id> or trace --request-id <id>.",
|
|
],
|
|
events: [
|
|
"Usage: bun scripts/cli.ts platform-infra sub2api codex-pool runtime events [--since <duration>] [--tail <count>] [--target <id>] [--json|--full|--raw]",
|
|
"",
|
|
"Options:",
|
|
" --since <duration> Policy-event window such as 30m or 8h",
|
|
" --tail <count> Bounded runtime event tail (100..200000)",
|
|
...common,
|
|
],
|
|
infrastructure: [
|
|
"Usage: bun scripts/cli.ts platform-infra sub2api codex-pool runtime infrastructure --account <name-or-id> [--group <name-or-id>] [--platform <name>] [--since <duration>] [--target <id>] [--json|--full|--raw]",
|
|
"",
|
|
"Options:",
|
|
" --account <name-or-id> Exact account selector",
|
|
" --group <name-or-id> Optional group context",
|
|
" --platform <name> Optional platform context",
|
|
" --since <duration> Recent infrastructure error window",
|
|
...common,
|
|
],
|
|
apply: [
|
|
"Usage: bun scripts/cli.ts platform-infra sub2api codex-pool runtime apply (--account <selector>|--accounts <selectors>|--select-configured-policy --account-type apikey) --kind <kind> [kind options] [--confirm] [--target <id>]",
|
|
"",
|
|
"Kinds:",
|
|
" temp-unschedulable Requires --template <id>",
|
|
" priority Requires --priority <1-1000>; lower is higher",
|
|
" capacity Requires --capacity <1-100000>; account concurrency guard",
|
|
" account-name Requires one --account and --name <new-name>",
|
|
"",
|
|
"Selection:",
|
|
" --exclude-accounts <selectors> Exclude explicit exceptions from configured-policy selection",
|
|
" --confirm Write after reviewing the default dry-run",
|
|
...common,
|
|
],
|
|
delete: [
|
|
"Usage: bun scripts/cli.ts platform-infra sub2api codex-pool runtime delete (--account <selector>|--accounts <selectors>|--select-configured-policy --account-type apikey) --kind temp-unschedulable [--confirm] [--target <id>]",
|
|
"",
|
|
"Options:",
|
|
" --exclude-accounts <selectors> Exclude explicit exceptions from configured-policy selection",
|
|
" --confirm Write after reviewing the default dry-run",
|
|
...common,
|
|
],
|
|
};
|
|
const lines = requested === null
|
|
? [
|
|
"Usage: bun scripts/cli.ts platform-infra sub2api codex-pool runtime <action> [options]",
|
|
"",
|
|
`Actions: ${actions.join(", ")}`,
|
|
"",
|
|
"Run runtime <action> --help for action-specific options. Help is local and does not submit a remote job.",
|
|
]
|
|
: actionHelp[requested];
|
|
lines.push("", "STATE mutation=false remoteJobSubmitted=false");
|
|
return {
|
|
ok: true,
|
|
command,
|
|
renderedText: lines.join("\n"),
|
|
contentType: "text/plain",
|
|
projection: { ok: true, mutation: false, remoteJobSubmitted: false, action: requested },
|
|
};
|
|
}
|
|
|
|
function compactRuntimeEvents(parsed: Record<string, unknown> | null): Record<string, unknown> | null {
|
|
if (parsed === null) return null;
|
|
return {
|
|
ok: parsed.ok,
|
|
error: parsed.error,
|
|
operation: parsed.operation,
|
|
source: parsed.source,
|
|
window: parsed.window,
|
|
tail: parsed.tail,
|
|
scannedLineCount: parsed.scannedLineCount,
|
|
policyEventCount: parsed.policyEventCount,
|
|
completionEventCount: parsed.completionEventCount,
|
|
eventCount: parsed.eventCount,
|
|
events: Array.isArray(parsed.events) ? parsed.events : [],
|
|
mutation: false,
|
|
valuesPrinted: false,
|
|
};
|
|
}
|
|
|
|
function compactRuntimeMutationJson(parsed: Record<string, unknown> | null): Record<string, unknown> | null {
|
|
if (parsed === null) return null;
|
|
const items = Array.isArray(parsed.items) ? parsed.items.filter(isRecord) : [];
|
|
const confirmed = parsed.mode === "confirmed";
|
|
return {
|
|
ok: parsed.ok,
|
|
operation: parsed.operation,
|
|
mode: parsed.mode,
|
|
mutation: parsed.mutation,
|
|
partialWrite: parsed.partialWrite,
|
|
selection: parsed.selection,
|
|
summary: parsed.summary,
|
|
items: items.map((item) => {
|
|
const write = isRecord(item.write) ? item.write : {};
|
|
const desiredDiffers = JSON.stringify(item.desired) !== JSON.stringify(item.before);
|
|
return {
|
|
accountName: item.accountName,
|
|
accountId: item.accountId,
|
|
selectionReason: item.selectionReason,
|
|
change: item.change,
|
|
template: item.template,
|
|
before: item.before,
|
|
desired: desiredDiffers ? item.desired : null,
|
|
actual: confirmed ? item.actual : undefined,
|
|
writeStatus: write.status,
|
|
writeError: write.error,
|
|
reconciled: item.reconciled,
|
|
reconcileError: item.reconcileError,
|
|
};
|
|
}),
|
|
credentialsPrinted: false,
|
|
valuesPrinted: false,
|
|
};
|
|
}
|