From edb626fb8dfe3c8aa67ddbcfbe0bd3fb69135085 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 8 Apr 2026 08:50:59 -0700 Subject: [PATCH 1/3] Allow remote exec cwd in TUI startup --- codex-rs/tui/src/lib.rs | 46 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 7cd4c64949fb..e857f41d2116 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -589,8 +589,10 @@ fn latest_session_lookup_params( fn config_cwd_for_app_server_target( cwd: Option<&Path>, app_server_target: &AppServerTarget, + remote_exec_server_configured: bool, ) -> std::io::Result { - if matches!(app_server_target, AppServerTarget::Remote { .. }) { + if remote_exec_server_configured || matches!(app_server_target, AppServerTarget::Remote { .. }) + { return AbsolutePathBuf::current_dir(); } @@ -689,7 +691,14 @@ pub async fn run_main( }; let cwd = cli.cwd.clone(); - let config_cwd = config_cwd_for_app_server_target(cwd.as_deref(), &app_server_target)?; + let remote_exec_server_configured = codex_exec_server::EnvironmentManager::from_env() + .exec_server_url() + .is_some(); + let config_cwd = config_cwd_for_app_server_target( + cwd.as_deref(), + &app_server_target, + remote_exec_server_configured, + )?; #[allow(clippy::print_stderr)] let config_toml = match load_config_as_toml_with_cli_overrides( @@ -1712,6 +1721,7 @@ mod tests { use codex_protocol::protocol::SessionMetaLine; use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::TurnContextItem; + use pretty_assertions::assert_eq; use serial_test::serial; use tempfile::TempDir; @@ -1892,7 +1902,11 @@ mod tests { auth_token: None, }; - let config_cwd = config_cwd_for_app_server_target(Some(remote_only_cwd), &target)?; + let config_cwd = config_cwd_for_app_server_target( + Some(remote_only_cwd), + &target, + /*remote_exec_server_configured*/ false, + )?; assert_eq!(config_cwd, AbsolutePathBuf::current_dir()?); Ok(()) @@ -1903,7 +1917,11 @@ mod tests { let temp_dir = TempDir::new()?; let target = AppServerTarget::Embedded; - let config_cwd = config_cwd_for_app_server_target(Some(temp_dir.path()), &target)?; + let config_cwd = config_cwd_for_app_server_target( + Some(temp_dir.path()), + &target, + /*remote_exec_server_configured*/ false, + )?; assert_eq!( config_cwd, @@ -1912,6 +1930,26 @@ mod tests { Ok(()) } + #[test] + fn config_cwd_for_app_server_target_uses_current_dir_for_remote_exec_server() + -> std::io::Result<()> { + let remote_only_cwd = if cfg!(windows) { + Path::new(r"C:\definitely\not\local\to\this\test") + } else { + Path::new("/definitely/not/local/to/this/test") + }; + let target = AppServerTarget::Embedded; + + let config_cwd = config_cwd_for_app_server_target( + Some(remote_only_cwd), + &target, + /*remote_exec_server_configured*/ true, + )?; + + assert_eq!(config_cwd, AbsolutePathBuf::current_dir()?); + Ok(()) + } + #[tokio::test] async fn read_session_cwd_returns_none_without_sqlite_or_rollout_path() -> std::io::Result<()> { let temp_dir = TempDir::new()?; From f6822bf6dfeb47bae0da7238519d242e8f71164f Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 8 Apr 2026 09:14:05 -0700 Subject: [PATCH 2/3] Plumb environment manager into TUI app server --- codex-rs/Cargo.lock | 1 + codex-rs/app-server-client/Cargo.toml | 1 + codex-rs/app-server-client/src/lib.rs | 16 ++++- codex-rs/app-server/src/in_process.rs | 5 +- codex-rs/core/src/config/mod.rs | 4 +- codex-rs/exec-server/src/environment.rs | 15 ++++ codex-rs/exec/src/lib.rs | 4 +- codex-rs/tui/src/app.rs | 9 +++ codex-rs/tui/src/lib.rs | 91 ++++++++++++++----------- codex-rs/tui/src/onboarding/auth.rs | 3 + 10 files changed, 106 insertions(+), 43 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index b109d64430a9..3a79e30844f5 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1474,6 +1474,7 @@ dependencies = [ "codex-app-server-protocol", "codex-arg0", "codex-core", + "codex-exec-server", "codex-feedback", "codex-protocol", "futures", diff --git a/codex-rs/app-server-client/Cargo.toml b/codex-rs/app-server-client/Cargo.toml index a0b98c0fec7d..d245e617f932 100644 --- a/codex-rs/app-server-client/Cargo.toml +++ b/codex-rs/app-server-client/Cargo.toml @@ -16,6 +16,7 @@ codex-app-server = { workspace = true } codex-app-server-protocol = { workspace = true } codex-arg0 = { workspace = true } codex-core = { workspace = true } +codex-exec-server = { workspace = true } codex-feedback = { workspace = true } codex-protocol = { workspace = true } futures = { workspace = true } diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index f7f24eacdfff..931b727c8060 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -43,6 +43,7 @@ use codex_arg0::Arg0DispatchPaths; use codex_core::config::Config; use codex_core::config_loader::CloudRequirementsLoader; use codex_core::config_loader::LoaderOverrides; +pub use codex_exec_server::EnvironmentManager; use codex_feedback::CodexFeedback; use codex_protocol::protocol::SessionSource; use serde::de::DeserializeOwned; @@ -268,6 +269,8 @@ pub struct InProcessClientStartArgs { pub cloud_requirements: CloudRequirementsLoader, /// Feedback sink used by app-server/core telemetry and logs. pub feedback: CodexFeedback, + /// Environment manager used by core execution and filesystem operations. + pub environment_manager: Arc, /// Startup warnings emitted after initialize succeeds. pub config_warnings: Vec, /// Session source recorded in app-server thread metadata. @@ -317,6 +320,7 @@ impl InProcessClientStartArgs { loader_overrides: self.loader_overrides, cloud_requirements: self.cloud_requirements, feedback: self.feedback, + environment_manager: self.environment_manager, config_warnings: self.config_warnings, session_source: self.session_source, enable_codex_api_key_env: self.enable_codex_api_key_env, @@ -893,6 +897,7 @@ mod tests { loader_overrides: LoaderOverrides::default(), cloud_requirements: CloudRequirementsLoader::default(), feedback: CodexFeedback::new(), + environment_manager: Arc::new(EnvironmentManager::new(/*exec_server_url*/ None)), config_warnings: Vec::new(), session_source, enable_codex_api_key_env: false, @@ -1891,8 +1896,11 @@ mod tests { } #[tokio::test] - async fn runtime_start_args_leave_manager_bootstrap_to_app_server() { + async fn runtime_start_args_forward_environment_manager() { let config = Arc::new(build_test_config().await); + let environment_manager = Arc::new(EnvironmentManager::new(Some( + "ws://127.0.0.1:8765".to_string(), + ))); let runtime_args = InProcessClientStartArgs { arg0_paths: Arg0DispatchPaths::default(), @@ -1901,6 +1909,7 @@ mod tests { loader_overrides: LoaderOverrides::default(), cloud_requirements: CloudRequirementsLoader::default(), feedback: CodexFeedback::new(), + environment_manager: environment_manager.clone(), config_warnings: Vec::new(), session_source: SessionSource::Exec, enable_codex_api_key_env: false, @@ -1913,6 +1922,11 @@ mod tests { .into_runtime_start_args(); assert_eq!(runtime_args.config, config); + assert!(Arc::ptr_eq( + &runtime_args.environment_manager, + &environment_manager + )); + assert!(runtime_args.environment_manager.is_remote()); } #[tokio::test] diff --git a/codex-rs/app-server/src/in_process.rs b/codex-rs/app-server/src/in_process.rs index 7bddd8a71491..0ae8690096eb 100644 --- a/codex-rs/app-server/src/in_process.rs +++ b/codex-rs/app-server/src/in_process.rs @@ -117,6 +117,8 @@ pub struct InProcessStartArgs { pub cloud_requirements: CloudRequirementsLoader, /// Feedback sink used by app-server/core telemetry and logs. pub feedback: CodexFeedback, + /// Environment manager used by core execution and filesystem operations. + pub environment_manager: Arc, /// Startup warnings emitted after initialize succeeds. pub config_warnings: Vec, /// Session source stamped into thread/session metadata. @@ -388,7 +390,7 @@ fn start_uninitialized(args: InProcessStartArgs) -> InProcessClientHandle { outgoing: Arc::clone(&processor_outgoing), arg0_paths: args.arg0_paths, config: args.config, - environment_manager: Arc::new(EnvironmentManager::from_env()), + environment_manager: args.environment_manager, cli_overrides: args.cli_overrides, loader_overrides: args.loader_overrides, cloud_requirements: args.cloud_requirements, @@ -721,6 +723,7 @@ mod tests { loader_overrides: LoaderOverrides::default(), cloud_requirements: CloudRequirementsLoader::default(), feedback: CodexFeedback::new(), + environment_manager: Arc::new(EnvironmentManager::new(/*exec_server_url*/ None)), config_warnings: Vec::new(), session_source, enable_codex_api_key_env: false, diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index e0a24bcc1030..bb344331360d 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -785,12 +785,12 @@ impl Config { /// applied yet, which risks failing to enforce required constraints. pub async fn load_config_as_toml_with_cli_overrides( codex_home: &Path, - cwd: &AbsolutePathBuf, + cwd: Option<&AbsolutePathBuf>, cli_overrides: Vec<(String, TomlValue)>, ) -> std::io::Result { let config_layer_stack = load_config_layers_state( codex_home, - Some(cwd.clone()), + cwd.cloned(), &cli_overrides, LoaderOverrides::default(), CloudRequirementsLoader::default(), diff --git a/codex-rs/exec-server/src/environment.rs b/codex-rs/exec-server/src/environment.rs index c76ac431ec78..3323db0064a3 100644 --- a/codex-rs/exec-server/src/environment.rs +++ b/codex-rs/exec-server/src/environment.rs @@ -69,6 +69,11 @@ impl EnvironmentManager { self.exec_server_url.as_deref() } + /// Returns true when this manager is configured to use a remote exec server. + pub fn is_remote(&self) -> bool { + self.exec_server_url.is_some() + } + /// Returns the cached environment, creating it on first access. pub async fn current(&self) -> Result>, ExecServerError> { self.current_environment @@ -227,6 +232,7 @@ mod tests { assert!(!manager.disabled); assert_eq!(manager.exec_server_url(), None); + assert!(!manager.is_remote()); } #[test] @@ -235,6 +241,15 @@ mod tests { assert!(manager.disabled); assert_eq!(manager.exec_server_url(), None); + assert!(!manager.is_remote()); + } + + #[test] + fn environment_manager_reports_remote_url() { + let manager = EnvironmentManager::new(Some("ws://127.0.0.1:8765".to_string())); + + assert!(manager.is_remote()); + assert_eq!(manager.exec_server_url(), Some("ws://127.0.0.1:8765")); } #[tokio::test] diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 8c849ca806ea..76634e778438 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -14,6 +14,7 @@ pub use cli::Cli; pub use cli::Command; pub use cli::ReviewArgs; use codex_app_server_client::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY; +use codex_app_server_client::EnvironmentManager; use codex_app_server_client::InProcessAppServerClient; use codex_app_server_client::InProcessClientStartArgs; use codex_app_server_client::InProcessServerEvent; @@ -293,7 +294,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result #[allow(clippy::print_stderr)] let config_toml = match load_config_as_toml_with_cli_overrides( &codex_home, - &config_cwd, + Some(&config_cwd), cli_kv_overrides.clone(), ) .await @@ -471,6 +472,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result loader_overrides: run_loader_overrides, cloud_requirements: run_cloud_requirements, feedback: CodexFeedback::new(), + environment_manager: std::sync::Arc::new(EnvironmentManager::from_env()), config_warnings, session_source: SessionSource::Exec, enable_codex_api_key_env: true, diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 7584b232a8a1..9d949e496982 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -95,6 +95,7 @@ use codex_core::config_loader::ConfigLayerStackOrdering; use codex_core::lookup_message_history_entry; #[cfg(target_os = "windows")] use codex_core::windows_sandbox::WindowsSandboxLevelExt; +use codex_exec_server::EnvironmentManager; use codex_features::Feature; use codex_models_manager::collaboration_mode_presets::CollaborationModesConfig; use codex_models_manager::model_presets::HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG; @@ -978,6 +979,7 @@ pub(crate) struct App { pub(crate) backtrack_render_pending: bool, pub(crate) feedback: codex_feedback::CodexFeedback, feedback_audience: FeedbackAudience, + environment_manager: Arc, remote_app_server_url: Option, remote_app_server_auth_token: Option, /// Set when the user confirms an update; propagated on exit. @@ -3568,6 +3570,7 @@ impl App { should_prompt_windows_sandbox_nux_at_startup: bool, remote_app_server_url: Option, remote_app_server_auth_token: Option, + environment_manager: Arc, ) -> Result { use tokio_stream::StreamExt; let (app_event_tx, mut app_event_rx) = unbounded_channel(); @@ -3783,6 +3786,7 @@ impl App { backtrack_render_pending: false, feedback: feedback.clone(), feedback_audience, + environment_manager, remote_app_server_url, remote_app_server_auth_token, pending_update_action: None, @@ -4039,6 +4043,7 @@ impl App { }, None => crate::AppServerTarget::Embedded, }, + self.environment_manager.clone(), ) .await { @@ -9101,6 +9106,7 @@ guardian_approval = true backtrack_render_pending: false, feedback: codex_feedback::CodexFeedback::new(), feedback_audience: FeedbackAudience::External, + environment_manager: Arc::new(EnvironmentManager::new(/*exec_server_url*/ None)), remote_app_server_url: None, remote_app_server_auth_token: None, pending_update_action: None, @@ -9155,6 +9161,9 @@ guardian_approval = true backtrack_render_pending: false, feedback: codex_feedback::CodexFeedback::new(), feedback_audience: FeedbackAudience::External, + environment_manager: Arc::new(EnvironmentManager::new( + /*exec_server_url*/ None, + )), remote_app_server_url: None, remote_app_server_auth_token: None, pending_update_action: None, diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index e857f41d2116..1f1a6f96b5c2 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -36,6 +36,7 @@ use codex_core::format_exec_policy_error_with_source; use codex_core::path_utils; use codex_core::read_session_meta_line; use codex_core::windows_sandbox::WindowsSandboxLevelExt; +use codex_exec_server::EnvironmentManager; use codex_login::AuthConfig; use codex_login::default_client::set_default_client_residency_requirement; use codex_login::enforce_login_restrictions; @@ -237,6 +238,7 @@ async fn start_embedded_app_server( loader_overrides: LoaderOverrides, cloud_requirements: CloudRequirementsLoader, feedback: codex_feedback::CodexFeedback, + environment_manager: Arc, ) -> color_eyre::Result { start_embedded_app_server_with( arg0_paths, @@ -245,6 +247,7 @@ async fn start_embedded_app_server( loader_overrides, cloud_requirements, feedback, + environment_manager, InProcessAppServerClient::start, ) .await @@ -351,6 +354,7 @@ async fn connect_remote_app_server( Ok(AppServerClient::Remote(app_server)) } +#[allow(clippy::too_many_arguments)] async fn start_app_server( target: &AppServerTarget, arg0_paths: Arg0DispatchPaths, @@ -359,6 +363,7 @@ async fn start_app_server( loader_overrides: LoaderOverrides, cloud_requirements: CloudRequirementsLoader, feedback: codex_feedback::CodexFeedback, + environment_manager: Arc, ) -> color_eyre::Result { match target { AppServerTarget::Embedded => start_embedded_app_server( @@ -368,6 +373,7 @@ async fn start_app_server( loader_overrides, cloud_requirements, feedback, + environment_manager, ) .await .map(AppServerClient::InProcess), @@ -381,6 +387,7 @@ async fn start_app_server( pub(crate) async fn start_app_server_for_picker( config: &Config, target: &AppServerTarget, + environment_manager: Arc, ) -> color_eyre::Result { let app_server = start_app_server( target, @@ -390,6 +397,7 @@ pub(crate) async fn start_app_server_for_picker( LoaderOverrides::default(), CloudRequirementsLoader::default(), codex_feedback::CodexFeedback::new(), + environment_manager, ) .await?; Ok(AppServerSession::new(app_server)) @@ -399,9 +407,15 @@ pub(crate) async fn start_app_server_for_picker( pub(crate) async fn start_embedded_app_server_for_picker( config: &Config, ) -> color_eyre::Result { - start_app_server_for_picker(config, &AppServerTarget::Embedded).await + start_app_server_for_picker( + config, + &AppServerTarget::Embedded, + Arc::new(EnvironmentManager::new(/*exec_server_url*/ None)), + ) + .await } +#[allow(clippy::too_many_arguments)] async fn start_embedded_app_server_with( arg0_paths: Arg0DispatchPaths, config: Config, @@ -409,6 +423,7 @@ async fn start_embedded_app_server_with( loader_overrides: LoaderOverrides, cloud_requirements: CloudRequirementsLoader, feedback: codex_feedback::CodexFeedback, + environment_manager: Arc, start_client: F, ) -> color_eyre::Result where @@ -432,6 +447,7 @@ where loader_overrides, cloud_requirements, feedback, + environment_manager, config_warnings, session_source: codex_protocol::protocol::SessionSource::Cli, enable_codex_api_key_env: false, @@ -589,17 +605,19 @@ fn latest_session_lookup_params( fn config_cwd_for_app_server_target( cwd: Option<&Path>, app_server_target: &AppServerTarget, - remote_exec_server_configured: bool, -) -> std::io::Result { - if remote_exec_server_configured || matches!(app_server_target, AppServerTarget::Remote { .. }) + environment_manager: &EnvironmentManager, +) -> std::io::Result> { + if environment_manager.is_remote() + || matches!(app_server_target, AppServerTarget::Remote { .. }) { - return AbsolutePathBuf::current_dir(); + return Ok(None); } - match cwd { + let cwd = match cwd { Some(path) => AbsolutePathBuf::from_absolute_path(path.canonicalize()?), None => AbsolutePathBuf::current_dir(), - } + }?; + Ok(Some(cwd)) } fn latest_session_cwd_filter<'a>( @@ -690,20 +708,15 @@ pub async fn run_main( } }; + let environment_manager = Arc::new(EnvironmentManager::from_env()); let cwd = cli.cwd.clone(); - let remote_exec_server_configured = codex_exec_server::EnvironmentManager::from_env() - .exec_server_url() - .is_some(); - let config_cwd = config_cwd_for_app_server_target( - cwd.as_deref(), - &app_server_target, - remote_exec_server_configured, - )?; + let config_cwd = + config_cwd_for_app_server_target(cwd.as_deref(), &app_server_target, &environment_manager)?; #[allow(clippy::print_stderr)] let config_toml = match load_config_as_toml_with_cli_overrides( &codex_home, - &config_cwd, + config_cwd.as_ref(), cli_kv_overrides.clone(), ) .await @@ -960,6 +973,7 @@ pub async fn run_main( feedback, remote_url, remote_auth_token, + environment_manager, ) .await .map_err(|err| std::io::Error::other(err.to_string())) @@ -979,6 +993,7 @@ async fn run_ratatui_app( feedback: codex_feedback::CodexFeedback, remote_url: Option, remote_auth_token: Option, + environment_manager: Arc, ) -> color_eyre::Result { let remote_mode = matches!(&app_server_target, AppServerTarget::Remote { .. }); color_eyre::install()?; @@ -1040,6 +1055,7 @@ async fn run_ratatui_app( loader_overrides.clone(), cloud_requirements.clone(), feedback.clone(), + environment_manager.clone(), ) .await?, ) @@ -1157,6 +1173,7 @@ async fn run_ratatui_app( loader_overrides.clone(), cloud_requirements.clone(), feedback.clone(), + environment_manager.clone(), ) .await?, ) @@ -1384,6 +1401,7 @@ async fn run_ratatui_app( loader_overrides, cloud_requirements.clone(), feedback.clone(), + environment_manager.clone(), ) .await { @@ -1410,6 +1428,7 @@ async fn run_ratatui_app( should_prompt_windows_sandbox_nux_at_startup, remote_url, remote_auth_token, + environment_manager, ) .await; @@ -1742,6 +1761,7 @@ mod tests { LoaderOverrides::default(), CloudRequirementsLoader::default(), codex_feedback::CodexFeedback::new(), + Arc::new(EnvironmentManager::new(/*exec_server_url*/ None)), ) .await } @@ -1890,8 +1910,7 @@ mod tests { } #[test] - fn config_cwd_for_app_server_target_uses_current_dir_for_remote_sessions() -> std::io::Result<()> - { + fn config_cwd_for_app_server_target_omits_cwd_for_remote_sessions() -> std::io::Result<()> { let remote_only_cwd = if cfg!(windows) { Path::new(r"C:\definitely\not\local\to\this\test") } else { @@ -1901,14 +1920,12 @@ mod tests { websocket_url: "ws://127.0.0.1:1234/".to_string(), auth_token: None, }; + let environment_manager = EnvironmentManager::new(/*exec_server_url*/ None); - let config_cwd = config_cwd_for_app_server_target( - Some(remote_only_cwd), - &target, - /*remote_exec_server_configured*/ false, - )?; + let config_cwd = + config_cwd_for_app_server_target(Some(remote_only_cwd), &target, &environment_manager)?; - assert_eq!(config_cwd, AbsolutePathBuf::current_dir()?); + assert_eq!(config_cwd, None); Ok(()) } @@ -1916,37 +1933,34 @@ mod tests { fn config_cwd_for_app_server_target_canonicalizes_embedded_cli_cwd() -> std::io::Result<()> { let temp_dir = TempDir::new()?; let target = AppServerTarget::Embedded; + let environment_manager = EnvironmentManager::new(/*exec_server_url*/ None); - let config_cwd = config_cwd_for_app_server_target( - Some(temp_dir.path()), - &target, - /*remote_exec_server_configured*/ false, - )?; + let config_cwd = + config_cwd_for_app_server_target(Some(temp_dir.path()), &target, &environment_manager)?; assert_eq!( config_cwd, - AbsolutePathBuf::from_absolute_path(temp_dir.path().canonicalize()?)? + Some(AbsolutePathBuf::from_absolute_path( + temp_dir.path().canonicalize()? + )?) ); Ok(()) } #[test] - fn config_cwd_for_app_server_target_uses_current_dir_for_remote_exec_server() - -> std::io::Result<()> { + fn config_cwd_for_app_server_target_omits_cwd_for_remote_exec_server() -> std::io::Result<()> { let remote_only_cwd = if cfg!(windows) { Path::new(r"C:\definitely\not\local\to\this\test") } else { Path::new("/definitely/not/local/to/this/test") }; let target = AppServerTarget::Embedded; + let environment_manager = EnvironmentManager::new(Some("ws://127.0.0.1:8765".to_string())); - let config_cwd = config_cwd_for_app_server_target( - Some(remote_only_cwd), - &target, - /*remote_exec_server_configured*/ true, - )?; + let config_cwd = + config_cwd_for_app_server_target(Some(remote_only_cwd), &target, &environment_manager)?; - assert_eq!(config_cwd, AbsolutePathBuf::current_dir()?); + assert_eq!(config_cwd, None); Ok(()) } @@ -2070,6 +2084,7 @@ mod tests { LoaderOverrides::default(), CloudRequirementsLoader::default(), codex_feedback::CodexFeedback::new(), + Arc::new(EnvironmentManager::new(/*exec_server_url*/ None)), |_args| async { Err(std::io::Error::other("boom")) }, ) .await; diff --git a/codex-rs/tui/src/onboarding/auth.rs b/codex-rs/tui/src/onboarding/auth.rs index f991d028cd35..a4960cfc35b8 100644 --- a/codex-rs/tui/src/onboarding/auth.rs +++ b/codex-rs/tui/src/onboarding/auth.rs @@ -988,6 +988,9 @@ mod tests { "https://chatgpt.com/backend-api/".to_string(), ), feedback: codex_feedback::CodexFeedback::new(), + environment_manager: Arc::new(codex_app_server_client::EnvironmentManager::new( + /*exec_server_url*/ None, + )), config_warnings: Vec::new(), session_source: SessionSource::Cli, enable_codex_api_key_env: false, From a56659fb5157d8078799c045dda9199ec610352a Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 8 Apr 2026 10:58:40 -0700 Subject: [PATCH 3/3] Print remote codex sync cwd --- scripts/start-codex-exec.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/start-codex-exec.sh b/scripts/start-codex-exec.sh index 2c737a893865..7102d1d08381 100755 --- a/scripts/start-codex-exec.sh +++ b/scripts/start-codex-exec.sh @@ -28,6 +28,7 @@ remote_exec_server_start_timeout_seconds="${CODEX_REMOTE_EXEC_SERVER_START_TIMEO remote_exec_server_pid='' remote_exec_server_log_path='' remote_exec_server_pid_path='' +remote_repo_root='' cleanup() { local exit_code=$? @@ -119,6 +120,7 @@ while (( SECONDS < deadline )); do if [[ "${listen_url}" == ws://* ]]; then printf 'remote_exec_server_pid=%s\n' "${remote_exec_server_pid}" printf 'remote_exec_server_log_path=%s\n' "${remote_exec_server_log_path}" + printf 'remote_repo_root=%s\n' "${remote_repo_root}" printf 'listen_url=%s\n' "${listen_url}" exit 0 fi @@ -148,13 +150,16 @@ while IFS='=' read -r key value; do remote_exec_server_log_path) remote_exec_server_log_path="${value}" ;; + remote_repo_root) + remote_repo_root="${value}" + ;; listen_url) listen_url="${value}" ;; esac done <<< "${remote_start_output}" -if [[ -z "${remote_exec_server_pid}" || -z "${listen_url}" ]]; then +if [[ -z "${remote_exec_server_pid}" || -z "${listen_url}" || -z "${remote_repo_root}" ]]; then echo "failed to parse remote exec server startup output" >&2 exit 1 fi @@ -169,7 +174,9 @@ echo "Remote exec server: ${listen_url}" echo "Remote exec server log: ${remote_exec_server_log_path}" echo "Press Ctrl-C to stop the SSH tunnel and remote exec server." echo "Start codex via: " -echo " CODEX_EXEC_SERVER_URL=ws://127.0.0.1:${local_exec_server_port} codex -C /tmp" +printf ' CODEX_EXEC_SERVER_URL=ws://127.0.0.1:%s codex -C %q\n' \ + "${local_exec_server_port}" \ + "${remote_repo_root}" ssh \ -nNT \