Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions apps/codex-plus-launcher/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>();
let helper_only = args.iter().any(|arg| arg == "--helper-only");
let options = parse_launch_options(args.iter());
Comment on lines 51 to 53
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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 {
Expand All @@ -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,
Expand Down
10 changes: 9 additions & 1 deletion crates/codex-plus-core/src/launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}),
);
}
}
}
}
Expand Down
24 changes: 23 additions & 1 deletion crates/codex-plus-core/src/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Comment on lines +220 to +223

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)?;
Expand Down Expand Up @@ -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();
Expand Down