From 3feacad69bc3e91c0c8bf8c566713aa1da4ccad5 Mon Sep 17 00:00:00 2001 From: bashrusakh Date: Wed, 19 Aug 2026 10:38:53 +1100 Subject: [PATCH] fix(server): keep env-provided OPENCODE_BINARY when settings clear the override --- CHANGELOG.md | 1 + .../web/server/lib/opencode/env-runtime.js | 8 ++++- .../server/lib/opencode/env-runtime.test.js | 33 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c9a9353..3958c0c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ All notable changes to this project will be documented in this file. - Desktop: browser pages served from a self-signed loopback HTTPS address now load instead of being blocked by the certificate warning. - Browser: typing a comment on a page no longer triggers app shortcuts. - Skills Catalog: the source is now named ClawHub instead of "ClawdHub" (thanks to @makeittech). +- Settings: an explicitly set `OPENCODE_BINARY` environment variable is no longer discarded when settings contain an empty opencodeBinary value; the environment variable keeps pointing the managed OpenCode server at the binary you chose. ## [1.18.4] - 2026-08-14 diff --git a/packages/web/server/lib/opencode/env-runtime.js b/packages/web/server/lib/opencode/env-runtime.js index 23950d63..003c97f4 100644 --- a/packages/web/server/lib/opencode/env-runtime.js +++ b/packages/web/server/lib/opencode/env-runtime.js @@ -1011,7 +1011,13 @@ export const createOpenCodeEnvRuntime = (deps) => { const normalized = normalizeOpencodeBinarySetting(settings.opencodeBinary); if (normalized === '') { - delete process.env.OPENCODE_BINARY; + // The empty-string sentinel drops a previously APPLIED settings + // override (source === 'settings'). An OPENCODE_BINARY provided by + // the user's own environment is explicit configuration and must not + // be destroyed by an empty setting. + if (state.resolvedOpencodeBinarySource === 'settings') { + delete process.env.OPENCODE_BINARY; + } state.resolvedOpencodeBinary = null; state.resolvedOpencodeBinarySource = null; clearWslOpencodeResolution(); diff --git a/packages/web/server/lib/opencode/env-runtime.test.js b/packages/web/server/lib/opencode/env-runtime.test.js index 7f51f40f..d02529a4 100644 --- a/packages/web/server/lib/opencode/env-runtime.test.js +++ b/packages/web/server/lib/opencode/env-runtime.test.js @@ -200,6 +200,39 @@ describe('OpenCode env runtime', () => { expect(state.resolvedOpencodeBinarySource).toBe('settings'); }); + it('keeps an env-provided OPENCODE_BINARY when the setting is an empty-string sentinel', async () => { + const dir = createTempDir('openchamber-env-opencode-'); + const binary = path.join(dir, 'opencode'); + fs.writeFileSync(binary, '#!/bin/sh\nexit 0\n'); + if (process.platform !== 'win32') fs.chmodSync(binary, 0o755); + process.env.OPENCODE_BINARY = binary; + const { runtime, state } = createRuntime({ opencodeBinary: '' }); + + await expect(runtime.applyOpencodeBinaryFromSettings()).resolves.toBeNull(); + expect(process.env.OPENCODE_BINARY).toBe(binary); + expect(state.resolvedOpencodeBinary).toBeNull(); + expect(state.resolvedOpencodeBinarySource).toBeNull(); + }); + + it('drops a previously applied settings override when the setting is cleared to an empty string', async () => { + const dir = createTempDir('openchamber-settings-opencode-'); + const binary = path.join(dir, 'opencode'); + fs.writeFileSync(binary, '#!/bin/sh\nexit 0\n'); + if (process.platform !== 'win32') fs.chmodSync(binary, 0o755); + const settings = { opencodeBinary: binary }; + const { runtime, state } = createRuntime(settings); + + await expect(runtime.applyOpencodeBinaryFromSettings()).resolves.toBe(binary); + expect(process.env.OPENCODE_BINARY).toBe(binary); + expect(state.resolvedOpencodeBinarySource).toBe('settings'); + + settings.opencodeBinary = ''; + await expect(runtime.applyOpencodeBinaryFromSettings()).resolves.toBeNull(); + expect(process.env.OPENCODE_BINARY).toBeUndefined(); + expect(state.resolvedOpencodeBinary).toBeNull(); + expect(state.resolvedOpencodeBinarySource).toBeNull(); + }); + it('prefers the bundled CLI over a user-installed OpenCode from PATH', () => { const bundledDir = createTempDir('openchamber-bundled-opencode-'); const bundledBinary = path.join(bundledDir, process.platform === 'win32' ? 'opencode.exe' : 'opencode');