From 2544ee949834eaaadd114b7372a7948db107ac39 Mon Sep 17 00:00:00 2001 From: Zius <64656661+ziuus@users.noreply.github.com> Date: Mon, 24 Aug 2026 20:50:02 +0530 Subject: [PATCH] feat: enable Linux PATH inheritance for autostart & extend loginShellPath to Linux (#11372) Merged via consolidated batch validation. Fixes autostart on Linux failing to inherit the user's shell PATH (CLI-dependent features like Kiro's Google OAuth broke). Resolved a conflict against a batch sibling in bin/cli/commands/doctor.mjs (kept the more complete prebuilds-aware candidate list) and setup-claude.mjs (formatting only). Own test (login-shell-path-3321.test.ts, 10/10) passes + typecheck:core clean. Thanks! --- bin/cli/commands/setup-claude.mjs | 3 ++- bin/cli/tray/autostart.mjs | 6 +++++- src/shared/services/loginShellPath.ts | 4 ++-- tests/unit/login-shell-path-3321.test.ts | 18 +++++++++++++++--- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/bin/cli/commands/setup-claude.mjs b/bin/cli/commands/setup-claude.mjs index 6567824490..d6c8fad593 100644 --- a/bin/cli/commands/setup-claude.mjs +++ b/bin/cli/commands/setup-claude.mjs @@ -169,7 +169,8 @@ export async function runSetupClaudeCommand(opts = {}) { let detail = `HTTP ${res.status}`; try { const errorBody = await res.json(); - const serverMsg = errorBody?.error?.message || errorBody?.error || errorBody?.message || ""; + const serverMsg = + errorBody?.error?.message || errorBody?.error || errorBody?.message || ""; if (serverMsg) detail += ` — ${serverMsg}`; } catch {} throw new Error(detail); diff --git a/bin/cli/tray/autostart.mjs b/bin/cli/tray/autostart.mjs index 3462c2711f..f554f4554c 100644 --- a/bin/cli/tray/autostart.mjs +++ b/bin/cli/tray/autostart.mjs @@ -114,10 +114,13 @@ function writeLinuxSystemdUnit(cliPath) { const unitDir = dirname(linuxSystemdUnitPath()); mkdirSync(unitDir, { recursive: true }); const envFile = join(userHomeDir(), ".omniroute", ".env"); + const nodeBinDir = dirname(process.execPath); + const userLocalBin = join(userHomeDir(), ".local", "bin"); + const pathEnv = `${nodeBinDir}:${userLocalBin}:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin`; const lines = [ "[Unit]", "Description=OmniRoute AI proxy router", - "After=network-online.target", + "After=network-online.target graphical-session.target", "Wants=network-online.target", "", "[Service]", @@ -134,6 +137,7 @@ function writeLinuxSystemdUnit(cliPath) { `ExecStart=${buildServeExecLine(cliPath, { tray: false })}`, "Restart=on-failure", "RestartSec=5", + `Environment="PATH=${pathEnv}"`, ]; if (existsSync(envFile)) lines.push(`EnvironmentFile=-${envFile}`); lines.push("", "[Install]", "WantedBy=default.target", ""); diff --git a/src/shared/services/loginShellPath.ts b/src/shared/services/loginShellPath.ts index 737ae49dea..c8d81d634d 100644 --- a/src/shared/services/loginShellPath.ts +++ b/src/shared/services/loginShellPath.ts @@ -59,8 +59,8 @@ export interface LoginShellPathOptions { */ export function getLoginShellPath(opts: LoginShellPathOptions = {}): string | null { const platform = opts.platform ?? process.platform; - if (platform !== "darwin") return null; - const shell = opts.shell || process.env.SHELL || "/bin/zsh"; + if (platform !== "darwin" && platform !== "linux") return null; + const shell = opts.shell || process.env.SHELL || (platform === "darwin" ? "/bin/zsh" : "/bin/bash"); if (!/^[\w./-]+$/.test(shell)) return null; const run = opts.runShell || diff --git a/tests/unit/login-shell-path-3321.test.ts b/tests/unit/login-shell-path-3321.test.ts index 28edb45101..c3a6422315 100644 --- a/tests/unit/login-shell-path-3321.test.ts +++ b/tests/unit/login-shell-path-3321.test.ts @@ -43,17 +43,29 @@ test("parseShellPathOutput returns null when no PATH line is present", () => { assert.equal(parseShellPathOutput(""), null); }); -test("getLoginShellPath returns null on non-darwin platforms (no-op on Linux/Windows)", () => { +test("getLoginShellPath returns null on win32 platform (no-op on Windows)", () => { let called = false; const result = getLoginShellPath({ - platform: "linux", + platform: "win32", runShell: () => { called = true; return "PATH=/should/not/be/used"; }, }); assert.equal(result, null); - assert.equal(called, false, "must not spawn the shell on non-darwin"); + assert.equal(called, false, "must not spawn the shell on win32"); +}); + +test("getLoginShellPath returns the login-shell PATH on linux", () => { + const result = getLoginShellPath({ + platform: "linux", + shell: "/bin/bash", + runShell: (sh) => { + assert.equal(sh, "/bin/bash"); + return "PATH=/home/user/.nvm/versions/node/v22.23.1/bin:/usr/local/bin:/usr/bin\n"; + }, + }); + assert.equal(result, "/home/user/.nvm/versions/node/v22.23.1/bin:/usr/local/bin:/usr/bin"); }); test("getLoginShellPath returns the login-shell PATH on darwin (#3321)", () => {