From 8e103c9c8a5b17d6545da63226d1252e249abb45 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sat, 14 Mar 2026 12:05:03 -0600 Subject: [PATCH] Part 4 --- codex-rs/app-server-client/src/lib.rs | 6 +-- codex-rs/app-server/src/in_process.rs | 6 +-- codex-rs/app-server/src/lib.rs | 17 ++----- codex-rs/app-server/src/message_processor.rs | 11 ++++- .../src/message_processor/tracing_tests.rs | 3 +- codex-rs/app-server/src/runtime_bootstrap.rs | 33 +++++++++++++ codex-rs/exec/src/lib.rs | 2 - codex-rs/tui/src/app.rs | 2 - codex-rs/tui/src/lib.rs | 46 +++++-------------- 9 files changed, 60 insertions(+), 66 deletions(-) create mode 100644 codex-rs/app-server/src/runtime_bootstrap.rs diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index ff5a6087c6..bd5f40cbc8 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -26,6 +26,7 @@ use std::time::Duration; pub use codex_app_server::in_process::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY; pub use codex_app_server::in_process::InProcessServerEvent; use codex_app_server::in_process::InProcessStartArgs; +pub use codex_app_server::shared_cloud_requirements_loader; use codex_app_server_protocol::ClientInfo; use codex_app_server_protocol::ClientNotification; use codex_app_server_protocol::ClientRequest; @@ -39,7 +40,6 @@ use codex_arg0::Arg0DispatchPaths; use codex_core::AuthManager; use codex_core::ThreadManager; use codex_core::config::Config; -use codex_core::config_loader::CloudRequirementsLoader; use codex_core::config_loader::LoaderOverrides; use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig; use codex_feedback::CodexFeedback; @@ -146,8 +146,6 @@ pub struct InProcessClientStartArgs { pub cli_overrides: Vec<(String, TomlValue)>, /// Loader override knobs used by config API paths. pub loader_overrides: LoaderOverrides, - /// Preloaded cloud requirements provider. - pub cloud_requirements: CloudRequirementsLoader, /// Feedback sink used by app-server/core telemetry and logs. pub feedback: CodexFeedback, /// Startup warnings emitted after initialize succeeds. @@ -221,7 +219,6 @@ impl InProcessClientStartArgs { config: self.config, cli_overrides: self.cli_overrides, loader_overrides: self.loader_overrides, - cloud_requirements: self.cloud_requirements, auth_manager: Some(shared_core.auth_manager.clone()), thread_manager: Some(shared_core.thread_manager.clone()), feedback: self.feedback, @@ -686,7 +683,6 @@ mod tests { config: Arc::new(build_test_config().await), cli_overrides: Vec::new(), loader_overrides: LoaderOverrides::default(), - cloud_requirements: CloudRequirementsLoader::default(), feedback: CodexFeedback::new(), config_warnings: Vec::new(), session_source, diff --git a/codex-rs/app-server/src/in_process.rs b/codex-rs/app-server/src/in_process.rs index 3a9286a5f4..f371449597 100644 --- a/codex-rs/app-server/src/in_process.rs +++ b/codex-rs/app-server/src/in_process.rs @@ -77,7 +77,6 @@ use codex_arg0::Arg0DispatchPaths; use codex_core::AuthManager; use codex_core::ThreadManager; use codex_core::config::Config; -use codex_core::config_loader::CloudRequirementsLoader; use codex_core::config_loader::LoaderOverrides; use codex_feedback::CodexFeedback; use codex_protocol::protocol::SessionSource; @@ -122,8 +121,6 @@ pub struct InProcessStartArgs { pub cli_overrides: Vec<(String, TomlValue)>, /// Loader override knobs used by config API paths. pub loader_overrides: LoaderOverrides, - /// Preloaded cloud requirements provider. - pub cloud_requirements: CloudRequirementsLoader, /// Optional prebuilt auth manager reused by an embedding caller. pub auth_manager: Option>, /// Optional prebuilt thread manager reused by an embedding caller. @@ -409,7 +406,7 @@ fn start_uninitialized(args: InProcessStartArgs) -> InProcessClientHandle { config: args.config, cli_overrides: args.cli_overrides, loader_overrides: args.loader_overrides, - cloud_requirements: args.cloud_requirements, + cloud_requirements: None, auth_manager: args.auth_manager, thread_manager: args.thread_manager, feedback: args.feedback, @@ -757,7 +754,6 @@ mod tests { config: Arc::new(build_test_config().await), cli_overrides: Vec::new(), loader_overrides: LoaderOverrides::default(), - cloud_requirements: CloudRequirementsLoader::default(), auth_manager: None, thread_manager: None, feedback: CodexFeedback::new(), diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index 8bd772d3e5..648d680201 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -1,8 +1,6 @@ #![deny(clippy::print_stdout, clippy::print_stderr)] use codex_arg0::Arg0DispatchPaths; -use codex_cloud_requirements::cloud_requirements_loader; -use codex_core::AuthManager; use codex_core::config::Config; use codex_core::config::ConfigBuilder; use codex_core::config_loader::CloudRequirementsLoader; @@ -71,6 +69,7 @@ pub mod in_process; mod message_processor; mod models; mod outgoing_message; +mod runtime_bootstrap; mod server_request_error; mod thread_state; mod thread_status; @@ -78,6 +77,7 @@ mod transport; pub use crate::error_code::INPUT_TOO_LARGE_ERROR_CODE; pub use crate::error_code::INVALID_PARAMS_ERROR_CODE; +pub use crate::runtime_bootstrap::shared_cloud_requirements_loader; pub use crate::transport::AppServerTransport; const LOG_FORMAT_ENV_VAR: &str = "LOG_FORMAT"; @@ -416,16 +416,7 @@ pub async fn run_main_with_transport( } } - let auth_manager = AuthManager::shared( - config.codex_home.clone(), - false, - config.cli_auth_credentials_store_mode, - ); - cloud_requirements_loader( - auth_manager, - config.chatgpt_base_url, - config.codex_home.clone(), - ) + runtime_bootstrap::shared_cloud_requirements_loader_for_config(&config, false) } Err(err) => { warn!(error = %err, "Failed to preload config for cloud requirements"); @@ -607,7 +598,7 @@ pub async fn run_main_with_transport( config: Arc::new(config), cli_overrides, loader_overrides, - cloud_requirements: cloud_requirements.clone(), + cloud_requirements: Some(cloud_requirements.clone()), auth_manager: None, thread_manager: None, feedback: feedback.clone(), diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index e16e2e693e..b049dcd816 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -47,6 +47,7 @@ use codex_app_server_protocol::ServerNotification; use codex_app_server_protocol::ServerRequestPayload; use codex_app_server_protocol::experimental_required_message; use codex_arg0::Arg0DispatchPaths; +use codex_cloud_requirements::cloud_requirements_loader; use codex_core::AnalyticsEventsClient; use codex_core::AuthManager; use codex_core::ThreadManager; @@ -168,7 +169,7 @@ pub(crate) struct MessageProcessorArgs { pub(crate) config: Arc, pub(crate) cli_overrides: Vec<(String, TomlValue)>, pub(crate) loader_overrides: LoaderOverrides, - pub(crate) cloud_requirements: CloudRequirementsLoader, + pub(crate) cloud_requirements: Option, pub(crate) auth_manager: Option>, pub(crate) thread_manager: Option>, pub(crate) feedback: CodexFeedback, @@ -232,7 +233,13 @@ impl MessageProcessor { thread_manager .plugins_manager() .maybe_start_curated_repo_sync_for_config(&config); - let cloud_requirements = Arc::new(RwLock::new(cloud_requirements)); + let cloud_requirements = Arc::new(RwLock::new(cloud_requirements.unwrap_or_else(|| { + cloud_requirements_loader( + auth_manager.clone(), + config.chatgpt_base_url.clone(), + config.codex_home.clone(), + ) + }))); let codex_message_processor = CodexMessageProcessor::new(CodexMessageProcessorArgs { auth_manager: auth_manager.clone(), thread_manager: Arc::clone(&thread_manager), diff --git a/codex-rs/app-server/src/message_processor/tracing_tests.rs b/codex-rs/app-server/src/message_processor/tracing_tests.rs index e39484cedb..dcb99e3bdb 100644 --- a/codex-rs/app-server/src/message_processor/tracing_tests.rs +++ b/codex-rs/app-server/src/message_processor/tracing_tests.rs @@ -22,7 +22,6 @@ use codex_app_server_protocol::UserInput; use codex_arg0::Arg0DispatchPaths; use codex_core::config::Config; use codex_core::config::ConfigBuilder; -use codex_core::config_loader::CloudRequirementsLoader; use codex_core::config_loader::LoaderOverrides; use codex_feedback::CodexFeedback; use codex_protocol::protocol::SessionSource; @@ -238,7 +237,7 @@ fn build_test_processor( config, cli_overrides: Vec::new(), loader_overrides: LoaderOverrides::default(), - cloud_requirements: CloudRequirementsLoader::default(), + cloud_requirements: None, auth_manager: None, thread_manager: None, feedback: CodexFeedback::new(), diff --git a/codex-rs/app-server/src/runtime_bootstrap.rs b/codex-rs/app-server/src/runtime_bootstrap.rs new file mode 100644 index 0000000000..a0d5ab26f7 --- /dev/null +++ b/codex-rs/app-server/src/runtime_bootstrap.rs @@ -0,0 +1,33 @@ +use std::path::PathBuf; + +use codex_cloud_requirements::cloud_requirements_loader; +use codex_core::AuthManager; +use codex_core::auth::AuthCredentialsStoreMode; +use codex_core::config::Config; +use codex_core::config_loader::CloudRequirementsLoader; + +pub fn shared_cloud_requirements_loader( + codex_home: PathBuf, + enable_codex_api_key_env: bool, + credentials_store_mode: AuthCredentialsStoreMode, + chatgpt_base_url: String, +) -> CloudRequirementsLoader { + let auth_manager = AuthManager::shared( + codex_home.clone(), + enable_codex_api_key_env, + credentials_store_mode, + ); + cloud_requirements_loader(auth_manager, chatgpt_base_url, codex_home) +} + +pub fn shared_cloud_requirements_loader_for_config( + config: &Config, + enable_codex_api_key_env: bool, +) -> CloudRequirementsLoader { + shared_cloud_requirements_loader( + config.codex_home.clone(), + enable_codex_api_key_env, + config.cli_auth_credentials_store_mode, + config.chatgpt_base_url.clone(), + ) +} diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index df1601e15d..87c40ee73d 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -300,7 +300,6 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result cloud_requirements_loader(cloud_auth_manager, chatgpt_base_url, codex_home.clone()); let run_cli_overrides = cli_kv_overrides.clone(); let run_loader_overrides = LoaderOverrides::default(); - let run_cloud_requirements = cloud_requirements.clone(); let model_provider = if oss { let resolved = resolve_oss_provider( @@ -434,7 +433,6 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result config: std::sync::Arc::new(config.clone()), cli_overrides: run_cli_overrides, loader_overrides: run_loader_overrides, - cloud_requirements: run_cloud_requirements, feedback: CodexFeedback::new(), config_warnings, session_source: SessionSource::Exec, diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 924db6cd90..f943e38771 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -4225,7 +4225,6 @@ mod tests { use codex_core::config::ConfigBuilder; use codex_core::config::ConfigOverrides; use codex_core::config::types::ModelAvailabilityNuxConfig; - use codex_core::config_loader::CloudRequirementsLoader; use codex_core::config_loader::LoaderOverrides; use codex_otel::SessionTelemetry; use codex_protocol::ThreadId; @@ -4265,7 +4264,6 @@ mod tests { config: Arc::new(config), cli_overrides: Vec::new(), loader_overrides: LoaderOverrides::default(), - cloud_requirements: CloudRequirementsLoader::default(), feedback: codex_feedback::CodexFeedback::new(), config_warnings: Vec::new(), session_source: SessionSource::Cli, diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 8f906c15dd..d0bd596cc3 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -10,9 +10,8 @@ pub use app::ExitReason; use codex_app_server_client::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY; use codex_app_server_client::InProcessAppServerClient; use codex_app_server_client::InProcessClientStartArgs; +use codex_app_server_client::shared_cloud_requirements_loader; use codex_app_server_protocol::ConfigWarningNotification; -use codex_cloud_requirements::cloud_requirements_loader; -use codex_core::AuthManager; use codex_core::INTERACTIVE_SESSION_SOURCES; use codex_core::RolloutRecorder; use codex_core::ThreadSortKey; @@ -242,7 +241,6 @@ async fn start_embedded_app_server( config: Config, cli_kv_overrides: Vec<(String, toml::Value)>, loader_overrides: LoaderOverrides, - cloud_requirements: CloudRequirementsLoader, feedback: codex_feedback::CodexFeedback, ) -> color_eyre::Result { start_embedded_app_server_with( @@ -250,7 +248,6 @@ async fn start_embedded_app_server( config, cli_kv_overrides, loader_overrides, - cloud_requirements, feedback, InProcessAppServerClient::start, ) @@ -262,7 +259,6 @@ async fn start_embedded_app_server_with( config: Config, cli_kv_overrides: Vec<(String, toml::Value)>, loader_overrides: LoaderOverrides, - cloud_requirements: CloudRequirementsLoader, feedback: codex_feedback::CodexFeedback, start_client: F, ) -> color_eyre::Result @@ -285,7 +281,6 @@ where config: Arc::new(config), cli_overrides: cli_kv_overrides, loader_overrides, - cloud_requirements, feedback, config_warnings, session_source: codex_protocol::protocol::SessionSource::Cli, @@ -394,19 +389,15 @@ pub async fn run_main( tracing::warn!(error = %err, "failed to run personality migration"); } - let cloud_auth_manager = AuthManager::shared( - codex_home.to_path_buf(), - false, - config_toml.cli_auth_credentials_store.unwrap_or_default(), - ); let chatgpt_base_url = config_toml .chatgpt_base_url .clone() .unwrap_or_else(|| "https://chatgpt.com/backend-api/".to_string()); - let cloud_requirements = cloud_requirements_loader( - cloud_auth_manager, - chatgpt_base_url, + let cloud_requirements = shared_cloud_requirements_loader( codex_home.to_path_buf(), + false, + config_toml.cli_auth_credentials_store.unwrap_or_default(), + chatgpt_base_url, ); let model_provider_override = if cli.oss { @@ -670,7 +661,6 @@ async fn run_ratatui_app( initial_config.clone(), cli_kv_overrides.clone(), loader_overrides.clone(), - cloud_requirements.clone(), feedback.clone(), ) .await @@ -745,14 +735,11 @@ async fn run_ratatui_app( // rebuild config. This avoids missing newly available cloud requirements due to login // status detection edge cases. if show_login_screen { - cloud_requirements = cloud_requirements_loader( - AuthManager::shared( - initial_config.codex_home.clone(), - false, - initial_config.cli_auth_credentials_store_mode, - ), - initial_config.chatgpt_base_url.clone(), + cloud_requirements = shared_cloud_requirements_loader( initial_config.codex_home.clone(), + false, + initial_config.cli_auth_credentials_store_mode, + initial_config.chatgpt_base_url.clone(), ); } @@ -1058,7 +1045,6 @@ async fn run_ratatui_app( config.clone(), cli_kv_overrides.clone(), loader_overrides, - cloud_requirements.clone(), feedback.clone(), ) .await @@ -1340,7 +1326,6 @@ mod tests { config, Vec::new(), LoaderOverrides::default(), - CloudRequirementsLoader::default(), codex_feedback::CodexFeedback::new(), ) .await @@ -1363,20 +1348,12 @@ mod tests { } #[tokio::test] - async fn embedded_app_server_exposes_client_manager_accessors() -> color_eyre::Result<()> { + async fn embedded_app_server_starts_without_preloaded_cloud_requirements() + -> color_eyre::Result<()> { let temp_dir = TempDir::new()?; let config = build_config(&temp_dir).await?; let app_server = start_test_embedded_app_server(config).await?; - assert!(Arc::ptr_eq( - &app_server.auth_manager(), - &app_server.auth_manager() - )); - assert!(Arc::ptr_eq( - &app_server.thread_manager(), - &app_server.thread_manager() - )); - app_server.shutdown().await?; Ok(()) } @@ -1390,7 +1367,6 @@ mod tests { config, Vec::new(), LoaderOverrides::default(), - CloudRequirementsLoader::default(), codex_feedback::CodexFeedback::new(), |_args| async { Err(std::io::Error::other("boom")) }, )