feat: add provider ssh bridge
This commit is contained in:
+54
-5
@@ -1,10 +1,11 @@
|
||||
import { readConfig } from "./src/config";
|
||||
import { debugDispatch, debugHealth } from "./src/debug";
|
||||
import { debugDispatch, debugHealth, debugTask, isDebugDispatchCommand, type DebugDispatchCommand } from "./src/debug";
|
||||
import { stackLogs, stackStatus, startStack, stopStack } from "./src/docker";
|
||||
import { runE2E } from "./src/e2e";
|
||||
import { emitError, emitJson } from "./src/output";
|
||||
import { jobWithTail, listJobs, readJob, runJob } from "./src/jobs";
|
||||
import { runChecks } from "./src/check";
|
||||
import { runSsh } from "./src/ssh";
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const commandName = args.join(" ") || "help";
|
||||
@@ -21,10 +22,12 @@ function help(): unknown {
|
||||
{ command: "server stop", description: "Fire-and-forget docker-compose down for the fixed UniDesk stack." },
|
||||
{ command: "server status", description: "Show fixed ports, containers, service health, and public URLs." },
|
||||
{ command: "server logs [--tail-bytes N]", description: "Return bounded tails from file logs and docker logs." },
|
||||
{ command: "ssh <providerId> [ssh-like args...]", description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge." },
|
||||
{ command: "job list", description: "List async jobs from .state/jobs." },
|
||||
{ command: "job status <jobId|latest> [--tail-bytes N]", description: "Show job state with bounded stdout/stderr tails." },
|
||||
{ command: "debug health", description: "Probe internal core, nodes, system/Docker status, frontend, provider ingress, and public boundary." },
|
||||
{ command: "debug dispatch [providerId] [docker.ps|provider.upgrade|echo]", description: "Submit a real internal-core dispatch request for CLI debugging." },
|
||||
{ command: "debug dispatch [providerId] [docker.ps|provider.upgrade|host.ssh|echo] [--wait-ms N]", description: "Submit a real internal-core dispatch request for CLI debugging." },
|
||||
{ command: "debug task <taskId|latest>", description: "Read a dispatched task record from internal core for CLI debugging." },
|
||||
{ command: "e2e run", description: "Run public frontend/provider, internal core/database, and Playwright login E2E checks." },
|
||||
],
|
||||
};
|
||||
@@ -39,6 +42,41 @@ function numberOption(name: string, defaultValue: number): number {
|
||||
return value;
|
||||
}
|
||||
|
||||
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 latestJobId(): string {
|
||||
const jobs = listJobs();
|
||||
if (jobs.length === 0) throw new Error("No jobs found");
|
||||
@@ -60,6 +98,12 @@ async function main(): Promise<void> {
|
||||
|
||||
const config = readConfig();
|
||||
|
||||
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;
|
||||
@@ -109,9 +153,14 @@ async function main(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
if (sub === "dispatch") {
|
||||
const providerId = third ?? config.providerGateway.id;
|
||||
const dispatchCommand = fourth === "docker.ps" || fourth === "provider.upgrade" || fourth === "echo" ? fourth : "docker.ps";
|
||||
emitJson(commandName, await debugDispatch(config, providerId, dispatchCommand));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user