From 4c767df87007679233e0450eaea732d7a8d94bd5 Mon Sep 17 00:00:00 2001 From: David Wiesen Date: Fri, 22 May 2026 09:57:40 -0700 Subject: [PATCH] windows-sandbox: materialize setup helper launches --- .../src/helper_materialization.rs | 67 ++++++++++--------- .../src/setup_orchestrator.rs | 47 +++++-------- 2 files changed, 54 insertions(+), 60 deletions(-) diff --git a/codex-rs/windows-sandbox-rs/src/helper_materialization.rs b/codex-rs/windows-sandbox-rs/src/helper_materialization.rs index b7d00e1dd9..2853829056 100644 --- a/codex-rs/windows-sandbox-rs/src/helper_materialization.rs +++ b/codex-rs/windows-sandbox-rs/src/helper_materialization.rs @@ -1,6 +1,6 @@ -use anyhow::anyhow; use anyhow::Context; use anyhow::Result; +use anyhow::anyhow; use std::collections::HashMap; use std::fs; use std::io::Write; @@ -20,18 +20,21 @@ const RESOURCES_DIRNAME: &str = "codex-resources"; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub(crate) enum HelperExecutable { CommandRunner, + Setup, } impl HelperExecutable { fn file_name(self) -> &'static str { match self { Self::CommandRunner => "codex-command-runner.exe", + Self::Setup => "codex-windows-sandbox-setup.exe", } } fn label(self) -> &'static str { match self { Self::CommandRunner => "command-runner", + Self::Setup => "setup-helper", } } } @@ -89,10 +92,7 @@ pub(crate) fn resolve_helper_for_launch( } } -pub fn resolve_current_exe_for_launch( - codex_home: &Path, - fallback_executable: &str, -) -> PathBuf { +pub fn resolve_current_exe_for_launch(codex_home: &Path, fallback_executable: &str) -> PathBuf { let source = match std::env::current_exe() { Ok(path) => path, Err(_) => return PathBuf::from(fallback_executable), @@ -242,11 +242,7 @@ fn dev_build_suffix(source: &Path) -> Result { let duration = modified .duration_since(UNIX_EPOCH) .with_context(|| format!("convert helper source mtime {}", source.display()))?; - Ok(format!( - "{}-{:x}", - metadata.len(), - duration.as_secs(), - )) + Ok(format!("{}-{:x}", metadata.len(), duration.as_secs(),)) } fn copy_from_source_if_needed(source: &Path, destination: &Path) -> Result { @@ -254,9 +250,12 @@ fn copy_from_source_if_needed(source: &Path, destination: &Path) -> Result Result { Ok(meta) => meta, Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(false), Err(err) => { - return Err(err) - .with_context(|| format!("read helper destination metadata {}", destination.display())); + return Err(err).with_context(|| { + format!("read helper destination metadata {}", destination.display()) + }); } }; @@ -348,16 +348,16 @@ fn destination_is_fresh(source: &Path, destination: &Path) -> Result { #[cfg(test)] mod tests { - use super::copy_from_source_if_needed; use super::CopyOutcome; - use super::dev_build_suffix; + use super::DEV_BUILD_VERSION_SENTINEL; + use super::HelperExecutable; + use super::RESOURCES_DIRNAME; + use super::copy_from_source_if_needed; use super::destination_is_fresh; + use super::dev_build_suffix; use super::helper_bin_dir; use super::helper_version_suffix; use super::materialized_file_name; - use super::HelperExecutable; - use super::DEV_BUILD_VERSION_SENTINEL; - use super::RESOURCES_DIRNAME; use super::source_path_for_exe; use pretty_assertions::assert_eq; use std::fs; @@ -376,7 +376,10 @@ mod tests { let outcome = copy_from_source_if_needed(&source, &destination).expect("copy helper"); assert_eq!(CopyOutcome::ReCopied, outcome); - assert_eq!(b"runner-v1".as_slice(), fs::read(&destination).expect("read destination")); + assert_eq!( + b"runner-v1".as_slice(), + fs::read(&destination).expect("read destination") + ); } #[test] @@ -403,11 +406,13 @@ mod tests { fs::write(&source, b"runner-v1").expect("write source"); copy_from_source_if_needed(&source, &destination).expect("initial copy"); - let outcome = - copy_from_source_if_needed(&source, &destination).expect("revalidate helper"); + let outcome = copy_from_source_if_needed(&source, &destination).expect("revalidate helper"); assert_eq!(CopyOutcome::Reused, outcome); - assert_eq!(b"runner-v1".as_slice(), fs::read(&destination).expect("read destination")); + assert_eq!( + b"runner-v1".as_slice(), + fs::read(&destination).expect("read destination") + ); } #[test] @@ -429,8 +434,10 @@ mod tests { let runner_source = source_dir.join("codex-command-runner.exe"); fs::write(&runner_source, b"runner").expect("runner"); let runner_suffix = helper_version_suffix(&runner_source).expect("runner suffix"); - let runner_destination = helper_bin_dir(&codex_home) - .join(materialized_file_name(HelperExecutable::CommandRunner, &runner_suffix)); + let runner_destination = helper_bin_dir(&codex_home).join(materialized_file_name( + HelperExecutable::CommandRunner, + &runner_suffix, + )); let runner_outcome = copy_from_source_if_needed(&runner_source, &runner_destination).expect("runner copy"); @@ -453,8 +460,8 @@ mod tests { fs::write(&exe, b"codex").expect("write exe"); fs::write(&helper, b"runner").expect("write helper"); - let resolved = - source_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe").expect("helper path"); + let resolved = source_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe") + .expect("helper path"); assert_eq!(resolved, helper); } @@ -472,8 +479,8 @@ mod tests { fs::write(&sibling_helper, b"sibling runner").expect("write sibling helper"); fs::write(&resource_helper, b"resource runner").expect("write resource helper"); - let resolved = - source_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe").expect("helper path"); + let resolved = source_path_for_exe(&exe, /*file_name*/ "codex-command-runner.exe") + .expect("helper path"); assert_eq!(resolved, sibling_helper); } diff --git a/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs b/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs index ef952a4e03..7649c0b17e 100644 --- a/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs +++ b/codex-rs/windows-sandbox-rs/src/setup_orchestrator.rs @@ -12,7 +12,9 @@ use std::process::Stdio; use crate::allow::AllowDenyPaths; use crate::allow::compute_allow_paths; +use crate::helper_materialization::HelperExecutable; use crate::helper_materialization::helper_bin_dir; +use crate::helper_materialization::resolve_helper_for_launch; use crate::logging::log_note; use crate::path_normalization::canonical_path_key; use crate::policy::SandboxPolicy; @@ -189,7 +191,12 @@ fn run_setup_refresh_inner( }; let json = serde_json::to_vec(&payload)?; let b64 = BASE64_STANDARD.encode(json); - let exe = find_setup_exe(); + let sandbox_log_dir = sandbox_dir(request.codex_home); + let exe = resolve_helper_for_launch( + HelperExecutable::Setup, + request.codex_home, + Some(&sandbox_log_dir), + ); // Refresh should never request elevation; ensure verb isn't set and we don't trigger UAC. let mut cmd = Command::new(&exe); cmd.arg(&b64).stdout(Stdio::null()).stderr(Stdio::null()); @@ -201,14 +208,14 @@ fn run_setup_refresh_inner( cwd.display(), b64.len() ), - Some(&sandbox_dir(request.codex_home)), + Some(&sandbox_log_dir), ); let status = cmd .status() .map_err(|e| { log_note( &format!("setup refresh: failed to spawn {}: {e}", exe.display()), - Some(&sandbox_dir(request.codex_home)), + Some(&sandbox_log_dir), ); e }) @@ -216,7 +223,7 @@ fn run_setup_refresh_inner( if !status.success() { log_note( &format!("setup refresh: exited with status {status:?}"), - Some(&sandbox_dir(request.codex_home)), + Some(&sandbox_log_dir), ); return Err(anyhow!("setup refresh failed with status {status}")); } @@ -555,28 +562,6 @@ fn quote_arg(arg: &str) -> String { out } -fn find_setup_exe() -> PathBuf { - if let Ok(exe) = std::env::current_exe() - && let Some(dir) = exe.parent() - { - let candidate = dir.join("codex-windows-sandbox-setup.exe"); - if candidate.exists() { - return candidate; - } - - // Standalone installs keep Windows helper binaries under - // `codex-resources/` next to `codex.exe`, so elevation needs to probe - // that sibling folder before falling back to PATH. - let resource_candidate = dir - .join("codex-resources") - .join("codex-windows-sandbox-setup.exe"); - if resource_candidate.exists() { - return resource_candidate; - } - } - PathBuf::from("codex-windows-sandbox-setup.exe") -} - fn report_helper_failure( codex_home: &Path, cleared_report: bool, @@ -607,7 +592,9 @@ fn run_setup_exe( use windows_sys::Win32::UI::Shell::SEE_MASK_NOCLOSEPROCESS; use windows_sys::Win32::UI::Shell::SHELLEXECUTEINFOW; use windows_sys::Win32::UI::Shell::ShellExecuteExW; - let exe = find_setup_exe(); + let sandbox_log_dir = sandbox_dir(codex_home); + let exe = + resolve_helper_for_launch(HelperExecutable::Setup, codex_home, Some(&sandbox_log_dir)); let payload_json = serde_json::to_string(payload).map_err(|err| { failure( SetupErrorCode::OrchestratorPayloadSerializeFailed, @@ -622,7 +609,7 @@ fn run_setup_exe( &format!( "setup orchestrator: failed to clear setup_error.json before launch: {err}" ), - Some(&sandbox_dir(codex_home)), + Some(&sandbox_log_dir), ); false } @@ -654,7 +641,7 @@ fn run_setup_exe( &format!( "setup orchestrator: failed to clear setup_error.json after success: {err}" ), - Some(&sandbox_dir(codex_home)), + Some(&sandbox_log_dir), ); } return Ok(()); @@ -701,7 +688,7 @@ fn run_setup_exe( if let Err(err) = clear_setup_error_report(codex_home) { log_note( &format!("setup orchestrator: failed to clear setup_error.json after success: {err}"), - Some(&sandbox_dir(codex_home)), + Some(&sandbox_log_dir), ); } Ok(())