45 lines
1.1 KiB
TypeScript
45 lines
1.1 KiB
TypeScript
import { spawn } from "node:child_process";
|
|
|
|
const SINGLE_QUOTE_ESCAPE = "'\"'\"'";
|
|
|
|
export function quoteScpPath(value: string): string {
|
|
return `'${value.replace(/'/g, SINGLE_QUOTE_ESCAPE)}'`;
|
|
}
|
|
|
|
export function formatScpSource(remoteHost: string, remotePath: string): string {
|
|
return `${remoteHost}:${quoteScpPath(remotePath)}`;
|
|
}
|
|
|
|
export async function copyRemoteFileViaScp(params: {
|
|
remoteHost: string;
|
|
remotePath: string;
|
|
localPath: string;
|
|
}): Promise<void> {
|
|
return await new Promise((resolve, reject) => {
|
|
const child = spawn(
|
|
"/usr/bin/scp",
|
|
[
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"-o",
|
|
"StrictHostKeyChecking=accept-new",
|
|
formatScpSource(params.remoteHost, params.remotePath),
|
|
params.localPath,
|
|
],
|
|
{ stdio: ["ignore", "ignore", "pipe"] },
|
|
);
|
|
|
|
let stderr = "";
|
|
child.stderr?.setEncoding("utf8");
|
|
child.stderr?.on("data", (chunk) => {
|
|
stderr += chunk;
|
|
});
|
|
|
|
child.once("error", reject);
|
|
child.once("exit", (code) => {
|
|
if (code === 0) resolve();
|
|
else reject(new Error(`scp failed (${code}): ${stderr.trim()}`));
|
|
});
|
|
});
|
|
}
|