From 23d7bd15892e527ee951b96262a79eefbdc584e8 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Sat, 6 Jun 2026 22:52:05 -0300 Subject: [PATCH] fix(cli): write OpenCode config to ~/.config on all platforms incl. Windows (#3330) (#3343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolveOpencodeConfigDir used %APPDATA% on Windows, but OpenCode reads its config from XDG ~/.config/opencode/ on every platform (on Windows: %USERPROFILE%\.config\opencode\, NOT %APPDATA%). So a Windows user who configured OpenCode via the dashboard had the file written where OpenCode never looks — it silently had no effect. Use the XDG path (XDG_CONFIG_HOME || ~/.config) unconditionally. Update the UI note + route JSDoc, and flip the three tests that encoded the old %APPDATA% behavior (t40 per-platform + card-note, cli-runtime-extended getCliConfigPaths). Co-authored-by: abdulkadirozyurt --- CHANGELOG.md | 1 + .../cli-tools/guide-settings/[toolId]/route.ts | 6 +++--- src/shared/constants/cliTools.ts | 2 +- src/shared/services/cliRuntime.ts | 13 ++++++------- tests/unit/cli-runtime-extended.test.ts | 7 +++---- .../t40-opencode-cli-tools-integration.test.ts | 17 +++++++++++++++-- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f4afc9697..2b46d9eace 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ _Development cycle in progress — entries are added as work merges into `releas ### 🔧 Bug Fixes +- **fix(cli):** write the OpenCode config to `~/.config/opencode/opencode.json` on **all** platforms — on Windows OmniRoute wrote to `%APPDATA%\opencode\` but OpenCode reads from `%USERPROFILE%\.config\opencode\` (XDG), so dashboard-saved config silently had no effect. (#3330 — thanks @abdulkadirozyurt) - **fix(catalog):** remove `minimaxai/minimax-m3` from the **NVIDIA NIM** tier — NVIDIA does not host it yet, so every request 404'd (`404 page not found`), while sibling `minimax-m2.7` on the same provider works. MiniMax M3 stays available on the tiers that actually serve it. (#3329 — thanks @mikmaneggahommie) - **fix(electron):** ship `loginManager.js` in the packaged app — #3292 added it (and a `require("./loginManager")` in `main.js`) without adding it to electron-builder's `build.files`, so the packaged app crashed at startup with "Cannot find module" on the Linux/macOS smoke tests. Plus a regression test asserting every local `require("./x")` in the Electron entry points is shipped. ([#3334](https://github.com/diegosouzapw/OmniRoute/pull/3334) — thanks @diegosouzapw) - **fix(startup):** correct the #3292 auto-refresh daemon import (`@/open-sse/...` → `@omniroute/open-sse/services/autoRefreshDaemon`); the `@/` alias maps to `src/`, so the daemon silently never ran in the built standalone (non-fatal "Cannot find module", caught at runtime). Adds a regression test banning `@/open-sse/*` imports in `src/`. ([#3335](https://github.com/diegosouzapw/OmniRoute/pull/3335) — thanks @diegosouzapw) diff --git a/src/app/api/cli-tools/guide-settings/[toolId]/route.ts b/src/app/api/cli-tools/guide-settings/[toolId]/route.ts index cfc3e16db5..ac8fd1a84a 100644 --- a/src/app/api/cli-tools/guide-settings/[toolId]/route.ts +++ b/src/app/api/cli-tools/guide-settings/[toolId]/route.ts @@ -159,9 +159,9 @@ async function saveContinueConfig({ baseUrl, apiKey, model }) { } /** - * Save OpenCode config to: - * - Linux/macOS: ~/.config/opencode/opencode.json (XDG_CONFIG_HOME aware) - * - Windows: %APPDATA%/opencode/opencode.json + * Save OpenCode config to ~/.config/opencode/opencode.json on ALL platforms + * (XDG_CONFIG_HOME aware). OpenCode uses XDG `~/.config` even on Windows + * (%USERPROFILE%\.config), NOT %APPDATA% (#3330). * * (#524) OpenCode was silently failing because this handler was missing. */ diff --git a/src/shared/constants/cliTools.ts b/src/shared/constants/cliTools.ts index feb478ecd4..f039bc4083 100644 --- a/src/shared/constants/cliTools.ts +++ b/src/shared/constants/cliTools.ts @@ -273,7 +273,7 @@ export const CLI_TOOLS: Record = { notes: [ { type: "warning", - text: "Config path: Linux/macOS ~/.config/opencode/opencode.json • Windows %APPDATA%\\\\opencode\\\\opencode.json", + text: "Config path: ~/.config/opencode/opencode.json on all platforms (Windows: %USERPROFILE%\\\\.config\\\\opencode\\\\opencode.json)", }, { type: "warning", diff --git a/src/shared/services/cliRuntime.ts b/src/shared/services/cliRuntime.ts index 9ca853eaa7..2041071bc6 100644 --- a/src/shared/services/cliRuntime.ts +++ b/src/shared/services/cliRuntime.ts @@ -932,16 +932,15 @@ export const getCliConfigHome = () => { }; export const resolveOpencodeConfigDir = ( - platform = process.platform, + _platform = process.platform, env: NodeJS.ProcessEnv = process.env, homeDir = os.homedir() ) => { - const isWin = platform === "win32"; - if (isWin) { - const appData = String(env.APPDATA || "").trim(); - return appData || path.join(homeDir, "AppData", "Roaming"); - } - + // #3330: OpenCode reads its config from XDG `~/.config/opencode/` on ALL + // platforms — including Windows, where it uses `%USERPROFILE%\.config`, NOT + // `%APPDATA%`. Writing to %APPDATA% on Windows put the file where OpenCode + // never looks, so dashboard-saved config silently had no effect. `_platform` + // is kept in the signature for call-site/test compatibility. const xdgConfigHome = String(env.XDG_CONFIG_HOME || "").trim(); return xdgConfigHome || path.join(homeDir, ".config"); }; diff --git a/tests/unit/cli-runtime-extended.test.ts b/tests/unit/cli-runtime-extended.test.ts index cceca1a3aa..d81af103a0 100644 --- a/tests/unit/cli-runtime-extended.test.ts +++ b/tests/unit/cli-runtime-extended.test.ts @@ -85,10 +85,9 @@ test("CLI config helpers enforce safe config homes and expose per-tool config pa assert.equal(cliRuntime.getCliConfigPaths("unknown"), null); process.env.XDG_CONFIG_HOME = path.join(homeDir, ".config-test"); - const expectedOpencodeRoot = - process.platform === "win32" - ? process.env.APPDATA || path.join(homeDir, "AppData", "Roaming") - : process.env.XDG_CONFIG_HOME; + // #3330: OpenCode uses XDG (`~/.config` / $XDG_CONFIG_HOME) on every platform, + // including Windows — no %APPDATA% special-case. + const expectedOpencodeRoot = process.env.XDG_CONFIG_HOME; assert.deepEqual(cliRuntime.getCliConfigPaths("opencode"), { config: path.join(expectedOpencodeRoot, "opencode", "opencode.json"), }); diff --git a/tests/unit/t40-opencode-cli-tools-integration.test.ts b/tests/unit/t40-opencode-cli-tools-integration.test.ts index fbaf05db0a..580b08f5ee 100644 --- a/tests/unit/t40-opencode-cli-tools-integration.test.ts +++ b/tests/unit/t40-opencode-cli-tools-integration.test.ts @@ -21,7 +21,10 @@ test("T40: OpenCode card documents config paths and --variant usage", () => { .toLowerCase(); assert.match(notesText, /\.config\/opencode\/opencode\.json/); - assert.match(notesText, /%appdata%/); + // #3330: OpenCode uses ~/.config on all platforms (incl. Windows) — the note + // must no longer point Windows users at %APPDATA%. + assert.doesNotMatch(notesText, /%appdata%/); + assert.match(notesText, /%userprofile%/); assert.match(notesText, /--variant/); }); @@ -36,6 +39,8 @@ test("T40: OpenCode config path resolves per-platform", () => { const linuxDefault = resolveOpencodeConfigPath("linux", {}, "/home/dev"); assert.equal(linuxDefault, path.join("/home/dev", ".config", "opencode", "opencode.json")); + // #3330: OpenCode uses XDG `~/.config/opencode/` on ALL platforms including + // Windows (NOT %APPDATA%) — OmniRoute must write where OpenCode reads. const windowsPath = resolveOpencodeConfigPath( "win32", { APPDATA: "C:\\Users\\dev\\AppData\\Roaming" }, @@ -43,8 +48,16 @@ test("T40: OpenCode config path resolves per-platform", () => { ); assert.equal( windowsPath, - path.join("C:\\Users\\dev\\AppData\\Roaming", "opencode", "opencode.json") + path.join("C:\\Users\\dev", ".config", "opencode", "opencode.json") ); + + // Windows still honors XDG_CONFIG_HOME when set. + const windowsXdg = resolveOpencodeConfigPath( + "win32", + { XDG_CONFIG_HOME: "D:\\xdg" }, + "C:\\Users\\dev" + ); + assert.equal(windowsXdg, path.join("D:\\xdg", "opencode", "opencode.json")); }); test("T40: OpenCode config generator includes endpoint and selected API key", () => {