From ae5f0153a2eb642da56187c3690d697b10f2acb7 Mon Sep 17 00:00:00 2001 From: Daniil Shushakov <4shushakov@gmail.com> Date: Fri, 28 Aug 2026 16:35:06 +0300 Subject: [PATCH] Use native Codex runtime on Windows --- .../src/agent/codex_acp_provisioner.rs | 47 +++++++++---------- .../src/agent/codex_acp_provisioner_tests.rs | 39 ++++++++------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/openaide-rs/app-server/src/agent/codex_acp_provisioner.rs b/openaide-rs/app-server/src/agent/codex_acp_provisioner.rs index 62a7eaad..68934bb0 100644 --- a/openaide-rs/app-server/src/agent/codex_acp_provisioner.rs +++ b/openaide-rs/app-server/src/agent/codex_acp_provisioner.rs @@ -211,7 +211,7 @@ impl CodexAcpProvisioner { .map_err(provisioning_installer_error) .and_then(|_| { validate_package(&staging)?; - validate_platform_launcher(&staging, self.windows)?; + validate_platform_runtime(&staging, self.windows)?; fs::write(staging.join(MANAGED_MARKER), managed_marker()) .map_err(provisioning_io_error)?; fs::rename(&staging, version_root).map_err(provisioning_io_error) @@ -226,26 +226,23 @@ impl CodexAcpProvisioner { let lease = Arc::new(open_lock_file(&version_root.join(".lease"))?); FileExt::lock_shared(lease.as_ref()).map_err(provisioning_io_error)?; - let (command, args) = if self.windows { - ( - windows_package_launcher(version_root) + let entrypoint = package_root(version_root).join("dist/index.js"); + let mut config = config; + config.command = resolved_command_or_name("node"); + config.args = vec![entrypoint.to_string_lossy().into_owned()]; + if self.windows { + // The @openai/codex Node launcher can terminate when nested under + // codex-acp on Windows. Use its pinned native binary directly. + config.env.retain(|(name, _)| name != "CODEX_PATH"); + config.env.push(( + "CODEX_PATH".to_string(), + windows_codex_binary(version_root) .to_string_lossy() .into_owned(), - Vec::new(), - ) - } else { - let entrypoint = package_root(version_root).join("dist/index.js"); - ( - resolved_command_or_name("node"), - vec![entrypoint.to_string_lossy().into_owned()], - ) - }; + )); + } Ok(PreparedCodexAcpLaunch { - config: AcpAgentConfig { - command, - args, - ..config - }, + config, lease: Some(lease), }) } @@ -336,17 +333,19 @@ fn valid_installation(version_root: &Path, windows: bool) -> bool { .as_deref() == Some(managed_marker().as_str()) && validate_package(version_root).is_ok() - && validate_platform_launcher(version_root, windows).is_ok() + && validate_platform_runtime(version_root, windows).is_ok() } -fn windows_package_launcher(version_root: &Path) -> PathBuf { - version_root.join("node_modules/.bin/codex-acp.cmd") +fn windows_codex_binary(version_root: &Path) -> PathBuf { + version_root + .join("node_modules/@openai/codex-win32-x64") + .join("vendor/x86_64-pc-windows-msvc/bin/codex.exe") } -fn validate_platform_launcher(version_root: &Path, windows: bool) -> Result<(), RuntimeError> { - if windows && !windows_package_launcher(version_root).is_file() { +fn validate_platform_runtime(version_root: &Path, windows: bool) -> Result<(), RuntimeError> { + if windows && !windows_codex_binary(version_root).is_file() { return Err(provisioning_error( - "installed Codex integration did not provide its Windows launcher".to_string(), + "installed Codex integration did not provide its native Windows runtime".to_string(), )); } Ok(()) diff --git a/openaide-rs/app-server/src/agent/codex_acp_provisioner_tests.rs b/openaide-rs/app-server/src/agent/codex_acp_provisioner_tests.rs index aed01fa0..11077d11 100644 --- a/openaide-rs/app-server/src/agent/codex_acp_provisioner_tests.rs +++ b/openaide-rs/app-server/src/agent/codex_acp_provisioner_tests.rs @@ -61,10 +61,11 @@ impl CodexAcpInstaller for RecordingInstaller { .expect("write managed package manifest"); fs::write(package_root.join("dist/index.js"), "#!/usr/bin/env node\n") .expect("write managed package entrypoint"); - let bin_root = destination.join("node_modules/.bin"); - fs::create_dir_all(&bin_root).expect("create managed package launchers"); - fs::write(bin_root.join("codex-acp.cmd"), "@echo off\r\n") - .expect("write managed Windows package launcher"); + let codex_root = destination + .join("node_modules/@openai/codex-win32-x64/vendor/x86_64-pc-windows-msvc/bin"); + fs::create_dir_all(&codex_root).expect("create managed native Codex fixture"); + fs::write(codex_root.join("codex.exe"), "native Codex fixture") + .expect("write managed native Codex fixture"); Ok(()) } } @@ -151,7 +152,7 @@ fn explicit_codex_launch_installs_the_locked_integration_once_and_reuses_it() { } #[test] -fn windows_launch_uses_the_managed_batch_entrypoint() { +fn windows_launch_uses_the_managed_native_codex_binary() { let storage = TempDir::new().expect("temporary storage root"); let provisioner = CodexAcpProvisioner::with_installer_for_platform( storage.path().to_path_buf(), @@ -163,20 +164,24 @@ fn windows_launch_uses_the_managed_batch_entrypoint() { .prepare(AcpAgentConfig::codex()) .expect("managed Windows Codex launch"); - let command = std::path::Path::new(&launch.config.command); + let expected_codex = storage + .path() + .join("agent-runtimes/codex-acp") + .join(CODEX_ACP_VERSION) + .join("node_modules/@openai/codex-win32-x64") + .join("vendor/x86_64-pc-windows-msvc/bin/codex.exe"); assert_eq!( - command.file_name().and_then(std::ffi::OsStr::to_str), - Some("codex-acp.cmd") + launch + .config + .env + .iter() + .find(|(name, _)| name == "CODEX_PATH") + .map(|(_, value)| value.as_str()), + Some(expected_codex.to_string_lossy().as_ref()), ); - assert_eq!( - command - .parent() - .and_then(std::path::Path::file_name) - .and_then(std::ffi::OsStr::to_str), - Some(".bin"), - ); - assert!(launch.config.args.is_empty()); - assert!(command.is_file()); + assert_eq!(launch.config.args.len(), 1); + assert!(launch.config.args[0].ends_with("node_modules/@openaide/codex-acp/dist/index.js")); + assert!(!launch.config.command.ends_with(".cmd")); } #[test]