From 343e0f3339f163582552aaac7666a56b4cbc1d38 Mon Sep 17 00:00:00 2001 From: Aayush Prajapati Date: Sat, 14 Feb 2026 12:30:51 +0530 Subject: [PATCH] bug: Root cause: On macOS, GUI applications don't inherit shell profile modifications (.zshrc, .bashrc). When OpenWork spawns the opencode engine, the PATH doesn't include paths like /opt/homebrew/bin where Homebrew-installed tools like npx reside. The fix (packages/desktop/src-tauri/src/paths.rs): Added a common_tool_paths() function that returns typical locations where user-installed tools are found: - macOS: /opt/homebrew/bin, /usr/local/bin, ~/.nvm/current/bin, ~/.volta/bin, ~/.bun/bin, ~/.cargo/bin, etc. - Linux: Similar paths adapted for Linux conventions - Windows: volta, pnpm, cargo, npm global paths These paths are now prepended to the PATH environment variable when spawning processes. To test the fix: Build and run the app, then try enabling your Playwright MCP server - it should now find npx correctly without needing to specify the full path. --- package-lock.json | 12 ++++ packages/desktop/src-tauri/src/paths.rs | 96 ++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..75b7e570 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "@different-ai/openwork-workspace", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@different-ai/openwork-workspace", + "version": "0.0.0" + } + } +} diff --git a/packages/desktop/src-tauri/src/paths.rs b/packages/desktop/src-tauri/src/paths.rs index 43a51a4e..a00fb7c6 100644 --- a/packages/desktop/src-tauri/src/paths.rs +++ b/packages/desktop/src-tauri/src/paths.rs @@ -125,6 +125,89 @@ pub fn sidecar_path_candidates( unique } +/// Returns common paths where user-installed tools are typically located. +/// On macOS, GUI apps don't inherit shell profile modifications (.zshrc, .bashrc), +/// so tools installed via Homebrew, nvm, volta, etc. won't be found unless we +/// explicitly include these common locations. +fn common_tool_paths() -> Vec { + let mut paths = Vec::new(); + + #[cfg(target_os = "macos")] + { + // Homebrew paths (Apple Silicon and Intel) + paths.push(PathBuf::from("/opt/homebrew/bin")); + paths.push(PathBuf::from("/opt/homebrew/sbin")); + paths.push(PathBuf::from("/usr/local/bin")); + paths.push(PathBuf::from("/usr/local/sbin")); + + // User-specific paths for common version managers + if let Some(home) = home_dir() { + // nvm, fnm (Node version managers) + paths.push(home.join(".nvm/current/bin")); + paths.push(home.join(".fnm/current/bin")); + // volta + paths.push(home.join(".volta/bin")); + // pnpm + paths.push(home.join("Library/pnpm")); + // bun + paths.push(home.join(".bun/bin")); + // cargo/rustup + paths.push(home.join(".cargo/bin")); + // pyenv + paths.push(home.join(".pyenv/shims")); + // local bin + paths.push(home.join(".local/bin")); + } + } + + #[cfg(target_os = "linux")] + { + paths.push(PathBuf::from("/usr/local/bin")); + paths.push(PathBuf::from("/usr/local/sbin")); + + if let Some(home) = home_dir() { + // nvm, fnm + paths.push(home.join(".nvm/current/bin")); + paths.push(home.join(".fnm/current/bin")); + // volta + paths.push(home.join(".volta/bin")); + // pnpm + paths.push(home.join(".local/share/pnpm")); + // bun + paths.push(home.join(".bun/bin")); + // cargo + paths.push(home.join(".cargo/bin")); + // pyenv + paths.push(home.join(".pyenv/shims")); + // local bin + paths.push(home.join(".local/bin")); + } + } + + #[cfg(windows)] + { + if let Some(home) = home_dir() { + // volta + paths.push(home.join(".volta/bin")); + // pnpm + if let Some(local) = env::var_os("LOCALAPPDATA") { + paths.push(PathBuf::from(local).join("pnpm")); + } + // bun + paths.push(home.join(".bun/bin")); + // cargo + paths.push(home.join(".cargo/bin")); + } + + // npm global + if let Some(app_data) = env::var_os("APPDATA") { + paths.push(PathBuf::from(app_data).join("npm")); + } + } + + paths +} + pub fn prepended_path_env(prefixes: &[PathBuf]) -> Option { let mut entries = Vec::::new(); @@ -134,8 +217,19 @@ pub fn prepended_path_env(prefixes: &[PathBuf]) -> Option { } } + // Add common tool paths that may not be in the GUI app's inherited PATH + for path in common_tool_paths() { + if path.is_dir() && !entries.contains(&path) { + entries.push(path); + } + } + if let Some(existing) = env::var_os("PATH") { - entries.extend(env::split_paths(&existing)); + for path in env::split_paths(&existing) { + if !entries.contains(&path) { + entries.push(path); + } + } } if entries.is_empty() {