mirror of
https://github.com/openai/codex.git
synced 2026-05-23 12:34:25 +00:00
windows-sandbox: materialize setup helper launches
This commit is contained in:
@@ -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<String> {
|
||||
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<CopyOutcome> {
|
||||
@@ -254,9 +250,12 @@ fn copy_from_source_if_needed(source: &Path, destination: &Path) -> Result<CopyO
|
||||
return Ok(CopyOutcome::Reused);
|
||||
}
|
||||
|
||||
let destination_dir = destination
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow!("helper destination has no parent: {}", destination.display()))?;
|
||||
let destination_dir = destination.parent().ok_or_else(|| {
|
||||
anyhow!(
|
||||
"helper destination has no parent: {}",
|
||||
destination.display()
|
||||
)
|
||||
})?;
|
||||
fs::create_dir_all(destination_dir).with_context(|| {
|
||||
format!(
|
||||
"create helper destination directory {}",
|
||||
@@ -327,8 +326,9 @@ fn destination_is_fresh(source: &Path, destination: &Path) -> Result<bool> {
|
||||
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<bool> {
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
Reference in New Issue
Block a user