diff --git a/apps/codex-plus-launcher/src/main.rs b/apps/codex-plus-launcher/src/main.rs index cc12faef..845828e7 100644 --- a/apps/codex-plus-launcher/src/main.rs +++ b/apps/codex-plus-launcher/src/main.rs @@ -35,6 +35,19 @@ impl Default for LauncherHooks { #[tokio::main] async fn main() -> Result<()> { + if let Err(error) = launcher_main().await { + let _ = codex_plus_core::diagnostic_log::append_diagnostic_log( + "launcher.failed", + json!({ + "message": error.to_string() + }), + ); + return Err(error); + } + Ok(()) +} + +async fn launcher_main() -> Result<()> { let args = std::env::args().skip(1).collect::>(); let helper_only = args.iter().any(|arg| arg == "--helper-only"); let options = parse_launch_options(args.iter()); @@ -135,6 +148,7 @@ fn should_recover_stale_launcher(debug_port: u16) -> bool { async fn activate_existing_codex_app(options: &LaunchOptions) -> anyhow::Result<()> { let hooks = LauncherHooks::default(); + let helper_port = hooks.select_helper_port(options.helper_port); let settings = hooks.load_settings().await?; let app_dir = hooks.resolve_app_dir(options.app_dir.as_deref(), &settings)?; let launch_result = hooks @@ -146,7 +160,7 @@ async fn activate_existing_codex_app(options: &LaunchOptions) -> anyhow::Result< ) .await; if settings.enhancements_enabled { - hooks.start_helper(options.helper_port).await?; + hooks.start_helper(helper_port).await?; } let process_ids = codex_plus_core::watcher::find_codex_processes(); let mut activated = false; @@ -161,14 +175,14 @@ async fn activate_existing_codex_app(options: &LaunchOptions) -> anyhow::Result< } let injection_ready = if settings.enhancements_enabled { hooks - .ensure_injection(options.debug_port, options.helper_port, &app_dir) + .ensure_injection(options.debug_port, helper_port, &app_dir) .await } else { false }; if injection_ready { hooks - .start_bridge_watchdog(options.debug_port, options.helper_port) + .start_bridge_watchdog(options.debug_port, helper_port) .await?; hooks.write_status("running").await; } else if settings.enhancements_enabled { @@ -179,7 +193,8 @@ async fn activate_existing_codex_app(options: &LaunchOptions) -> anyhow::Result< json!({ "app_dir": app_dir.to_string_lossy(), "debug_port": options.debug_port, - "helper_port": options.helper_port, + "helper_port": helper_port, + "requested_helper_port": options.helper_port, "process_ids": process_ids, "activated": activated, "injection_ready": injection_ready, diff --git a/crates/codex-plus-core/src/launcher.rs b/crates/codex-plus-core/src/launcher.rs index fcdeb046..c4d03d46 100644 --- a/crates/codex-plus-core/src/launcher.rs +++ b/crates/codex-plus-core/src/launcher.rs @@ -835,7 +835,15 @@ impl LaunchHooks for DefaultLaunchHooks { } CodexLaunch::PackagedActivation { process_id, .. } => { if let Some(process_id) = process_id { - wait_for_windows_process_id(*process_id).await?; + if let Err(error) = wait_for_windows_process_id(*process_id).await { + let _ = crate::diagnostic_log::append_diagnostic_log( + "launcher.packaged_process_wait_failed_nonfatal", + serde_json::json!({ + "process_id": process_id, + "message": error.to_string() + }), + ); + } } } } diff --git a/crates/codex-plus-core/src/ports.rs b/crates/codex-plus-core/src/ports.rs index 2263b21f..3e8cf6e9 100644 --- a/crates/codex-plus-core/src/ports.rs +++ b/crates/codex-plus-core/src/ports.rs @@ -208,13 +208,20 @@ fn acquire_resilient_loopback_port_guard_with( Err(error) if error.kind() == std::io::ErrorKind::AddrInUse && can_connect(port) => { Err(error) } - Err(error) if error.kind() == std::io::ErrorKind::AddrInUse => { + Err(error) + if error.kind() == std::io::ErrorKind::AddrInUse || port_bind_forbidden(&error) => + { Ok(LoopbackPortGuard::fallback_lock(file, path)) } Err(error) => Err(error), } } +fn port_bind_forbidden(error: &std::io::Error) -> bool { + error.kind() == std::io::ErrorKind::PermissionDenied + || matches!(error.raw_os_error(), Some(10013)) +} + fn acquire_lock_guard(port: u16, state_dir: &Path) -> std::io::Result<(File, PathBuf)> { let dir = state_dir.join("locks"); std::fs::create_dir_all(&dir)?; @@ -322,6 +329,21 @@ mod tests { assert_eq!(second.kind(), std::io::ErrorKind::WouldBlock); } + #[test] + fn resilient_guard_uses_lock_fallback_when_port_bind_is_forbidden() { + let temp = tempfile::tempdir().unwrap(); + let guard = acquire_resilient_loopback_port_guard_with( + 57319, + temp.path(), + |_| Err(std::io::Error::from_raw_os_error(10013)), + |_| false, + ) + .unwrap(); + + assert!(guard._listener.is_none()); + assert!(guard.fallback_path().is_some()); + } + #[test] fn launcher_guard_port_returns_base_when_no_env_override() { let _guard = guard_port_env_lock();