Improve ssh tran file transfer reliability
This commit is contained in:
+32
-6
@@ -1,6 +1,7 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { type UniDeskConfig, repoRoot } from "./config";
|
||||
import { isApplyPatchV2HelpArgs, runApplyPatchV2, type ApplyPatchV2Executor } from "./apply-patch-v2";
|
||||
import { isSshFileTransferOperation, runSshFileTransferOperation, type SshRemoteCommandExecutor } from "./ssh-file-transfer";
|
||||
|
||||
export interface ParsedSshArgs {
|
||||
remoteCommand: string | null;
|
||||
@@ -824,6 +825,9 @@ export function isSshSkillDiscoveryArgs(args: string[]): boolean {
|
||||
|
||||
export function parseSshArgs(args: string[]): ParsedSshArgs {
|
||||
const subcommand = args[0] ?? "";
|
||||
if (isSshFileTransferOperation(args)) {
|
||||
return { remoteCommand: null, requiresStdin: false, invocationKind: "helper" };
|
||||
}
|
||||
if (isSshSkillDiscoveryArgs(args)) {
|
||||
const toolArgs = subcommand === "skill" ? ["skill-discover", ...args.slice(2)] : ["skill-discover", ...args.slice(1)];
|
||||
return { remoteCommand: shellArgv(toolArgs), requiresStdin: false, invocationKind: "helper", requiredHelpers: ["skill-discover"] };
|
||||
@@ -981,6 +985,9 @@ function parseWinRouteArgs(route: ParsedSshRoute, args: string[]): ParsedSshArgs
|
||||
if (operation.length === 0) {
|
||||
throw new Error(`ssh ${route.raw} requires a Windows operation, for example: ssh ${route.providerId}:win cmd ver or ssh ${route.providerId}:win skills`);
|
||||
}
|
||||
if (operation === "upload" || operation === "download") {
|
||||
return { remoteCommand: null, requiresStdin: false, invocationKind: "helper" };
|
||||
}
|
||||
if (operation === "skills" || operation === "skill-discover" || operation === "discover-skills") {
|
||||
return {
|
||||
remoteCommand: buildWindowsPowerShellInvocation(buildWindowsSkillsDiscoveryScript(args.slice(1))),
|
||||
@@ -1103,7 +1110,7 @@ function buildWindowsCmdLauncherScript(cmdLine: string): string {
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
function buildWindowsPowerShellInvocation(script: string): string {
|
||||
export function buildWindowsPowerShellInvocation(script: string): string {
|
||||
return shellArgv([
|
||||
windowsPowerShellExePath,
|
||||
"-NoProfile",
|
||||
@@ -1292,6 +1299,9 @@ function parseK3sRouteArgs(route: ParsedSshRoute, args: string[]): ParsedSshArgs
|
||||
|
||||
function parseK3sControlPlaneOperation(route: ParsedSshRoute, args: string[]): ParsedSshArgs {
|
||||
const operation = args[0] ?? "guard";
|
||||
if (operation === "upload" || operation === "download") {
|
||||
throw new Error(`ssh ${route.providerId}:k3s ${operation} requires a workload route: ssh ${route.providerId}:k3s:<namespace>:<workload> ${operation} ...`);
|
||||
}
|
||||
if (operation === "apply-patch" || operation === "apply-patch-v1") {
|
||||
throw new Error(`ssh ${route.providerId}:k3s apply-patch requires a workload route: ssh ${route.providerId}:k3s:<namespace>:<workload> apply-patch`);
|
||||
}
|
||||
@@ -1323,6 +1333,9 @@ function parseK3sTargetOperation(route: ParsedSshRoute, args: string[]): ParsedS
|
||||
}
|
||||
const operation = args[0] ?? "";
|
||||
const operationArgs = args.slice(1);
|
||||
if (operation === "upload" || operation === "download") {
|
||||
return { remoteCommand: null, requiresStdin: false, invocationKind: "helper" };
|
||||
}
|
||||
if (operation === "apply-patch") {
|
||||
if (isApplyPatchV2HelpArgs(operationArgs)) {
|
||||
return { remoteCommand: null, requiresStdin: false, invocationKind: "helper" };
|
||||
@@ -1361,8 +1374,8 @@ function buildK3sTargetObjectCommand(action: "get" | "describe", route: ParsedSs
|
||||
return shellArgv(["env", `KUBECONFIG=${nativeK3sKubeconfig}`, "kubectl", action, "-n", route.namespace, normalizeK3sRouteResource(route.resource), ...args]);
|
||||
}
|
||||
|
||||
function buildK3sTargetCommand(route: ParsedSshRoute, command: string[]): string {
|
||||
return buildK3sExecCommand([...k3sRouteTargetArgs(route), "--", ...command]);
|
||||
function buildK3sTargetCommand(route: ParsedSshRoute, command: string[], options: { stdin?: boolean } = {}): string {
|
||||
return buildK3sExecCommand([...k3sRouteTargetArgs(route), ...(options.stdin === true ? ["--stdin"] : []), "--", ...command]);
|
||||
}
|
||||
|
||||
function k3sRouteTargetArgs(route: ParsedSshRoute): string[] {
|
||||
@@ -2072,15 +2085,19 @@ function terminalSize(): { cols: number; rows: number } {
|
||||
};
|
||||
}
|
||||
|
||||
export function remoteCommandForRoute(route: ParsedSshRoute, command: string[]): string {
|
||||
if (route.plane === "k3s") return buildK3sTargetCommand(route, command);
|
||||
export function remoteCommandForRoute(route: ParsedSshRoute, command: string[], options: { stdin?: boolean } = {}): string {
|
||||
if (route.plane === "k3s") return buildK3sTargetCommand(route, command, options);
|
||||
if (route.plane === "win") throw new Error(`ssh apply-patch does not support win routes yet: ${route.raw}`);
|
||||
return shellArgv(command);
|
||||
}
|
||||
|
||||
async function runSshCaptureCommand(config: UniDeskConfig, invocation: ParsedSshInvocation, command: string[], input?: string): Promise<SshCaptureResult> {
|
||||
const remoteCommand = remoteCommandForRoute(invocation.route, command, { stdin: input !== undefined });
|
||||
return await runSshCaptureRemoteCommand(config, invocation, remoteCommand, input);
|
||||
}
|
||||
|
||||
async function runSshCaptureRemoteCommand(config: UniDeskConfig, invocation: ParsedSshInvocation, remoteCommand: string, input?: string): Promise<SshCaptureResult> {
|
||||
const startedAtMs = Date.now();
|
||||
const remoteCommand = remoteCommandForRoute(invocation.route, command);
|
||||
const size = terminalSize();
|
||||
const runtimeTimeoutMs = sshRuntimeTimeoutMs();
|
||||
const payload = {
|
||||
@@ -2184,6 +2201,15 @@ export async function runSsh(config: UniDeskConfig, providerId: string, args: st
|
||||
const invocation = parseSshInvocation(providerId, args);
|
||||
const parsed = invocation.parsed;
|
||||
const operationName = args[0] ?? "";
|
||||
if (isSshFileTransferOperation(args)) {
|
||||
const executor: SshRemoteCommandExecutor = {
|
||||
runRemoteCommand: (remoteCommand, input) => runSshCaptureRemoteCommand(config, invocation, remoteCommand, input),
|
||||
};
|
||||
return await runSshFileTransferOperation(invocation, args, executor, {
|
||||
buildRouteCommand: remoteCommandForRoute,
|
||||
buildWindowsPowerShellCommand: buildWindowsPowerShellInvocation,
|
||||
});
|
||||
}
|
||||
if (operationName === "apply-patch") {
|
||||
const executor: ApplyPatchV2Executor = { run: (command, input) => runSshCaptureCommand(config, invocation, command, input) };
|
||||
return await runApplyPatchV2({
|
||||
|
||||
Reference in New Issue
Block a user