fix(openclaw): batch config set calls into single exec (#3319)

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.
This commit is contained in:
Ahmed Abushagur 2026-04-18 00:56:37 -07:00 committed by GitHub
parent acd3e2339e
commit dc4fb59f67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 36 additions and 44 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openrouter/spawn",
"version": "1.0.15",
"version": "1.0.16",
"type": "module",
"bin": {
"spawn": "cli.js"

View file

@ -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)");
}
// 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",
];
// 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."
// 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.