Files
pikasTech-unidesk/scripts/cli.ts
T
2026-06-13 17:19:08 +00:00

596 lines
21 KiB
TypeScript

import { readConfig } from "./src/config";
import { debugDispatch, debugHealth, debugSshPool, debugTask, isDebugDispatchCommand, type DebugDispatchCommand } from "./src/debug";
import { isRebuildableService, rebuildService, restartService, stackLogs, stackStatus, startStack, stopStack, unsupportedRebuildService, unsupportedRestartService } from "./src/docker";
import { emitError, emitJson, emitText, isRenderedCliResult } from "./src/output";
import { cancelJob, jobWithTail, listJobs, listJobsSummary, readJob, runJob } from "./src/jobs";
import { checkHelp, parseCheckOptions, runChecks, runRecoveryGuardrailsCheck } from "./src/check";
import { runSsh } from "./src/ssh";
import { autoRemoteCiPublishUserServiceDryRunPlan, extractRemoteCliOptions, runRemoteCli } from "./src/remote";
import { runMicroserviceCommand } from "./src/microservices";
import { runCodeQueueCommand } from "./src/code-queue";
import { runDecisionCenterCommand } from "./src/decision-center";
import { deployHelp, runCodeQueueDeployCompatCommand, runDeployCommand } from "./src/deploy";
import { runProviderCommand } from "./src/provider-attach";
import { runScheduleCommand } from "./src/schedules";
import { parseNetworkPerfOptions, runNetworkPerf } from "./src/network-perf";
import { ciHelp, runCiCommand } from "./src/ci";
import { runSwapCommand } from "./src/swap";
import { runDevEnvCommand } from "./src/dev-env";
import { runArtifactRegistryCommand } from "./src/artifact-registry";
import { runAuthBrokerCommand } from "./src/auth-broker";
import { runGhCommand } from "./src/gh";
import { isGhContentRoute, runGhContentRoute } from "./src/gh-route";
import { runCommanderCommand } from "./src/commander";
import { isHelpToken, rootHelp, serverHelp, sshHelp, staticNamespaceHelp } from "./src/help";
import { runServerCleanupCommand } from "./src/server-cleanup";
import { runGcCommand } from "./src/gc";
import { runPlatformDbCommand } from "./src/platform-db";
import { runSecretsCommand } from "./src/secrets";
const remoteOptions = extractRemoteCliOptions(process.argv.slice(2));
const args = remoteOptions.args;
const commandName = displayCommandName(args);
function displayCommandName(parts: string[]): string {
if (parts.length === 0) return "help";
if (parts[0] === "agentrun" && parts[1] === "v01") {
return ["agentrun", ...parts.slice(2)].join(" ").trim();
}
if (parts[0] === "codex" && (parts[1] === "submit" || parts[1] === "enqueue")) {
const shown = ["codex", parts[1]];
const shownValueOptions = new Set([
"--prompt-file",
"--file",
"--queue",
"--queue-id",
"--provider",
"--provider-id",
"--cwd",
"--workdir",
"--model",
"--reasoning-effort",
"--execution-mode",
"--mode",
"--max-attempts",
"--reference-task-id",
"--reference",
"--ref",
]);
const hasPromptFile = parts.includes("--prompt-file") || parts.includes("--file");
const hasPromptStdin = parts.includes("--prompt-stdin") || parts.includes("--stdin");
const hasHelp = parts.slice(2).some(isHelpToken);
if (!hasPromptFile && !hasPromptStdin && !hasHelp) shown.push("<prompt:redacted>");
for (let index = 2; index < parts.length; index += 1) {
const part = parts[index] ?? "";
if (!part.startsWith("--")) continue;
shown.push(part);
if (shownValueOptions.has(part)) {
shown.push(parts[index + 1] ?? "<missing>");
index += 1;
}
}
return shown.join(" ");
}
if (parts[0] === "codex" && parts[1] === "steer" && parts[2] !== undefined) {
const shown = ["codex", "steer", parts[2]];
const shownValueOptions = new Set(["--prompt-file", "--file", "--retry-attempts", "--retry-delay-ms", "--steer-id", "--steerId"]);
const hasPromptFile = parts.includes("--prompt-file") || parts.includes("--file");
const hasPromptStdin = parts.includes("--prompt-stdin") || parts.includes("--stdin");
const hasHelp = parts.slice(3).some(isHelpToken);
if (!hasPromptFile && !hasPromptStdin && !hasHelp) shown.push("<prompt:redacted>");
for (let index = 3; index < parts.length; index += 1) {
const part = parts[index] ?? "";
if (!part.startsWith("--")) continue;
shown.push(part);
if (shownValueOptions.has(part)) {
shown.push(parts[index + 1] ?? "<missing>");
index += 1;
}
}
return shown.join(" ");
}
if (parts[0] === "codex" && parts[1] === "resume" && parts[2] !== undefined) {
const shown = ["codex", "resume", parts[2]];
const shownValueOptions = new Set(["--prompt-file", "--file", "--resume-id", "--resumeId"]);
const hasPromptFile = parts.includes("--prompt-file") || parts.includes("--file");
const hasPromptStdin = parts.includes("--prompt-stdin") || parts.includes("--stdin");
const hasHelp = parts.slice(3).some(isHelpToken);
if (!hasPromptFile && !hasPromptStdin && !hasHelp) shown.push("<prompt:redacted>");
for (let index = 3; index < parts.length; index += 1) {
const part = parts[index] ?? "";
if (!part.startsWith("--")) continue;
shown.push(part);
if (shownValueOptions.has(part)) {
shown.push(parts[index + 1] ?? "<missing>");
index += 1;
}
}
return shown.join(" ");
}
if (parts[0] === "commander" && parts[1] === "approval" && parts[2] === "request") {
const shown: string[] = [];
for (let index = 0; index < parts.length; index += 1) {
const part = parts[index] ?? "";
shown.push(part);
if (part === "--reason") {
shown.push("<reason:redacted>");
index += 1;
}
}
return shown.join(" ");
}
if (parts[0] === "gh") {
const shown: string[] = [];
for (let index = 0; index < parts.length; index += 1) {
const part = parts[index] ?? "";
shown.push(part);
if (part === "--body" || part === "--comment") {
shown.push("<body:redacted>");
index += 1;
}
}
return shown.join(" ");
}
return parts.join(" ");
}
function numberOption(name: string, defaultValue: number): number {
const index = args.indexOf(name);
if (index === -1) return defaultValue;
const raw = args[index + 1];
const value = Number(raw);
if (!Number.isInteger(value) || value <= 0) throw new Error(`${name} must be a positive integer`);
return value;
}
function boundedNumberOption(name: string, defaultValue: number, maxValue: number): number {
return Math.min(numberOption(name, defaultValue), maxValue);
}
function stringOption(name: string): string | undefined {
const index = args.indexOf(name);
if (index === -1) return undefined;
const raw = args[index + 1];
if (raw === undefined || raw.length === 0) throw new Error(`${name} requires a non-empty value`);
return raw;
}
function jsonOption(name: string): Record<string, unknown> | undefined {
const raw = stringOption(name);
if (raw === undefined) return undefined;
const parsed = JSON.parse(raw) as unknown;
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error(`${name} must be a JSON object`);
return parsed as Record<string, unknown>;
}
function dispatchPayload(command: DebugDispatchCommand): Record<string, unknown> {
const explicit = jsonOption("--payload-json") ?? {};
if (command === "provider.upgrade") {
return { source: "cli-debug", mode: stringOption("--mode") ?? stringOption("--upgrade-mode") ?? "plan", ...explicit };
}
if (command === "host.ssh") {
const sshCommand = stringOption("--ssh-command");
return {
source: "cli-debug",
mode: sshCommand === undefined ? "probe" : "exec",
...(sshCommand === undefined ? {} : { command: sshCommand }),
...(stringOption("--cwd") === undefined ? {} : { cwd: stringOption("--cwd") }),
...(args.includes("--timeout-ms") ? { timeoutMs: numberOption("--timeout-ms", 8000) } : {}),
...explicit,
};
}
return { source: "cli-debug", ...explicit };
}
function resultOk(result: unknown): boolean {
return typeof result !== "object" || result === null || !("ok" in result) || (result as { ok?: unknown }).ok !== false;
}
function latestJobId(): string {
const jobs = listJobs();
if (jobs.length === 0) throw new Error("No jobs found");
return jobs[0].id;
}
async function main(): Promise<void> {
if (remoteOptions.host !== null) {
process.exitCode = await runRemoteCli(remoteOptions, readConfig());
return;
}
const [top, sub, third, fourth] = args;
if (top === undefined || top === "help" || top === "--help" || top === "-h") {
emitJson(commandName, rootHelp());
return;
}
if (top === "ssh" && (sub === undefined || isHelpToken(sub) || (isHelpToken(third) && args.length === 3))) {
emitJson(commandName, sshHelp());
return;
}
if (top === "ssh" && isGhContentRoute(sub)) {
process.exitCode = await runGhContentRoute(sub ?? "", args.slice(2));
return;
}
if (top === "check" && isHelpToken(sub)) {
emitJson(commandName, checkHelp());
return;
}
if (top === "server" && (isHelpToken(sub) || args.slice(2).some(isHelpToken))) {
emitJson(commandName, serverHelp(isHelpToken(sub) ? undefined : sub));
return;
}
if (top === "deploy" && args.slice(1).some(isHelpToken)) {
emitJson(commandName, deployHelp(isHelpToken(sub) ? undefined : sub));
return;
}
if (top === "ci" && (isHelpToken(sub) || args.slice(1).some(isHelpToken))) {
emitJson(commandName, ciHelp());
return;
}
if (top === "agentrun") {
const { runAgentRunCommand } = await import("./src/agentrun");
const agentRunArgs = args.slice(1);
const config = agentRunArgs.length === 0 || agentRunArgs.some(isHelpToken) ? null : readConfig();
const result = await runAgentRunCommand(config, agentRunArgs);
const ok = (result as { ok?: unknown }).ok !== false;
if (isRenderedCliResult(result)) {
emitText(result.renderedText);
if (!ok) process.exitCode = 1;
return;
}
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
const namespaceHelp = await staticNamespaceHelp(args);
if (namespaceHelp !== null) {
emitJson(commandName, namespaceHelp);
return;
}
if (top === "internal" && sub === "run-job") {
if (!third) throw new Error("internal run-job requires job id");
emitJson(commandName, await runJob(third));
return;
}
if (top === "dev-env") {
const result = runDevEnvCommand(args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "artifact-registry") {
const result = await runArtifactRegistryCommand(args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "auth-broker") {
const result = runAuthBrokerCommand(args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "gh") {
const result = await runGhCommand(args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "commander") {
const result = runCommanderCommand(args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "hwlab") {
if (sub === "node" || sub === "nodes") {
const { runHwlabNodeCommand } = await import("./src/hwlab-node");
const result = await runHwlabNodeCommand(readConfig(), args.slice(2));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (sub === "g14") {
const { runHwlabG14Command } = await import("./src/hwlab-g14");
const result = await runHwlabG14Command(readConfig(), args.slice(2));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
const { runHwlabCdCommand } = await import("./src/hwlab-cd");
const result = await runHwlabCdCommand(args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "platform-infra") {
const { runPlatformInfraCommand } = await import("./src/platform-infra");
const result = await runPlatformInfraCommand(readConfig(), args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
if (isRenderedCliResult(result)) {
emitText(result.renderedText);
if (!ok) process.exitCode = 1;
return;
}
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "platform-db") {
const result = await runPlatformDbCommand(readConfig(), args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "secrets") {
const result = await runSecretsCommand(readConfig(), args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
const config = readConfig();
const autoRemoteCiPublishPlan = autoRemoteCiPublishUserServiceDryRunPlan(config, args);
if (autoRemoteCiPublishPlan.enabled && autoRemoteCiPublishPlan.host !== null) {
process.exitCode = await runRemoteCli({
...remoteOptions,
host: autoRemoteCiPublishPlan.host,
transport: "frontend",
args,
}, config);
return;
}
if (top === "ssh") {
const exitCode = await runSsh(config, sub ?? "", args.slice(2));
process.exitCode = exitCode;
return;
}
if (top === "config" && sub === "show") {
emitJson(commandName, { config });
return;
}
if (top === "check") {
if (isHelpToken(sub)) {
emitJson(commandName, checkHelp());
return;
}
if (sub === "recovery-guardrails") {
const result = runRecoveryGuardrailsCheck(config);
emitJson(commandName, result, result.ok);
if (!result.ok) process.exitCode = 1;
return;
}
const result = await runChecks(config, parseCheckOptions(args.slice(1)));
emitJson(commandName, result, result.ok);
if (!result.ok) process.exitCode = 1;
return;
}
if (top === "server") {
if (isHelpToken(sub) || args.slice(2).some(isHelpToken)) {
emitJson(commandName, serverHelp(isHelpToken(sub) ? undefined : sub));
return;
}
if (sub === "start") {
const result = startStack(config);
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (sub === "stop") {
emitJson(commandName, stopStack(config));
return;
}
if (sub === "status") {
const result = await stackStatus(config);
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (sub === "swap") {
const result = runSwapCommand(args.slice(2));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (sub === "logs") {
emitJson(commandName, stackLogs(config, boundedNumberOption("--tail-bytes", 3000, 500_000)));
return;
}
if (sub === "cleanup") {
const result = await runServerCleanupCommand(config, args.slice(2));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (sub === "rebuild") {
if (!isRebuildableService(third)) {
const result = unsupportedRebuildService(third);
emitJson(commandName, result, false);
process.exitCode = 1;
return;
}
emitJson(commandName, rebuildService(config, third));
return;
}
if (sub === "restart") {
if (!isRebuildableService(third)) {
const result = unsupportedRestartService(third);
emitJson(commandName, result, false);
process.exitCode = 1;
return;
}
emitJson(commandName, restartService(config, third));
return;
}
}
if (top === "gc") {
const result = await runGcCommand(config, args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "microservice") {
const result = await runMicroserviceCommand(config, args.slice(1));
const ok = resultOk(result);
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "decision" || top === "decision-center") {
const result = await runDecisionCenterCommand(config, args.slice(1));
const ok = resultOk(result);
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "deploy") {
const result = await runDeployCommand(config, args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "provider") {
emitJson(commandName, await runProviderCommand(config, args.slice(1)));
return;
}
if (top === "schedule") {
const result = await runScheduleCommand(config, args.slice(1));
const ok = resultOk(result);
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "codex") {
if (sub === "deploy") {
const result = await runCodeQueueDeployCompatCommand(config, args.slice(2));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
const result = await runCodeQueueCommand(config, args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "job") {
if (sub === "list") {
emitJson(commandName, listJobsSummary({ limit: boundedNumberOption("--limit", 50, 500), includeCommand: args.includes("--include-command") }));
return;
}
if (sub === "status") {
const id = third === "latest" || third === undefined ? latestJobId() : third;
emitJson(commandName, { job: jobWithTail(readJob(id), boundedNumberOption("--tail-bytes", 12000, 500_000)) });
return;
}
if (sub === "cancel") {
if (!third) throw new Error("job cancel requires job id");
emitJson(commandName, cancelJob(third));
return;
}
}
if (top === "debug") {
if (sub === "health") {
emitJson(commandName, await debugHealth(config));
return;
}
if (sub === "ssh-pool") {
const providerId = third ?? "";
if (providerId.length === 0) throw new Error("debug ssh-pool requires providerId");
const result = await debugSshPool(config, providerId);
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (sub === "dispatch") {
const providerId = isDebugDispatchCommand(third) ? config.providerGateway.id : third ?? config.providerGateway.id;
const commandArg = isDebugDispatchCommand(third) ? third : fourth;
const dispatchCommand = isDebugDispatchCommand(commandArg) ? commandArg : "docker.ps";
emitJson(commandName, await debugDispatch(config, providerId, dispatchCommand, dispatchPayload(dispatchCommand), numberOption("--wait-ms", 0)));
return;
}
if (sub === "task") {
emitJson(commandName, await debugTask(config, third ?? "latest"));
return;
}
}
if (top === "network" && sub === "perf") {
emitJson(commandName, await runNetworkPerf(parseNetworkPerfOptions(config, args.slice(2))));
return;
}
if (top === "ci") {
const result = await runCiCommand(config, args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "e2e" && sub === "run") {
const { parseE2ERunOptions, runE2E } = await import("./src/e2e");
const result = await runE2E(config, parseE2ERunOptions(args.slice(2)));
const ok = (result as { ok?: unknown }).ok === true;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
throw new Error(`Unknown command: ${commandName}`);
}
main().catch((error) => {
emitError(commandName, error);
process.exitCode = 1;
});