From e33d40f0eaf2f262e68b1e3c103613bfca3f477b Mon Sep 17 00:00:00 2001 From: Ahmed Abushagur Date: Fri, 17 Apr 2026 14:12:43 -0700 Subject: [PATCH] fix(openclaw): batch config set calls into single exec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merges 4 separate runner.runServer() calls (model, sandbox, browser, channel stubs) into one exec with commands chained by `;`. On Sprite (container-exec, not persistent SSH), many sequential execs exhaust the connection and cause "connection closed" / "context deadline exceeded" on later steps like gateway startup. Before: 4 execs → 14 "Config overwrite" log lines → flaky connection After: 1 exec → same config result → stable connection for gateway Individual commands use `;` not `&&` so a failure in one (e.g. browser path not found) doesn't skip the rest — these are all non-fatal prefs. Bumps 1.0.15 -> 1.0.16. --- packages/cli/package.json | 2 +- packages/cli/src/shared/agent-setup.ts | 80 ++++++++++++-------------- 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 3102385fe..9d9978a71 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openrouter/spawn", - "version": "1.0.15", + "version": "1.0.16", "type": "module", "bin": { "spawn": "cli.js" diff --git a/packages/cli/src/shared/agent-setup.ts b/packages/cli/src/shared/agent-setup.ts index a0c8cf0b0..f2ebff21b 100644 --- a/packages/cli/src/shared/agent-setup.ts +++ b/packages/cli/src/shared/agent-setup.ts @@ -478,45 +478,31 @@ async function setupOpenclawConfig( await uploadConfigFile(runner, fallbackConfig, "$HOME/.openclaw/openclaw.json"); } - // Always set the model after onboard — `openclaw onboard` may pick its own - // default (e.g. arcee/trinity-large-thinking) instead of openrouter/auto. - const modelResult = await asyncTryCatchIf(isOperationalError, () => - runner.runServer( - "export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " + - `openclaw config set agents.defaults.model.primary ${shellQuote(modelId)} >/dev/null`, - ), - ); - if (!modelResult.ok) { - logWarn("Model config failed (non-fatal)"); - } - - // Disable Docker sandboxing — when Docker is installed on the VM, openclaw - // auto-detects it and runs agents inside containers, which hangs the session. - await asyncTryCatchIf(isOperationalError, () => - runner.runServer( - "export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " + - "openclaw config set agents.defaults.sandbox.mode off >/dev/null", - ), - ); - - // Configure browser via CLI (openclaw config set) — the supported way to set - // browser options. Redirect stdout to suppress doctor warnings on each call. - const browserResult = await asyncTryCatchIf(isOperationalError, () => - runner.runServer( - "export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " + - "openclaw config set browser.executablePath /usr/bin/google-chrome-stable >/dev/null; " + - "openclaw config set browser.noSandbox true >/dev/null; " + - "openclaw config set browser.headless true >/dev/null; " + - "openclaw config set browser.defaultProfile openclaw >/dev/null", - ), - ); - if (!browserResult.ok) { - logWarn("Browser config setup failed (non-fatal)"); - } - - // Write channel stubs so the dashboard renders channel cards properly, - // even when the user hasn't configured them yet. Without stubs the - // dashboard shows "Unsupported type: . Use Raw mode." + // Batch all `openclaw config set` calls into ONE exec to reduce Sprite + // connection overhead. Previously 4 separate exec calls, each triggering a + // "Config overwrite" log line from OpenClaw. On Sprite (container-exec, not + // persistent SSH), many sequential execs exhaust the connection and cause + // "connection closed" / "context deadline exceeded" on later steps. + // + // Each individual config set is chained with `;` (not `&&`) so a failure + // in one doesn't skip the rest — these are all non-fatal preferences. + const configCmds = [ + // Model — openclaw onboard writes arcee/trinity-large-thinking to the + // agent-specific config (agents.main.model.primary) which overrides + // the defaults path. Set BOTH so our model always wins. + `openclaw config set agents.defaults.model.primary ${shellQuote(modelId)} >/dev/null`, + `openclaw config set agents.main.model.primary ${shellQuote(modelId)} >/dev/null`, + // Disable Docker sandboxing — auto-detected Docker hangs the session + "openclaw config set agents.defaults.sandbox.mode off >/dev/null", + "openclaw config set agents.main.sandbox.mode off >/dev/null", + // Browser (requires Chrome installed above) + "openclaw config set browser.executablePath /usr/bin/google-chrome-stable >/dev/null", + "openclaw config set browser.noSandbox true >/dev/null", + "openclaw config set browser.headless true >/dev/null", + "openclaw config set browser.defaultProfile openclaw >/dev/null", + ]; + + // Channel stubs so the dashboard renders channel cards const channelNames = [ "telegram", "whatsapp", @@ -526,11 +512,17 @@ async function setupOpenclawConfig( "googlechat", "bluebubbles", ].filter((ch) => !enabledSteps || enabledSteps.has(ch)); - if (channelNames.length > 0) { - const stubCmds = channelNames.map((ch) => `openclaw config set channels.${ch}.enabled true >/dev/null`).join("; "); - await asyncTryCatchIf(isOperationalError, () => - runner.runServer("export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " + stubCmds), - ); + for (const ch of channelNames) { + configCmds.push(`openclaw config set channels.${ch}.enabled true >/dev/null`); + } + + const batchResult = await asyncTryCatchIf(isOperationalError, () => + runner.runServer( + "export PATH=$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$PATH; " + configCmds.join("; "), + ), + ); + if (!batchResult.ok) { + logWarn("Some config settings may have failed (non-fatal)"); } // Configure Telegram channel if a bot token was provided.