From 57f2c6485700e78d91a106b73123de4c96e736b1 Mon Sep 17 00:00:00 2001 From: latenighthackathon Date: Sun, 29 Mar 2026 14:52:25 -0500 Subject: [PATCH 1/2] fix(onboard): add timeout to spawnSync calls to prevent hung processes All spawnSync calls in the onboarding wizard lacked a timeout option, meaning a hung curl or stalled download would freeze the entire wizard with no recovery path. Add process-level timeouts as a safety net: - 30s for curl endpoint probes (10s buffer over curl --max-time 20) - 10min for ollama model downloads - 5min for install-openshell.sh execution Closes #1017 --- bin/lib/onboard.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bin/lib/onboard.js b/bin/lib/onboard.js index 1480f435d..699e0ac87 100644 --- a/bin/lib/onboard.js +++ b/bin/lib/onboard.js @@ -678,6 +678,7 @@ function probeOpenAiLikeEndpoint(endpointUrl, model, apiKey) { const result = spawnSync("bash", ["-c", cmd], { cwd: ROOT, encoding: "utf8", + timeout: 30_000, env: { ...process.env, NEMOCLAW_PROBE_API_KEY: apiKey, @@ -727,6 +728,7 @@ function probeAnthropicEndpoint(endpointUrl, model, apiKey) { const result = spawnSync("bash", ["-c", cmd], { cwd: ROOT, encoding: "utf8", + timeout: 30_000, env: { ...process.env, NEMOCLAW_PROBE_API_KEY: apiKey, @@ -867,6 +869,7 @@ function fetchNvidiaEndpointModels(apiKey) { const result = spawnSync("bash", ["-c", cmd], { cwd: ROOT, encoding: "utf8", + timeout: 30_000, env: { ...process.env, NEMOCLAW_PROBE_API_KEY: apiKey, @@ -920,6 +923,7 @@ function fetchOpenAiLikeModels(endpointUrl, apiKey) { const result = spawnSync("bash", ["-c", cmd], { cwd: ROOT, encoding: "utf8", + timeout: 30_000, env: { ...process.env, NEMOCLAW_PROBE_API_KEY: apiKey, @@ -957,6 +961,7 @@ function fetchAnthropicModels(endpointUrl, apiKey) { const result = spawnSync("bash", ["-c", cmd], { cwd: ROOT, encoding: "utf8", + timeout: 30_000, env: { ...process.env, NEMOCLAW_PROBE_API_KEY: apiKey, @@ -1223,6 +1228,7 @@ function pullOllamaModel(model) { cwd: ROOT, encoding: "utf8", stdio: "inherit", + timeout: 600_000, env: { ...process.env }, }); return result.status === 0; @@ -1358,6 +1364,7 @@ function installOpenshell() { env: process.env, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8", + timeout: 300_000, }); if (result.status !== 0) { const output = `${result.stdout || ""}${result.stderr || ""}`.trim(); From f3460d7530499ba60dd71154424468a3dc3b4009 Mon Sep 17 00:00:00 2001 From: latenighthackathon Date: Sun, 29 Mar 2026 15:05:15 -0500 Subject: [PATCH 2/2] fix(onboard): distinguish timeout from other failures in ollama pull When spawnSync kills the process due to timeout, result.signal is SIGTERM. Surface a specific error message so users know the 10-minute limit was hit, rather than seeing the generic pull failure message. Addresses CodeRabbit review nitpick. --- bin/lib/onboard.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bin/lib/onboard.js b/bin/lib/onboard.js index 699e0ac87..525d129f3 100644 --- a/bin/lib/onboard.js +++ b/bin/lib/onboard.js @@ -1231,6 +1231,10 @@ function pullOllamaModel(model) { timeout: 600_000, env: { ...process.env }, }); + if (result.signal === "SIGTERM") { + console.error(` Model pull timed out after 10 minutes. Try a smaller model or check your network connection.`); + return false; + } return result.status === 0; }