diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index e9b514bf5f..6e10cef255 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -2092,14 +2092,17 @@ mod tests { #[tokio::test] async fn runtime_start_args_forward_environment_manager() { let config = Arc::new(build_test_config().await); - let environment_manager = Arc::new(EnvironmentManager::create_for_tests( - Some("ws://127.0.0.1:8765".to_string()), - ExecServerRuntimePaths::new( - std::env::current_exe().expect("current exe"), - /*codex_linux_sandbox_exe*/ None, + let environment_manager = Arc::new( + EnvironmentManager::create_for_tests( + Some("ws://127.0.0.1:8765".to_string()), + ExecServerRuntimePaths::new( + std::env::current_exe().expect("current exe"), + /*codex_linux_sandbox_exe*/ None, + ) + .expect("runtime paths"), ) - .expect("runtime paths"), - )); + .await, + ); let runtime_args = InProcessClientStartArgs { arg0_paths: Arg0DispatchPaths::default(), diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index f27261728e..2efdfef52e 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -420,12 +420,15 @@ pub async fn run_main_with_transport_options( auth: AppServerWebsocketAuthSettings, runtime_options: AppServerRuntimeOptions, ) -> IoResult<()> { - let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::new( - ExecServerRuntimePaths::from_optional_paths( - arg0_paths.codex_self_exe.clone(), - arg0_paths.codex_linux_sandbox_exe.clone(), - )?, - ))); + let environment_manager = Arc::new( + EnvironmentManager::new(EnvironmentManagerArgs::new( + ExecServerRuntimePaths::from_optional_paths( + arg0_paths.codex_self_exe.clone(), + arg0_paths.codex_linux_sandbox_exe.clone(), + )?, + )) + .await, + ); let (transport_event_tx, mut transport_event_rx) = mpsc::channel::(CHANNEL_CAPACITY); let (outgoing_tx, mut outgoing_rx) = mpsc::channel::(CHANNEL_CAPACITY); diff --git a/codex-rs/core/src/connectors.rs b/codex-rs/core/src/connectors.rs index 3c7ef672be..0bd53a50ee 100644 --- a/codex-rs/core/src/connectors.rs +++ b/codex-rs/core/src/connectors.rs @@ -202,7 +202,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options_and_status( config.codex_linux_sandbox_exe.clone(), )?; let environment_manager = - EnvironmentManager::new(EnvironmentManagerArgs::new(local_runtime_paths)); + EnvironmentManager::new(EnvironmentManagerArgs::new(local_runtime_paths)).await; list_accessible_connectors_from_mcp_tools_with_environment_manager( config, force_refetch, diff --git a/codex-rs/core/src/environment_selection.rs b/codex-rs/core/src/environment_selection.rs index f14361bdb5..b4bd9cbe89 100644 --- a/codex-rs/core/src/environment_selection.rs +++ b/codex-rs/core/src/environment_selection.rs @@ -106,7 +106,8 @@ mod tests { let manager = EnvironmentManager::create_for_tests( Some("ws://127.0.0.1:8765".to_string()), test_runtime_paths(), - ); + ) + .await; assert_eq!( default_thread_environment_selections(&manager, &cwd), diff --git a/codex-rs/core/src/prompt_debug.rs b/codex-rs/core/src/prompt_debug.rs index 145e372981..1b0c75230c 100644 --- a/codex-rs/core/src/prompt_debug.rs +++ b/codex-rs/core/src/prompt_debug.rs @@ -48,9 +48,7 @@ pub async fn build_prompt_input( &config, Arc::clone(&auth_manager), SessionSource::Exec, - Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::new( - local_runtime_paths, - ))), + Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::new(local_runtime_paths)).await), /*analytics_events_client*/ None, state_db, thread_store, diff --git a/codex-rs/core/src/thread_manager_tests.rs b/codex-rs/core/src/thread_manager_tests.rs index 7ad54565ff..0f6afa05a6 100644 --- a/codex-rs/core/src/thread_manager_tests.rs +++ b/codex-rs/core/src/thread_manager_tests.rs @@ -318,10 +318,13 @@ async fn start_thread_accepts_explicit_environment_when_default_environment_is_d /*codex_linux_sandbox_exe*/ None, ) .expect("runtime paths"); - let environment_manager = Arc::new(codex_exec_server::EnvironmentManager::create_for_tests( - Some("none".to_string()), - runtime_paths, - )); + let environment_manager = Arc::new( + codex_exec_server::EnvironmentManager::create_for_tests( + Some("none".to_string()), + runtime_paths, + ) + .await, + ); let manager = ThreadManager::with_models_provider_and_home_for_tests( CodexAuth::from_api_key("dummy"), config.model_provider.clone(), diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 5399d05292..1728f5bc5c 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -391,11 +391,13 @@ impl TestCodexBuilder { std::env::current_exe()?, /*codex_linux_sandbox_exe*/ None, )?; - let environment_manager = - Arc::new(codex_exec_server::EnvironmentManager::create_for_tests( + let environment_manager = Arc::new( + codex_exec_server::EnvironmentManager::create_for_tests( exec_server_url, local_runtime_paths, - )); + ) + .await, + ); let file_system = test_env.environment().get_filesystem(); let mut workspace_setups = vec![]; swap(&mut self.workspace_setups, &mut workspace_setups); diff --git a/codex-rs/exec-server/src/environment.rs b/codex-rs/exec-server/src/environment.rs index 7dc4ae2ae8..d7867c9732 100644 --- a/codex-rs/exec-server/src/environment.rs +++ b/codex-rs/exec-server/src/environment.rs @@ -9,7 +9,9 @@ use crate::client::LazyRemoteExecServerClient; use crate::client::http_client::ReqwestHttpClient; use crate::client_api::ExecServerTransportParams; use crate::environment_provider::DefaultEnvironmentProvider; +use crate::environment_provider::EnvironmentDefault; use crate::environment_provider::EnvironmentProvider; +use crate::environment_provider::EnvironmentProviderSnapshot; use crate::environment_provider::normalize_exec_server_url; use crate::local_file_system::LocalFileSystem; use crate::local_process::LocalProcess; @@ -23,8 +25,7 @@ pub const CODEX_EXEC_SERVER_URL_ENV_VAR: &str = "CODEX_EXEC_SERVER_URL"; /// /// `EnvironmentManager` is a shared registry for concrete environments. Its /// default constructor preserves the legacy `CODEX_EXEC_SERVER_URL` behavior -/// while provider-based construction accepts a provider-supplied environment -/// list and default id. +/// while provider-based construction accepts a provider-supplied snapshot. /// /// Setting `CODEX_EXEC_SERVER_URL=none` disables environment access by leaving /// the default environment unset while still keeping an explicit local @@ -81,44 +82,56 @@ impl EnvironmentManager { } /// Builds a test-only manager from a raw exec-server URL value. - pub fn create_for_tests( + pub async fn create_for_tests( exec_server_url: Option, local_runtime_paths: ExecServerRuntimePaths, ) -> Self { - Self::from_default_provider_url(exec_server_url, local_runtime_paths) + Self::from_default_provider_url(exec_server_url, local_runtime_paths).await } /// Builds a manager from `CODEX_EXEC_SERVER_URL` and local runtime paths /// used when creating local filesystem helpers. - pub fn new(args: EnvironmentManagerArgs) -> Self { + pub async fn new(args: EnvironmentManagerArgs) -> Self { let EnvironmentManagerArgs { local_runtime_paths, } = args; let exec_server_url = std::env::var(CODEX_EXEC_SERVER_URL_ENV_VAR).ok(); - Self::from_default_provider_url(exec_server_url, local_runtime_paths) + Self::from_default_provider_url(exec_server_url, local_runtime_paths).await } - fn from_default_provider_url( + async fn from_default_provider_url( exec_server_url: Option, local_runtime_paths: ExecServerRuntimePaths, ) -> Self { let provider = DefaultEnvironmentProvider::new(exec_server_url); - match Self::from_provider(&provider, local_runtime_paths) { + match Self::from_provider(&provider, local_runtime_paths).await { Ok(manager) => manager, Err(err) => panic!("default provider should create valid environments: {err}"), } } - /// Builds a manager from a provider-supplied environment list and default. - pub fn from_provider

( + /// Builds a manager from a provider-supplied startup snapshot. + pub async fn from_provider

( provider: &P, local_runtime_paths: ExecServerRuntimePaths, ) -> Result where P: EnvironmentProvider + ?Sized, { - let environments = provider.get_environments(&local_runtime_paths)?; - let default_environment_id = provider.default_environment_id(); + Self::from_provider_snapshot( + provider.snapshot(&local_runtime_paths).await?, + local_runtime_paths, + ) + } + + fn from_provider_snapshot( + snapshot: EnvironmentProviderSnapshot, + local_runtime_paths: ExecServerRuntimePaths, + ) -> Result { + let EnvironmentProviderSnapshot { + environments, + default, + } = snapshot; for id in environments.keys() { if id.is_empty() { return Err(ExecServerError::Protocol( @@ -127,13 +140,17 @@ impl EnvironmentManager { } } - if let Some(environment_id) = default_environment_id.as_ref() - && !environments.contains_key(environment_id) - { - return Err(ExecServerError::Protocol(format!( - "default environment `{environment_id}` is not configured" - ))); - } + let default_environment = match default { + EnvironmentDefault::Disabled => None, + EnvironmentDefault::EnvironmentId(environment_id) => { + if !environments.contains_key(&environment_id) { + return Err(ExecServerError::Protocol(format!( + "default environment `{environment_id}` is not configured" + ))); + } + Some(environment_id) + } + }; let local_environment = Arc::new(Environment::local(local_runtime_paths)); let environments = environments .into_iter() @@ -141,7 +158,7 @@ impl EnvironmentManager { .collect(); Ok(Self { - default_environment: default_environment_id, + default_environment, environments, local_environment, }) @@ -311,23 +328,21 @@ mod tests { use crate::ExecServerError; use crate::ExecServerRuntimePaths; use crate::ProcessId; + use crate::environment_provider::EnvironmentDefault; + use crate::environment_provider::EnvironmentProviderSnapshot; use pretty_assertions::assert_eq; struct TestEnvironmentProvider { - environments: HashMap, - default_environment_id: Option, + snapshot: EnvironmentProviderSnapshot, } + #[async_trait::async_trait] impl EnvironmentProvider for TestEnvironmentProvider { - fn get_environments( + async fn snapshot( &self, _local_runtime_paths: &ExecServerRuntimePaths, - ) -> Result, ExecServerError> { - Ok(self.environments.clone()) - } - - fn default_environment_id(&self) -> Option { - self.default_environment_id.clone() + ) -> Result { + Ok(self.snapshot.clone()) } } @@ -351,7 +366,7 @@ mod tests { #[tokio::test] async fn environment_manager_normalizes_empty_url() { let manager = - EnvironmentManager::create_for_tests(Some(String::new()), test_runtime_paths()); + EnvironmentManager::create_for_tests(Some(String::new()), test_runtime_paths()).await; let environment = manager.default_environment().expect("default environment"); assert_eq!(manager.default_environment_id(), Some(LOCAL_ENVIRONMENT_ID)); @@ -381,7 +396,8 @@ mod tests { let manager = EnvironmentManager::create_for_tests( Some("ws://127.0.0.1:8765".to_string()), test_runtime_paths(), - ); + ) + .await; let environment = manager.default_environment().expect("default environment"); assert_eq!( @@ -422,14 +438,17 @@ mod tests { #[tokio::test] async fn environment_manager_builds_from_provider() { let provider = TestEnvironmentProvider { - environments: HashMap::from([( - REMOTE_ENVIRONMENT_ID.to_string(), - Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string())) - .expect("remote environment"), - )]), - default_environment_id: Some(REMOTE_ENVIRONMENT_ID.to_string()), + snapshot: EnvironmentProviderSnapshot { + environments: HashMap::from([( + REMOTE_ENVIRONMENT_ID.to_string(), + Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string())) + .expect("remote environment"), + )]), + default: EnvironmentDefault::EnvironmentId(REMOTE_ENVIRONMENT_ID.to_string()), + }, }; let manager = EnvironmentManager::from_provider(&provider, test_runtime_paths()) + .await .expect("environment manager"); assert_eq!( @@ -449,10 +468,13 @@ mod tests { #[tokio::test] async fn environment_manager_rejects_empty_environment_id() { let provider = TestEnvironmentProvider { - environments: HashMap::from([("".to_string(), Environment::default_for_tests())]), - default_environment_id: None, + snapshot: EnvironmentProviderSnapshot { + environments: HashMap::from([("".to_string(), Environment::default_for_tests())]), + default: EnvironmentDefault::Disabled, + }, }; let err = EnvironmentManager::from_provider(&provider, test_runtime_paths()) + .await .expect_err("empty id should fail"); assert_eq!( @@ -464,21 +486,24 @@ mod tests { #[tokio::test] async fn environment_manager_uses_explicit_provider_default() { let provider = TestEnvironmentProvider { - environments: HashMap::from([ - ( - LOCAL_ENVIRONMENT_ID.to_string(), - Environment::default_for_tests(), - ), - ( - "devbox".to_string(), - Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string())) - .expect("remote environment"), - ), - ]), - default_environment_id: Some("devbox".to_string()), + snapshot: EnvironmentProviderSnapshot { + environments: HashMap::from([ + ( + LOCAL_ENVIRONMENT_ID.to_string(), + Environment::default_for_tests(), + ), + ( + "devbox".to_string(), + Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string())) + .expect("remote environment"), + ), + ]), + default: EnvironmentDefault::EnvironmentId("devbox".to_string()), + }, }; - let manager = - EnvironmentManager::from_provider(&provider, test_runtime_paths()).expect("manager"); + let manager = EnvironmentManager::from_provider(&provider, test_runtime_paths()) + .await + .expect("manager"); assert_eq!(manager.default_environment_id(), Some("devbox")); assert!(manager.default_environment().expect("default").is_remote()); @@ -487,14 +512,17 @@ mod tests { #[tokio::test] async fn environment_manager_disables_provider_default() { let provider = TestEnvironmentProvider { - environments: HashMap::from([( - LOCAL_ENVIRONMENT_ID.to_string(), - Environment::default_for_tests(), - )]), - default_environment_id: None, + snapshot: EnvironmentProviderSnapshot { + environments: HashMap::from([( + LOCAL_ENVIRONMENT_ID.to_string(), + Environment::default_for_tests(), + )]), + default: EnvironmentDefault::Disabled, + }, }; - let manager = - EnvironmentManager::from_provider(&provider, test_runtime_paths()).expect("manager"); + let manager = EnvironmentManager::from_provider(&provider, test_runtime_paths()) + .await + .expect("manager"); assert_eq!(manager.default_environment_id(), None); assert!(manager.default_environment().is_none()); @@ -504,13 +532,16 @@ mod tests { #[tokio::test] async fn environment_manager_rejects_unknown_provider_default() { let provider = TestEnvironmentProvider { - environments: HashMap::from([( - LOCAL_ENVIRONMENT_ID.to_string(), - Environment::default_for_tests(), - )]), - default_environment_id: Some("missing".to_string()), + snapshot: EnvironmentProviderSnapshot { + environments: HashMap::from([( + LOCAL_ENVIRONMENT_ID.to_string(), + Environment::default_for_tests(), + )]), + default: EnvironmentDefault::EnvironmentId("missing".to_string()), + }, }; let err = EnvironmentManager::from_provider(&provider, test_runtime_paths()) + .await .expect_err("unknown default should fail"); assert_eq!( @@ -524,7 +555,8 @@ mod tests { let manager = EnvironmentManager::create_for_tests( /*exec_server_url*/ None, test_runtime_paths(), - ); + ) + .await; assert_eq!(manager.default_environment_id(), Some(LOCAL_ENVIRONMENT_ID)); let provider_local = manager @@ -541,7 +573,8 @@ mod tests { let manager = EnvironmentManager::create_for_tests( /*exec_server_url*/ None, runtime_paths.clone(), - ); + ) + .await; let environment = manager.default_environment().expect("default environment"); @@ -552,7 +585,8 @@ mod tests { .local_runtime_paths() .expect("local runtime paths") .clone(), - ); + ) + .await; let environment = manager.default_environment().expect("default environment"); assert_eq!(environment.local_runtime_paths(), Some(&runtime_paths)); } @@ -568,7 +602,8 @@ mod tests { #[tokio::test] async fn environment_manager_keeps_default_provider_local_lookup_when_default_disabled() { let manager = - EnvironmentManager::create_for_tests(Some("none".to_string()), test_runtime_paths()); + EnvironmentManager::create_for_tests(Some("none".to_string()), test_runtime_paths()) + .await; assert!(manager.default_environment().is_none()); assert_eq!(manager.default_environment_id(), None); diff --git a/codex-rs/exec-server/src/environment_provider.rs b/codex-rs/exec-server/src/environment_provider.rs index 677a554480..0e4bcc5191 100644 --- a/codex-rs/exec-server/src/environment_provider.rs +++ b/codex-rs/exec-server/src/environment_provider.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use async_trait::async_trait; + use crate::Environment; use crate::ExecServerError; use crate::ExecServerRuntimePaths; @@ -9,19 +11,29 @@ use crate::environment::REMOTE_ENVIRONMENT_ID; /// Lists the concrete environments available to Codex. /// -/// Implementations own both the available environment list and the default -/// environment id. Providers that want the local environment to be addressable -/// by id should include it explicitly in the returned map. +/// Implementations own a startup snapshot containing both the available +/// environment list and default environment selection. Providers that want the +/// local environment to be addressable by id should include it explicitly in +/// the returned map. +#[async_trait] pub trait EnvironmentProvider: Send + Sync { - /// Returns the environments available for a new manager. - fn get_environments( + /// Returns the provider-owned environment startup snapshot. + async fn snapshot( &self, local_runtime_paths: &ExecServerRuntimePaths, - ) -> Result, ExecServerError>; + ) -> Result; +} - /// Returns the provider-selected default environment id, or `None` to - /// disable model-facing environment access. - fn default_environment_id(&self) -> Option; +#[derive(Clone, Debug)] +pub struct EnvironmentProviderSnapshot { + pub environments: HashMap, + pub default: EnvironmentDefault, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum EnvironmentDefault { + Disabled, + EnvironmentId(String), } /// Default provider backed by `CODEX_EXEC_SERVER_URL`. @@ -41,15 +53,15 @@ impl DefaultEnvironmentProvider { Self::new(std::env::var(CODEX_EXEC_SERVER_URL_ENV_VAR).ok()) } - pub(crate) fn environments( + pub(crate) fn snapshot_inner( &self, local_runtime_paths: &ExecServerRuntimePaths, - ) -> HashMap { + ) -> EnvironmentProviderSnapshot { let mut environments = HashMap::from([( LOCAL_ENVIRONMENT_ID.to_string(), Environment::local(local_runtime_paths.clone()), )]); - let (exec_server_url, _disabled) = normalize_exec_server_url(self.exec_server_url.clone()); + let (exec_server_url, disabled) = normalize_exec_server_url(self.exec_server_url.clone()); if let Some(exec_server_url) = exec_server_url { environments.insert( @@ -58,32 +70,28 @@ impl DefaultEnvironmentProvider { ); } - environments + let default = if disabled { + EnvironmentDefault::Disabled + } else if environments.contains_key(REMOTE_ENVIRONMENT_ID) { + EnvironmentDefault::EnvironmentId(REMOTE_ENVIRONMENT_ID.to_string()) + } else { + EnvironmentDefault::EnvironmentId(LOCAL_ENVIRONMENT_ID.to_string()) + }; + + EnvironmentProviderSnapshot { + environments, + default, + } } } +#[async_trait] impl EnvironmentProvider for DefaultEnvironmentProvider { - fn get_environments( + async fn snapshot( &self, local_runtime_paths: &ExecServerRuntimePaths, - ) -> Result, ExecServerError> { - Ok(self.environments(local_runtime_paths)) - } - - fn default_environment_id(&self) -> Option { - let (exec_server_url, disabled) = normalize_exec_server_url(self.exec_server_url.clone()); - if disabled { - return None; - } - - Some( - if exec_server_url.is_some() { - REMOTE_ENVIRONMENT_ID - } else { - LOCAL_ENVIRONMENT_ID - } - .to_string(), - ) + ) -> Result { + Ok(self.snapshot_inner(local_runtime_paths)) } } @@ -114,9 +122,11 @@ mod tests { async fn default_provider_returns_local_environment_when_url_is_missing() { let provider = DefaultEnvironmentProvider::new(/*exec_server_url*/ None); let runtime_paths = test_runtime_paths(); - let environments = provider - .get_environments(&runtime_paths) + let snapshot = provider + .snapshot(&runtime_paths) + .await .expect("environments"); + let environments = snapshot.environments; assert!(!environments[LOCAL_ENVIRONMENT_ID].is_remote()); assert_eq!( @@ -125,8 +135,8 @@ mod tests { ); assert!(!environments.contains_key(REMOTE_ENVIRONMENT_ID)); assert_eq!( - provider.default_environment_id(), - Some(LOCAL_ENVIRONMENT_ID.to_string()) + snapshot.default, + EnvironmentDefault::EnvironmentId(LOCAL_ENVIRONMENT_ID.to_string()) ); } @@ -134,15 +144,17 @@ mod tests { async fn default_provider_returns_local_environment_when_url_is_empty() { let provider = DefaultEnvironmentProvider::new(Some(String::new())); let runtime_paths = test_runtime_paths(); - let environments = provider - .get_environments(&runtime_paths) + let snapshot = provider + .snapshot(&runtime_paths) + .await .expect("environments"); + let environments = snapshot.environments; assert!(!environments[LOCAL_ENVIRONMENT_ID].is_remote()); assert!(!environments.contains_key(REMOTE_ENVIRONMENT_ID)); assert_eq!( - provider.default_environment_id(), - Some(LOCAL_ENVIRONMENT_ID.to_string()) + snapshot.default, + EnvironmentDefault::EnvironmentId(LOCAL_ENVIRONMENT_ID.to_string()) ); } @@ -150,22 +162,26 @@ mod tests { async fn default_provider_returns_local_environment_for_none_value() { let provider = DefaultEnvironmentProvider::new(Some("none".to_string())); let runtime_paths = test_runtime_paths(); - let environments = provider - .get_environments(&runtime_paths) + let snapshot = provider + .snapshot(&runtime_paths) + .await .expect("environments"); + let environments = snapshot.environments; assert!(!environments[LOCAL_ENVIRONMENT_ID].is_remote()); assert!(!environments.contains_key(REMOTE_ENVIRONMENT_ID)); - assert_eq!(provider.default_environment_id(), None); + assert_eq!(snapshot.default, EnvironmentDefault::Disabled); } #[tokio::test] async fn default_provider_adds_remote_environment_for_websocket_url() { let provider = DefaultEnvironmentProvider::new(Some("ws://127.0.0.1:8765".to_string())); let runtime_paths = test_runtime_paths(); - let environments = provider - .get_environments(&runtime_paths) + let snapshot = provider + .snapshot(&runtime_paths) + .await .expect("environments"); + let environments = snapshot.environments; assert!(!environments[LOCAL_ENVIRONMENT_ID].is_remote()); let remote_environment = &environments[REMOTE_ENVIRONMENT_ID]; @@ -175,8 +191,8 @@ mod tests { Some("ws://127.0.0.1:8765") ); assert_eq!( - provider.default_environment_id(), - Some(REMOTE_ENVIRONMENT_ID.to_string()) + snapshot.default, + EnvironmentDefault::EnvironmentId(REMOTE_ENVIRONMENT_ID.to_string()) ); } @@ -185,11 +201,12 @@ mod tests { let provider = DefaultEnvironmentProvider::new(Some(" ws://127.0.0.1:8765 ".to_string())); let runtime_paths = test_runtime_paths(); let environments = provider - .get_environments(&runtime_paths) + .snapshot(&runtime_paths) + .await .expect("environments"); assert_eq!( - environments[REMOTE_ENVIRONMENT_ID].exec_server_url(), + environments.environments[REMOTE_ENVIRONMENT_ID].exec_server_url(), Some("ws://127.0.0.1:8765") ); } diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index a8fba18f4a..b035a19517 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -518,9 +518,9 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result feedback: CodexFeedback::new(), log_db: None, state_db: state_db.clone(), - environment_manager: std::sync::Arc::new(EnvironmentManager::new( - EnvironmentManagerArgs::new(local_runtime_paths), - )), + environment_manager: std::sync::Arc::new( + EnvironmentManager::new(EnvironmentManagerArgs::new(local_runtime_paths)).await, + ), config_warnings, session_source: SessionSource::Exec, enable_codex_api_key_env: true, diff --git a/codex-rs/mcp-server/src/lib.rs b/codex-rs/mcp-server/src/lib.rs index 63ddd0c3e5..2fd94e586a 100644 --- a/codex-rs/mcp-server/src/lib.rs +++ b/codex-rs/mcp-server/src/lib.rs @@ -61,12 +61,15 @@ pub async fn run_main( arg0_paths: Arg0DispatchPaths, cli_config_overrides: CliConfigOverrides, ) -> IoResult<()> { - let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::new( - ExecServerRuntimePaths::from_optional_paths( - arg0_paths.codex_self_exe.clone(), - arg0_paths.codex_linux_sandbox_exe.clone(), - )?, - ))); + let environment_manager = Arc::new( + EnvironmentManager::new(EnvironmentManagerArgs::new( + ExecServerRuntimePaths::from_optional_paths( + arg0_paths.codex_self_exe.clone(), + arg0_paths.codex_linux_sandbox_exe.clone(), + )?, + )) + .await, + ); // Parse CLI overrides once and derive the base Config eagerly so later // components do not need to work with raw TOML values. let cli_kv_overrides = cli_config_overrides.parse_overrides().map_err(|e| { diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 5bcc6ffa26..b2e92a19b4 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -761,12 +761,15 @@ pub async fn run_main( } }; - let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::new( - ExecServerRuntimePaths::from_optional_paths( - arg0_paths.codex_self_exe.clone(), - arg0_paths.codex_linux_sandbox_exe.clone(), - )?, - ))); + let environment_manager = Arc::new( + EnvironmentManager::new(EnvironmentManagerArgs::new( + ExecServerRuntimePaths::from_optional_paths( + arg0_paths.codex_self_exe.clone(), + arg0_paths.codex_linux_sandbox_exe.clone(), + )?, + )) + .await, + ); let cwd = cli.cwd.clone(); let config_cwd = config_cwd_for_app_server_target(cwd.as_deref(), &app_server_target, &environment_manager)?; @@ -2138,7 +2141,8 @@ mod tests { std::env::current_exe().expect("current exe"), /*codex_linux_sandbox_exe*/ None, )?, - ); + ) + .await; let config_cwd = config_cwd_for_app_server_target(Some(remote_only_cwd), &target, &environment_manager)?;