fix(desktop): use augmented PATH for readiness probes (#1613)

Signed-off-by: Ian Oberst <ioberst@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Ian Oberst
2026-07-09 17:55:29 -04:00
committed by GitHub
co-authored by Codex
parent e5d215e3c2
commit 94ec2c2bc6
4 changed files with 109 additions and 7 deletions
@@ -52,6 +52,8 @@ use crate::managed_agents::{
types::{AcpAvailabilityStatus, ManagedAgentRecord, PersonaRecord},
};
mod cli_probe;
// ── EffectiveAgentEnv ─────────────────────────────────────────────────────────
/// The resolved environment that a spawn of `record` would actually receive.
@@ -486,11 +488,12 @@ fn cli_login_requirements(
}];
};
let logged_in = std::process::Command::new(&binary_path)
.args(&probe_args[1..])
.output()
.map(|o| o.status.success())
.unwrap_or(false);
let augmented_path = cli_probe::augmented_path();
let logged_in = cli_probe::login_probe_succeeds(
&binary_path,
probe_args,
augmented_path.as_deref(),
);
if logged_in {
vec![]
@@ -0,0 +1,99 @@
use std::path::Path;
use crate::managed_agents::runtime::build_augmented_path;
pub(super) fn augmented_path() -> Option<String> {
build_augmented_path(
dirs::home_dir(),
std::env::current_exe()
.ok()
.and_then(|exe| exe.parent().map(std::path::Path::to_path_buf)),
crate::managed_agents::login_shell_path(),
)
}
pub(super) fn login_probe_succeeds(
binary_path: &Path,
probe_args: &[&str],
augmented_path: Option<&str>,
) -> bool {
// Run the probe at the resolved absolute path so the GUI-PATH gap is
// bypassed. Inject the same augmented PATH used for launched agents so
// script shims with `/usr/bin/env <interpreter>` shebangs can find runtimes
// such as node/python when the app was launched with a bare GUI PATH.
let mut command = std::process::Command::new(binary_path);
command.args(&probe_args[1..]);
if let Some(path) = augmented_path {
command.env("PATH", path);
}
command
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
#[cfg(unix)]
#[test]
fn login_probe_uses_augmented_path_for_env_shebang_interpreter() {
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::process::Command;
let temp = tempfile::tempdir().expect("temp dir");
let script_dir = temp.path().join("script-bin");
let interpreter_dir = temp.path().join("interpreter-bin");
let empty_path_dir = temp.path().join("empty-bin");
fs::create_dir_all(&script_dir).expect("script dir");
fs::create_dir_all(&interpreter_dir).expect("interpreter dir");
fs::create_dir_all(&empty_path_dir).expect("empty path dir");
let interpreter_path = interpreter_dir.join("node");
let marker_path = temp.path().join("fake-node-ran");
fs::write(
&interpreter_path,
format!(
"#!/bin/sh\nprintf 'fake node ran\\n' > '{}' || exit 1\nexit 0\n",
marker_path.display()
),
)
.expect("write interpreter");
fs::set_permissions(&interpreter_path, fs::Permissions::from_mode(0o755))
.expect("chmod interpreter");
let script_path = script_dir.join("fake-codex");
fs::write(&script_path, "#!/usr/bin/env node\n").expect("write script");
fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755)).expect("chmod script");
let scrubbed_path = std::env::join_paths([empty_path_dir.as_path()])
.expect("join scrubbed PATH")
.to_string_lossy()
.into_owned();
let without_augmented_path = Command::new(&script_path)
.args(["login", "status"])
.env("PATH", &scrubbed_path)
.output()
.expect("run script with scrubbed PATH");
assert!(
!without_augmented_path.status.success(),
"with a scrubbed PATH, /usr/bin/env should not find node"
);
let augmented_path =
std::env::join_paths([interpreter_dir.as_path()]).expect("join augmented PATH");
let augmented_path = augmented_path.to_string_lossy().into_owned();
assert!(
super::login_probe_succeeds(
&script_path,
&["fake-codex", "login", "status"],
Some(&augmented_path),
),
"the injected augmented PATH should allow /usr/bin/env to find the interpreter"
);
assert!(
marker_path.exists(),
"the fake node from the injected PATH should have run"
);
}
}
@@ -14,7 +14,7 @@ use crate::{
};
mod path;
use path::build_augmented_path;
pub(in crate::managed_agents) use path::build_augmented_path;
mod sweep;
pub(crate) use sweep::sweep_untracked_bundle_harnesses;
@@ -15,7 +15,7 @@ use std::path::PathBuf;
/// error), collapsing the entire augmented `PATH` to `None` — the bug this
/// guards against, which left managed agents unable to find `buzz`. Returns
/// `None` only when no entries exist.
pub(super) fn build_augmented_path(
pub(in crate::managed_agents) fn build_augmented_path(
home: Option<PathBuf>,
exe_parent: Option<PathBuf>,
shell_path: Option<String>,