From 3058e82da1d7696ff0f8f79b413180ee979bd9fc Mon Sep 17 00:00:00 2001 From: Dusty Date: Thu, 29 Oct 2026 01:55:24 -0700 Subject: [PATCH] fix(desktop): start the app from Finder, with the installation's Bun on its PATH The app runs as a package script (`bun run serve`), and that script runs `bun` again by name. A desktop app opened from Finder or the Dock has the system's minimal PATH, which has no `bun`, so the app exited with status 127 at Start ("bun: command not found"). The server and worker run Bun on a file and were unaffected. Every host process now gets the directory of the Bun this installation downloaded first on its PATH, with the rest of PATH kept after it (`stack::bun_command`). --- CHANGELOG.md | 7 +++++ desktop/src-tauri/src/engine.rs | 2 +- desktop/src-tauri/src/stack.rs | 46 ++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f36937ac2..f2ce3aeda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,13 @@ The newest come first, and a press on When turns the order round. A search box a Started by buttons narrow the events already loaded, and the count says how many of them match. A row about a Bot (a message routed to it, say) now names it under Bot. +### The desktop app starts OpenBot when it is opened from Finder + +Opened from Finder or the Dock, the desktop app stopped at Start with "OpenBot did not start", and +the app's log said `bun: command not found`. The app part of OpenBot runs as a package script that +starts `bun` again by name, and an app opened that way has no `bun` on its `PATH`. The desktop now +hands every process it starts the directory of the Bun it installed. + ### The desktop setup page does not freeze when it loads during an install If the setup page loaded again while the desktop app downloaded local software, the window showed a diff --git a/desktop/src-tauri/src/engine.rs b/desktop/src-tauri/src/engine.rs index 2108ce191..43dba263e 100644 --- a/desktop/src-tauri/src/engine.rs +++ b/desktop/src-tauri/src/engine.rs @@ -376,7 +376,7 @@ fn command_at(engine: Engine, binary: &Path) -> Command { /// /// In front, so the provider OpenBot placed is the one found; appended, a broken `docker-compose` /// earlier on PATH would still win. -fn path_with(first: &Path) -> OsString { +pub(crate) fn path_with(first: &Path) -> OsString { let mut joined = OsString::from(first); if let Some(existing) = std::env::var_os("PATH") { if !existing.is_empty() { diff --git a/desktop/src-tauri/src/stack.rs b/desktop/src-tauri/src/stack.rs index 038a0a338..9639722fa 100644 --- a/desktop/src-tauri/src/stack.rs +++ b/desktop/src-tauri/src/stack.rs @@ -1603,7 +1603,7 @@ pub fn spawn_host_process( let out = std::fs::File::create(logs.join(format!("{}.log", process.name)))?; let err = out.try_clone()?; - let mut command = command(bun); + let mut command = bun_command(bun); command.current_dir(root.join(process.cwd)); let ports = crate::env::Ports::read(root)?; if process.name == LOCAL_BROWSER { @@ -1638,6 +1638,23 @@ pub fn spawn_host_process( command.spawn() } +/// The installation's Bun, with its own directory first on `PATH`. +/// +/// The app runs as a package script (`serve`), and that script runs `bun` again by name. A desktop +/// app opened from Finder has the system's minimal `PATH`, where there is no `bun`, so the app +/// exited with status 127 the moment it started. First, so the Bun this installation downloaded is +/// the one found, not an older one somebody has on `PATH`. +fn bun_command(bun: &Path) -> Command { + let mut command = command(bun); + if let Some(directory) = bun + .parent() + .filter(|directory| !directory.as_os_str().is_empty()) + { + command.env("PATH", crate::engine::path_with(directory)); + } + command +} + /// The person's browser is started the way `scripts/start-local-chrome-computer.ts` starts it, and /// is handed only what it needs (see `local_browser::environment`). /// @@ -3660,6 +3677,33 @@ fn dirs_home() -> PathBuf { #[cfg(test)] mod tests { use super::*; + + /// The app's `serve` script runs `bun` by name. Opened from Finder, the desktop has no `bun` on + /// `PATH`, so a host process has to be handed the directory of the Bun that started it. + #[test] + fn a_host_process_finds_the_installations_own_bun_by_name() { + let bun = Path::new("/installation/openbot-engine/bun-1.4.3/bun"); + let command = bun_command(bun); + let path = command + .get_envs() + .find(|(name, _)| *name == "PATH") + .and_then(|(_, value)| value) + .expect("a host process is given a PATH"); + let mut entries = std::env::split_paths(path); + assert_eq!( + entries.next().as_deref(), + Some(Path::new("/installation/openbot-engine/bun-1.4.3")), + "the installation's Bun comes first" + ); + let inherited: Vec<_> = std::env::var_os("PATH") + .map(|path| std::env::split_paths(&path).collect()) + .unwrap_or_default(); + assert_eq!( + entries.collect::>(), + inherited, + "and the rest of PATH is kept" + ); + } use crate::test_support::temp_root; #[test]