mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-19 16:39:50 +00:00
fix: add per-process timeout to SSH handshake probes in waitForSsh (#2299)
The Phase 2 SSH handshake loop in waitForSsh spawns SSH processes without a per-process timeout. ConnectTimeout=10 only covers TCP connect — if sshd accepts the connection but stalls during key exchange or authentication, the process hangs indefinitely. This causes the entire spawn command to freeze with no way to recover. Add a 30s killWithTimeout guard to each probe, matching the pattern already used in every cloud-specific runServer/uploadFile function. -- refactor/code-health Agent: code-health Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
099ad8940e
commit
90ae485c02
2 changed files with 25 additions and 16 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@openrouter/spawn",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.10",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"spawn": "cli.js"
|
||||
|
|
|
|||
|
|
@ -251,23 +251,32 @@ export async function waitForSsh(opts: WaitForSshOpts): Promise<void> {
|
|||
],
|
||||
},
|
||||
);
|
||||
const [stdout, stderr] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
]);
|
||||
const exitCode = await proc.exited;
|
||||
// Per-process timeout: ConnectTimeout=10 only covers TCP connect, not
|
||||
// the full SSH handshake. If sshd accepts the connection but stalls
|
||||
// during key exchange or auth, the process hangs indefinitely. Kill it
|
||||
// after 30s so the retry loop can continue.
|
||||
const timer = setTimeout(() => killWithTimeout(proc), 30_000);
|
||||
try {
|
||||
const [stdout, stderr] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
]);
|
||||
const exitCode = await proc.exited;
|
||||
|
||||
if (exitCode === 0 && stdout.includes("ok")) {
|
||||
logInfo("SSH is ready");
|
||||
return;
|
||||
}
|
||||
if (exitCode === 0 && stdout.includes("ok")) {
|
||||
logInfo("SSH is ready");
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the actual SSH error reason dimly so users can debug
|
||||
const reason = stderr.trim();
|
||||
if (reason) {
|
||||
logStep(`SSH handshake failed (${i}/${handshakeAttempts}): ${reason}`);
|
||||
} else {
|
||||
logStep(`SSH handshake failed (${i}/${handshakeAttempts})`);
|
||||
// Show the actual SSH error reason dimly so users can debug
|
||||
const reason = stderr.trim();
|
||||
if (reason) {
|
||||
logStep(`SSH handshake failed (${i}/${handshakeAttempts}): ${reason}`);
|
||||
} else {
|
||||
logStep(`SSH handshake failed (${i}/${handshakeAttempts})`);
|
||||
}
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
} catch {
|
||||
logStep(`SSH handshake error (${i}/${handshakeAttempts})`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue