diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 71ed77e9e242..0ae00484368d 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2163,6 +2163,7 @@ dependencies = [ "codex-core", "codex-login", "codex-model-provider", + "codex-protocol", "codex-state", "codex-uds", "codex-utils-absolute-path", @@ -3263,7 +3264,6 @@ dependencies = [ "base64 0.22.1", "chrono", "codex-agent-identity", - "codex-app-server-protocol", "codex-client", "codex-config", "codex-keyring-store", @@ -3498,7 +3498,6 @@ name = "codex-model-provider-info" version = "0.0.0" dependencies = [ "codex-api", - "codex-app-server-protocol", "codex-protocol", "codex-utils-absolute-path", "http 1.4.0", @@ -3515,7 +3514,6 @@ name = "codex-models-manager" version = "0.0.0" dependencies = [ "chrono", - "codex-app-server-protocol", "codex-collaboration-mode-templates", "codex-login", "codex-otel", @@ -3591,7 +3589,6 @@ version = "0.0.0" dependencies = [ "chrono", "codex-api", - "codex-app-server-protocol", "codex-protocol", "codex-utils-absolute-path", "codex-utils-string", diff --git a/codex-rs/app-server-transport/Cargo.toml b/codex-rs/app-server-transport/Cargo.toml index 7ad9444921e2..d4c0a83739c4 100644 --- a/codex-rs/app-server-transport/Cargo.toml +++ b/codex-rs/app-server-transport/Cargo.toml @@ -55,6 +55,7 @@ uuid = { workspace = true, features = ["serde", "v7"] } [dev-dependencies] chrono = { workspace = true } codex-config = { workspace = true } +codex-protocol = { workspace = true } pretty_assertions = { workspace = true } tempfile = { workspace = true } tokio = { workspace = true, features = ["test-util"] } diff --git a/codex-rs/app-server-transport/src/transport/remote_control/tests.rs b/codex-rs/app-server-transport/src/transport/remote_control/tests.rs index 44299c4b5723..a506b18841e3 100644 --- a/codex-rs/app-server-transport/src/transport/remote_control/tests.rs +++ b/codex-rs/app-server-transport/src/transport/remote_control/tests.rs @@ -18,7 +18,6 @@ use crate::transport::CHANNEL_CAPACITY; use crate::transport::ConnectionOrigin; use crate::transport::TransportEvent; use base64::Engine; -use codex_app_server_protocol::AuthMode; use codex_app_server_protocol::ConfigWarningNotification; use codex_app_server_protocol::JSONRPCMessage; use codex_app_server_protocol::RemoteControlConnectionStatus; @@ -36,6 +35,7 @@ use codex_login::CodexAuth; use codex_login::save_auth; use codex_login::token_data::TokenData; use codex_login::token_data::parse_chatgpt_jwt_claims; +use codex_protocol::auth::AuthMode; use codex_state::RemoteControlEnrollmentRecord; use codex_state::StateRuntime; use futures::SinkExt; diff --git a/codex-rs/app-server-transport/src/transport/remote_control/websocket.rs b/codex-rs/app-server-transport/src/transport/remote_control/websocket.rs index b9f1c7175209..e34cd3409153 100644 --- a/codex-rs/app-server-transport/src/transport/remote_control/websocket.rs +++ b/codex-rs/app-server-transport/src/transport/remote_control/websocket.rs @@ -1813,7 +1813,6 @@ mod tests { use crate::transport::remote_control::protocol::StreamId; use crate::transport::remote_control::protocol::normalize_remote_control_url; use chrono::Utc; - use codex_app_server_protocol::AuthMode; use codex_app_server_protocol::ConfigWarningNotification; use codex_app_server_protocol::JSONRPCMessage; use codex_app_server_protocol::JSONRPCNotification; @@ -1826,6 +1825,7 @@ mod tests { use codex_login::save_auth; use codex_login::token_data::TokenData; use codex_login::token_data::parse_chatgpt_jwt_claims; + use codex_protocol::auth::AuthMode; use codex_state::StateRuntime; use futures::StreamExt; use pretty_assertions::assert_eq; diff --git a/codex-rs/app-server/src/auth_mode.rs b/codex-rs/app-server/src/auth_mode.rs new file mode 100644 index 000000000000..67d3ee61b985 --- /dev/null +++ b/codex-rs/app-server/src/auth_mode.rs @@ -0,0 +1,19 @@ +use codex_app_server_protocol::AuthMode as ApiAuthMode; +use codex_protocol::auth::AuthMode; + +/// Converts the domain auth mode owned by `codex-protocol` into the app-server wire type owned by +/// `codex-app-server-protocol`. +/// +/// The types stay separate so app-server protocol ownership does not leak into domain crates. +/// Because this crate owns neither type, Rust's orphan rules require an explicit conversion +/// function instead of a `From` implementation. +pub(crate) fn auth_mode_to_api(auth_mode: AuthMode) -> ApiAuthMode { + match auth_mode { + AuthMode::ApiKey => ApiAuthMode::ApiKey, + AuthMode::Chatgpt => ApiAuthMode::Chatgpt, + AuthMode::ChatgptAuthTokens => ApiAuthMode::ChatgptAuthTokens, + AuthMode::AgentIdentity => ApiAuthMode::AgentIdentity, + AuthMode::PersonalAccessToken => ApiAuthMode::PersonalAccessToken, + AuthMode::BedrockApiKey => ApiAuthMode::BedrockApiKey, + } +} diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index 9eb0009464eb..0a237de9c312 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -82,6 +82,7 @@ const SQLITE_RECOVERY_CONFIG_WARNING_SUMMARY: &str = "Codex rebuilt its local da mod analytics_utils; mod app_server_tracing; mod attestation; +mod auth_mode; mod bespoke_event_handling; mod command_exec; mod config; diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 17f34a7b2980..7c9f006d2657 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -50,7 +50,6 @@ use crate::transport::AppServerTransport; use crate::transport::RemoteControlHandle; use codex_analytics::AnalyticsEventsClient; use codex_analytics::AppServerRpcTransport; -use codex_app_server_protocol::AuthMode as LoginAuthMode; use codex_app_server_protocol::ChatgptAuthTokensRefreshParams; use codex_app_server_protocol::ChatgptAuthTokensRefreshReason; use codex_app_server_protocol::ChatgptAuthTokensRefreshResponse; @@ -80,6 +79,7 @@ use codex_login::auth::ExternalAuthRefreshContext; use codex_login::auth::ExternalAuthRefreshReason; use codex_login::auth::ExternalAuthTokens; use codex_protocol::ThreadId; +use codex_protocol::auth::AuthMode as LoginAuthMode; use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::W3cTraceContext; use codex_rollout::StateDbHandle; diff --git a/codex-rs/app-server/src/request_processors/account_processor.rs b/codex-rs/app-server/src/request_processors/account_processor.rs index 6aa624153de4..4691e222044c 100644 --- a/codex-rs/app-server/src/request_processors/account_processor.rs +++ b/codex-rs/app-server/src/request_processors/account_processor.rs @@ -1,4 +1,5 @@ use super::*; +use crate::auth_mode::auth_mode_to_api; use chrono::DateTime; mod rate_limit_resets; @@ -179,7 +180,10 @@ impl AccountRequestProcessor { fn current_account_updated_notification(&self) -> AccountUpdatedNotification { let auth = self.auth_manager.auth_cached(); AccountUpdatedNotification { - auth_mode: auth.as_ref().map(CodexAuth::api_auth_mode), + auth_mode: auth + .as_ref() + .map(CodexAuth::api_auth_mode) + .map(auth_mode_to_api), plan_type: auth.as_ref().and_then(CodexAuth::account_plan_type), } } @@ -693,7 +697,10 @@ impl AccountRequestProcessor { ) .await; let payload_v2 = AccountUpdatedNotification { - auth_mode: auth.as_ref().map(CodexAuth::api_auth_mode), + auth_mode: auth + .as_ref() + .map(CodexAuth::api_auth_mode) + .map(auth_mode_to_api), plan_type: auth.as_ref().and_then(CodexAuth::account_plan_type), }; outgoing @@ -730,7 +737,8 @@ impl AccountRequestProcessor { .auth_manager .auth_cached() .as_ref() - .map(CodexAuth::api_auth_mode)) + .map(CodexAuth::api_auth_mode) + .map(auth_mode_to_api)) } async fn logout_v2(&self, request_id: ConnectionRequestId) -> Result<(), JSONRPCErrorError> { @@ -801,7 +809,7 @@ impl AccountRequestProcessor { Some(auth) => { let permanent_refresh_failure = self.auth_manager.refresh_failure_for_auth(&auth).is_some(); - let auth_mode = auth.api_auth_mode(); + let auth_mode = auth_mode_to_api(auth.api_auth_mode()); let (reported_auth_method, token_opt) = if matches!( auth, CodexAuth::AgentIdentity(_) | CodexAuth::PersonalAccessToken(_) diff --git a/codex-rs/app-server/src/request_processors/plugins.rs b/codex-rs/app-server/src/request_processors/plugins.rs index eb68feb544f7..6008c0dddb4a 100644 --- a/codex-rs/app-server/src/request_processors/plugins.rs +++ b/codex-rs/app-server/src/request_processors/plugins.rs @@ -24,6 +24,7 @@ use codex_mcp::oauth_login_support; use codex_mcp::should_retry_without_scopes; use codex_plugin::PluginId; use codex_plugin::PluginTelemetryMetadata; +use codex_protocol::auth::AuthMode as DomainAuthMode; use codex_rmcp_client::perform_oauth_login_silent; #[derive(Clone)] @@ -567,7 +568,7 @@ impl PluginRequestProcessor { let include_global_remote = !explicit_marketplace_kinds && config.features.enabled(Feature::RemotePlugin); let use_remote_global_catalog = - include_global_remote && auth_mode.is_some_and(AuthMode::uses_codex_backend); + include_global_remote && auth_mode.is_some_and(DomainAuthMode::uses_codex_backend); let remote_plugin_service_config = RemotePluginServiceConfig { chatgpt_base_url: config.chatgpt_base_url.clone(), }; diff --git a/codex-rs/app-server/tests/common/auth_fixtures.rs b/codex-rs/app-server/tests/common/auth_fixtures.rs index ed3fe923a7fd..d68a49c1a1d0 100644 --- a/codex-rs/app-server/tests/common/auth_fixtures.rs +++ b/codex-rs/app-server/tests/common/auth_fixtures.rs @@ -6,13 +6,13 @@ use base64::Engine; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use chrono::DateTime; use chrono::Utc; -use codex_app_server_protocol::AuthMode; use codex_config::types::AuthCredentialsStoreMode; use codex_login::AuthDotJson; use codex_login::AuthKeyringBackendKind; use codex_login::save_auth; use codex_login::token_data::TokenData; use codex_login::token_data::parse_chatgpt_jwt_claims; +use codex_protocol::auth::AuthMode; use serde_json::json; /// Builder for writing a fake ChatGPT auth.json in tests. diff --git a/codex-rs/app-server/tests/suite/v2/app_list.rs b/codex-rs/app-server/tests/suite/v2/app_list.rs index daface3447a2..bb28190cf709 100644 --- a/codex-rs/app-server/tests/suite/v2/app_list.rs +++ b/codex-rs/app-server/tests/suite/v2/app_list.rs @@ -27,7 +27,6 @@ use codex_app_server_protocol::AppReview; use codex_app_server_protocol::AppScreenshot; use codex_app_server_protocol::AppsListParams; use codex_app_server_protocol::AppsListResponse; -use codex_app_server_protocol::AuthMode; use codex_app_server_protocol::JSONRPCError; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::RequestId; @@ -38,6 +37,7 @@ use codex_config::types::AuthCredentialsStoreMode; use codex_login::AuthDotJson; use codex_login::AuthKeyringBackendKind; use codex_login::save_auth; +use codex_protocol::auth::AuthMode; use pretty_assertions::assert_eq; use rmcp::handler::server::ServerHandler; use rmcp::model::JsonObject; diff --git a/codex-rs/cli/src/doctor.rs b/codex-rs/cli/src/doctor.rs index 5f60a96ff3f3..ca25b3b1cfc2 100644 --- a/codex-rs/cli/src/doctor.rs +++ b/codex-rs/cli/src/doctor.rs @@ -53,6 +53,7 @@ use codex_login::default_client::build_reqwest_client; use codex_login::default_client::default_headers; use codex_login::load_auth_dot_json; use codex_model_provider::create_model_provider; +use codex_protocol::auth::AuthMode; use codex_protocol::protocol::AskForApproval; use codex_terminal_detection::Multiplexer; use codex_terminal_detection::TerminalInfo; @@ -1323,27 +1324,27 @@ fn provider_specific_auth_check( fn stored_auth_mode(auth: &codex_login::AuthDotJson) -> &'static str { match stored_auth_mode_value(auth) { - codex_app_server_protocol::AuthMode::ApiKey => "api_key", - codex_app_server_protocol::AuthMode::Chatgpt => "chatgpt", - codex_app_server_protocol::AuthMode::ChatgptAuthTokens => "chatgpt_auth_tokens", - codex_app_server_protocol::AuthMode::AgentIdentity => "agent_identity", - codex_app_server_protocol::AuthMode::PersonalAccessToken => "personal_access_token", - codex_app_server_protocol::AuthMode::BedrockApiKey => "bedrock_api_key", + AuthMode::ApiKey => "api_key", + AuthMode::Chatgpt => "chatgpt", + AuthMode::ChatgptAuthTokens => "chatgpt_auth_tokens", + AuthMode::AgentIdentity => "agent_identity", + AuthMode::PersonalAccessToken => "personal_access_token", + AuthMode::BedrockApiKey => "bedrock_api_key", } } -fn stored_auth_mode_value(auth: &AuthDotJson) -> codex_app_server_protocol::AuthMode { +fn stored_auth_mode_value(auth: &AuthDotJson) -> AuthMode { if let Some(mode) = auth.auth_mode { return mode; } if auth.personal_access_token.is_some() { - codex_app_server_protocol::AuthMode::PersonalAccessToken + AuthMode::PersonalAccessToken } else if auth.bedrock_api_key.is_some() { - codex_app_server_protocol::AuthMode::BedrockApiKey + AuthMode::BedrockApiKey } else if auth.openai_api_key.is_some() { - codex_app_server_protocol::AuthMode::ApiKey + AuthMode::ApiKey } else { - codex_app_server_protocol::AuthMode::Chatgpt + AuthMode::Chatgpt } } @@ -1353,7 +1354,7 @@ fn stored_auth_issues( ) -> Vec<&'static str> { let mut issues = Vec::new(); match stored_auth_mode_value(auth) { - codex_app_server_protocol::AuthMode::ApiKey => { + AuthMode::ApiKey => { let stored_key_present = auth .openai_api_key .as_deref() @@ -1364,7 +1365,7 @@ fn stored_auth_issues( issues.push("API key auth is missing an API key"); } } - codex_app_server_protocol::AuthMode::Chatgpt => { + AuthMode::Chatgpt => { match auth.tokens.as_ref() { Some(tokens) => { if tokens.access_token.trim().is_empty() { @@ -1380,7 +1381,7 @@ fn stored_auth_issues( issues.push("ChatGPT auth is missing refresh metadata"); } } - codex_app_server_protocol::AuthMode::ChatgptAuthTokens => { + AuthMode::ChatgptAuthTokens => { match auth.tokens.as_ref() { Some(tokens) => { if tokens.access_token.trim().is_empty() { @@ -1396,7 +1397,7 @@ fn stored_auth_issues( issues.push("external ChatGPT auth is missing refresh metadata"); } } - codex_app_server_protocol::AuthMode::AgentIdentity => { + AuthMode::AgentIdentity => { if auth .agent_identity .as_ref() @@ -1405,7 +1406,7 @@ fn stored_auth_issues( issues.push("agent identity auth is missing an agent identity token"); } } - codex_app_server_protocol::AuthMode::PersonalAccessToken => { + AuthMode::PersonalAccessToken => { if auth .personal_access_token .as_deref() @@ -1414,7 +1415,7 @@ fn stored_auth_issues( issues.push("personal access token auth is missing a personal access token"); } } - codex_app_server_protocol::AuthMode::BedrockApiKey => { + AuthMode::BedrockApiKey => { if auth.bedrock_api_key.is_none() { issues.push("Bedrock API key auth is missing a Bedrock API key"); } @@ -2462,12 +2463,12 @@ fn websocket_error_detail(err: &ApiError) -> String { fn auth_mode_name(auth: &CodexAuth) -> &'static str { match auth.auth_mode() { - codex_app_server_protocol::AuthMode::ApiKey => "api_key", - codex_app_server_protocol::AuthMode::Chatgpt => "chatgpt", - codex_app_server_protocol::AuthMode::ChatgptAuthTokens => "chatgpt_auth_tokens", - codex_app_server_protocol::AuthMode::AgentIdentity => "agent_identity", - codex_app_server_protocol::AuthMode::PersonalAccessToken => "personal_access_token", - codex_app_server_protocol::AuthMode::BedrockApiKey => "bedrock_api_key", + AuthMode::ApiKey => "api_key", + AuthMode::Chatgpt => "chatgpt", + AuthMode::ChatgptAuthTokens => "chatgpt_auth_tokens", + AuthMode::AgentIdentity => "agent_identity", + AuthMode::PersonalAccessToken => "personal_access_token", + AuthMode::BedrockApiKey => "bedrock_api_key", } } @@ -2600,15 +2601,12 @@ fn provider_auth_reachability_mode_from_auth( return ProviderAuthReachabilityMode::Chatgpt; } match stored_auth.map(stored_auth_mode_value) { + Some(AuthMode::ApiKey | AuthMode::BedrockApiKey) => ProviderAuthReachabilityMode::ApiKey, Some( - codex_app_server_protocol::AuthMode::ApiKey - | codex_app_server_protocol::AuthMode::BedrockApiKey, - ) => ProviderAuthReachabilityMode::ApiKey, - Some( - codex_app_server_protocol::AuthMode::Chatgpt - | codex_app_server_protocol::AuthMode::ChatgptAuthTokens - | codex_app_server_protocol::AuthMode::AgentIdentity - | codex_app_server_protocol::AuthMode::PersonalAccessToken, + AuthMode::Chatgpt + | AuthMode::ChatgptAuthTokens + | AuthMode::AgentIdentity + | AuthMode::PersonalAccessToken, ) | None => ProviderAuthReachabilityMode::Chatgpt, } @@ -3502,7 +3500,7 @@ mod tests { #[test] fn stored_auth_validation_rejects_missing_api_key() { let auth = AuthDotJson { - auth_mode: Some(codex_app_server_protocol::AuthMode::ApiKey), + auth_mode: Some(AuthMode::ApiKey), openai_api_key: None, tokens: None, last_refresh: None, @@ -3554,7 +3552,7 @@ mod tests { assert_eq!(stored_auth_mode(&auth), "personal_access_token"); assert!(stored_auth_issues(&auth, |_| false).is_empty()); - auth.auth_mode = Some(codex_app_server_protocol::AuthMode::PersonalAccessToken); + auth.auth_mode = Some(AuthMode::PersonalAccessToken); auth.personal_access_token = None; assert_eq!( stored_auth_issues(&auth, |_| false), @@ -3565,7 +3563,7 @@ mod tests { #[test] fn provider_reachability_mode_uses_api_key_auth() { let api_key_auth = AuthDotJson { - auth_mode: Some(codex_app_server_protocol::AuthMode::ApiKey), + auth_mode: Some(AuthMode::ApiKey), openai_api_key: Some("sk-test".to_string()), tokens: None, last_refresh: None, diff --git a/codex-rs/cli/src/login.rs b/codex-rs/cli/src/login.rs index 5691c3cd1f59..d3f9b7b96b02 100644 --- a/codex-rs/cli/src/login.rs +++ b/codex-rs/cli/src/login.rs @@ -7,7 +7,6 @@ //! into a one-shot CLI command while still producing a durable `codex-login.log` artifact that //! support can request from users. -use codex_app_server_protocol::AuthMode; use codex_config::types::AuthCredentialsStoreMode; use codex_core::config::Config; use codex_login::AuthKeyringBackendKind; @@ -20,6 +19,7 @@ use codex_login::login_with_api_key; use codex_login::logout_with_revoke; use codex_login::run_device_code_login; use codex_login::run_login_server; +use codex_protocol::auth::AuthMode; use codex_protocol::config_types::ForcedLoginMethod; use codex_utils_cli::CliConfigOverrides; use std::fs::OpenOptions; diff --git a/codex-rs/cli/src/plugin_cmd.rs b/codex-rs/cli/src/plugin_cmd.rs index 433714a00c43..b1cf21aa54a1 100644 --- a/codex-rs/cli/src/plugin_cmd.rs +++ b/codex-rs/cli/src/plugin_cmd.rs @@ -2,7 +2,6 @@ use anyhow::Context; use anyhow::Result; use anyhow::bail; use clap::Parser; -use codex_app_server_protocol::AuthMode; use codex_core::config::Config; use codex_core::config::find_codex_home; use codex_core_plugins::ConfiguredMarketplace; @@ -22,6 +21,7 @@ use codex_login::CodexAuth; use codex_login::auth::read_codex_api_key_from_env; use codex_plugin::PluginId; use codex_plugin::validate_plugin_segment; +use codex_protocol::auth::AuthMode; use codex_utils_cli::CliConfigOverrides; use serde::Serialize; use std::collections::HashMap; diff --git a/codex-rs/core-plugins/src/app_mcp_routing.rs b/codex-rs/core-plugins/src/app_mcp_routing.rs index 18cacb414018..0034dddc4a50 100644 --- a/codex-rs/core-plugins/src/app_mcp_routing.rs +++ b/codex-rs/core-plugins/src/app_mcp_routing.rs @@ -1,5 +1,5 @@ -use codex_app_server_protocol::AuthMode; use codex_plugin::AppDeclaration; +use codex_protocol::auth::AuthMode; use std::collections::HashMap; use std::collections::HashSet; diff --git a/codex-rs/core-plugins/src/discoverable_tests.rs b/codex-rs/core-plugins/src/discoverable_tests.rs index d472c76344c4..5b817e6a7e4d 100644 --- a/codex-rs/core-plugins/src/discoverable_tests.rs +++ b/codex-rs/core-plugins/src/discoverable_tests.rs @@ -15,9 +15,9 @@ use crate::test_support::write_curated_plugin_sha_with; use crate::test_support::write_file; use crate::test_support::write_openai_api_curated_marketplace; use crate::test_support::write_openai_curated_marketplace; -use codex_app_server_protocol::AuthMode; use codex_config::CONFIG_TOML_FILE; use codex_login::CodexAuth; +use codex_protocol::auth::AuthMode; use codex_utils_absolute_path::AbsolutePathBuf; use pretty_assertions::assert_eq; use serde_json::json; diff --git a/codex-rs/core-plugins/src/loader.rs b/codex-rs/core-plugins/src/loader.rs index 7244b65ceee0..6fdc3f1a2806 100644 --- a/codex-rs/core-plugins/src/loader.rs +++ b/codex-rs/core-plugins/src/loader.rs @@ -15,7 +15,6 @@ use crate::remote::RemoteInstalledPlugin; use crate::store::PluginStore; use crate::store::plugin_version_for_source; use crate::store::plugin_version_for_source_with_fallback_manifest; -use codex_app_server_protocol::AuthMode; use codex_config::ConfigLayerStack; use codex_config::HooksFile; use codex_config::types::McpServerConfig; @@ -39,6 +38,7 @@ use codex_plugin::PluginHookSource; use codex_plugin::PluginId; use codex_plugin::PluginIdError; use codex_plugin::app_connector_ids_from_declarations; +use codex_protocol::auth::AuthMode; use codex_protocol::protocol::Product; use codex_protocol::protocol::SkillScope; use codex_utils_absolute_path::AbsolutePathBuf; diff --git a/codex-rs/core-plugins/src/manager.rs b/codex-rs/core-plugins/src/manager.rs index 9b84094bac7b..72f2ad68e7fa 100644 --- a/codex-rs/core-plugins/src/manager.rs +++ b/codex-rs/core-plugins/src/manager.rs @@ -54,7 +54,6 @@ use crate::store::PluginStore; use crate::store::PluginStoreError; use crate::tool_suggest_metadata::ToolSuggestMetadataCache; use codex_analytics::AnalyticsEventsClient; -use codex_app_server_protocol::AuthMode; use codex_config::ConfigLayerStack; use codex_config::clear_user_plugin; use codex_config::set_user_plugin_enabled; @@ -75,6 +74,7 @@ use codex_plugin::PluginIdError; use codex_plugin::PluginTelemetryMetadata; use codex_plugin::app_connector_ids_from_declarations; use codex_plugin::prompt_safe_plugin_description; +use codex_protocol::auth::AuthMode; use codex_protocol::protocol::HookEventName; use codex_protocol::protocol::Product; use codex_tools::DiscoverablePluginInfo; diff --git a/codex-rs/core-plugins/src/manager_tests.rs b/codex-rs/core-plugins/src/manager_tests.rs index 1d3363fffe35..60bbe351d371 100644 --- a/codex-rs/core-plugins/src/manager_tests.rs +++ b/codex-rs/core-plugins/src/manager_tests.rs @@ -25,7 +25,6 @@ use crate::test_support::write_curated_plugin_sha_with as write_curated_plugin_s use crate::test_support::write_file; use crate::test_support::write_openai_api_curated_marketplace; use crate::test_support::write_openai_curated_marketplace; -use codex_app_server_protocol::AuthMode; use codex_app_server_protocol::ConfigLayerSource; use codex_config::AppToolApproval; use codex_config::CONFIG_TOML_FILE; @@ -44,6 +43,7 @@ use codex_core_skills::config_rules::SkillConfigRules; use codex_login::CodexAuth; use codex_plugin::AppDeclaration; use codex_plugin::PluginId; +use codex_protocol::auth::AuthMode; use codex_protocol::protocol::HookEventName; use codex_protocol::protocol::Product; use codex_utils_absolute_path::AbsolutePathBuf; diff --git a/codex-rs/core-plugins/src/tool_suggest_metadata.rs b/codex-rs/core-plugins/src/tool_suggest_metadata.rs index 310bec865449..30065e6d94e6 100644 --- a/codex-rs/core-plugins/src/tool_suggest_metadata.rs +++ b/codex-rs/core-plugins/src/tool_suggest_metadata.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::sync::Arc; use std::sync::RwLock; -use codex_app_server_protocol::AuthMode; use codex_core_skills::config_rules::SkillConfigRules; use codex_plugin::AppDeclaration; use codex_plugin::PluginCapabilitySummary; @@ -10,6 +9,7 @@ use codex_plugin::PluginId; use codex_plugin::PluginIdError; use codex_plugin::app_connector_ids_from_declarations; use codex_plugin::prompt_safe_plugin_description; +use codex_protocol::auth::AuthMode; use codex_protocol::protocol::Product; use tokio::sync::Semaphore; diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 67a940dc9e7f..649f52e54914 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -61,7 +61,6 @@ use codex_api::auth_header_telemetry; use codex_api::build_session_headers; use codex_api::create_text_param_for_request; use codex_api::response_create_client_metadata; -use codex_app_server_protocol::AuthMode; use codex_login::AuthManager; use codex_login::CodexAuth; use codex_login::RefreshTokenError; @@ -69,6 +68,7 @@ use codex_login::UnauthorizedRecovery; use codex_login::default_client::build_reqwest_client; use codex_otel::SessionTelemetry; use codex_otel::current_span_w3c_trace_context; +use codex_protocol::auth::AuthMode; use codex_protocol::ThreadId; use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig; diff --git a/codex-rs/core/src/client_tests.rs b/codex-rs/core/src/client_tests.rs index 8d58fec9bdce..78dc324c4302 100644 --- a/codex-rs/core/src/client_tests.rs +++ b/codex-rs/core/src/client_tests.rs @@ -16,7 +16,6 @@ use crate::test_support::responses_metadata as test_responses_metadata; use codex_api::ApiError; use codex_api::ResponseEvent; use codex_api::TransportError; -use codex_app_server_protocol::AuthMode; use codex_login::AuthManager; use codex_login::CodexAuth; use codex_model_provider::BearerAuthProvider; @@ -28,6 +27,7 @@ use codex_model_provider_info::WireApi; use codex_model_provider_info::create_oss_provider_with_base_url; use codex_otel::SessionTelemetry; use codex_protocol::ThreadId; +use codex_protocol::auth::AuthMode; use codex_protocol::models::ContentItem; use codex_protocol::models::ResponseItem; use codex_protocol::openai_models::ModelInfo; diff --git a/codex-rs/core/src/compact_remote.rs b/codex-rs/core/src/compact_remote.rs index 21c019073f5b..6b251562f798 100644 --- a/codex-rs/core/src/compact_remote.rs +++ b/codex-rs/core/src/compact_remote.rs @@ -25,7 +25,7 @@ use codex_analytics::CompactionImplementation; use codex_analytics::CompactionPhase; use codex_analytics::CompactionReason; use codex_analytics::CompactionTrigger; -use codex_app_server_protocol::AuthMode; +use codex_protocol::auth::AuthMode; use codex_protocol::error::CodexErr; use codex_protocol::error::Result as CodexResult; use codex_protocol::items::ContextCompactionItem; diff --git a/codex-rs/core/src/realtime_conversation.rs b/codex-rs/core/src/realtime_conversation.rs index 43ecc3e576f4..0c81133be11b 100644 --- a/codex-rs/core/src/realtime_conversation.rs +++ b/codex-rs/core/src/realtime_conversation.rs @@ -22,13 +22,13 @@ use codex_api::RealtimeWebsocketClient; use codex_api::RealtimeWebsocketEvents; use codex_api::RealtimeWebsocketWriter; use codex_api::map_api_error; -use codex_app_server_protocol::AuthMode; use codex_config::config_toml::RealtimeWsMode; use codex_config::config_toml::RealtimeWsVersion; use codex_login::CodexAuth; use codex_login::default_client::default_headers; use codex_login::read_openai_api_key_from_env; use codex_model_provider_info::ModelProviderInfo; +use codex_protocol::auth::AuthMode; use codex_protocol::error::CodexErr; use codex_protocol::error::Result as CodexResult; use codex_protocol::models::MessagePhase; diff --git a/codex-rs/login/Cargo.toml b/codex-rs/login/Cargo.toml index d34e9d87978a..04066f5894f5 100644 --- a/codex-rs/login/Cargo.toml +++ b/codex-rs/login/Cargo.toml @@ -11,7 +11,6 @@ workspace = true base64 = { workspace = true } chrono = { workspace = true, features = ["serde"] } codex-agent-identity = { workspace = true } -codex-app-server-protocol = { workspace = true } codex-client = { workspace = true } codex-config = { workspace = true } codex-keyring-store = { workspace = true } diff --git a/codex-rs/login/src/auth/auth_tests.rs b/codex-rs/login/src/auth/auth_tests.rs index bcce562e69e6..d52563f02327 100644 --- a/codex-rs/login/src/auth/auth_tests.rs +++ b/codex-rs/login/src/auth/auth_tests.rs @@ -2,8 +2,8 @@ use super::*; use crate::auth::storage::FileAuthStorage; use crate::auth::storage::get_auth_file; use crate::token_data::IdTokenInfo; -use codex_app_server_protocol::AuthMode; use codex_protocol::account::PlanType as AccountPlanType; +use codex_protocol::auth::AuthMode; use codex_protocol::auth::KnownPlan as InternalKnownPlan; use codex_protocol::auth::PlanType as InternalPlanType; use codex_protocol::protocol::SessionSource; @@ -162,7 +162,7 @@ async fn stored_agent_identity_jwt_keeps_auth_json_unchanged() -> anyhow::Result save_auth( codex_home.path(), &AuthDotJson { - auth_mode: Some(ApiAuthMode::AgentIdentity), + auth_mode: Some(AuthMode::AgentIdentity), openai_api_key: None, tokens: None, last_refresh: None, @@ -897,7 +897,7 @@ async fn loads_api_key_from_auth_json() { fn logout_removes_auth_file() -> Result<(), std::io::Error> { let dir = tempdir()?; let auth_dot_json = AuthDotJson { - auth_mode: Some(ApiAuthMode::ApiKey), + auth_mode: Some(AuthMode::ApiKey), openai_api_key: Some("sk-test-key".to_string()), tokens: None, last_refresh: None, @@ -1045,7 +1045,7 @@ async fn external_bearer_only_auth_manager_uses_cached_provider_token() { assert_eq!(first.as_deref(), Some("provider-token")); assert_eq!(second.as_deref(), Some("provider-token")); assert_eq!(manager.auth_mode(), Some(AuthMode::ApiKey)); - assert_eq!(manager.get_api_auth_mode(), Some(ApiAuthMode::ApiKey)); + assert_eq!(manager.get_api_auth_mode(), Some(AuthMode::ApiKey)); } #[tokio::test] @@ -1822,7 +1822,7 @@ async fn enforce_login_restrictions_logs_out_for_agent_identity_workspace_mismat save_auth( codex_home.path(), &AuthDotJson { - auth_mode: Some(ApiAuthMode::AgentIdentity), + auth_mode: Some(AuthMode::AgentIdentity), openai_api_key: None, tokens: None, last_refresh: None, diff --git a/codex-rs/login/src/auth/bedrock_api_key.rs b/codex-rs/login/src/auth/bedrock_api_key.rs index af3d16398f69..3445878365a1 100644 --- a/codex-rs/login/src/auth/bedrock_api_key.rs +++ b/codex-rs/login/src/auth/bedrock_api_key.rs @@ -7,7 +7,7 @@ use serde::Serialize; use super::manager::save_auth; use super::storage::AuthDotJson; use super::storage::AuthKeyringBackendKind; -use codex_app_server_protocol::AuthMode; +use codex_protocol::auth::AuthMode; /// Managed Amazon Bedrock API key persisted in `auth.json`. #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] diff --git a/codex-rs/login/src/auth/bedrock_api_key_tests.rs b/codex-rs/login/src/auth/bedrock_api_key_tests.rs index 7db2672ce36a..1f8fab68b2c9 100644 --- a/codex-rs/login/src/auth/bedrock_api_key_tests.rs +++ b/codex-rs/login/src/auth/bedrock_api_key_tests.rs @@ -1,5 +1,5 @@ -use codex_app_server_protocol::AuthMode; use codex_config::types::AuthCredentialsStoreMode; +use codex_protocol::auth::AuthMode; use pretty_assertions::assert_eq; use serial_test::serial; use tempfile::tempdir; diff --git a/codex-rs/login/src/auth/external_bearer.rs b/codex-rs/login/src/auth/external_bearer.rs index e40c33264013..f30d52f434c1 100644 --- a/codex-rs/login/src/auth/external_bearer.rs +++ b/codex-rs/login/src/auth/external_bearer.rs @@ -2,7 +2,7 @@ use super::manager::ExternalAuth; use super::manager::ExternalAuthFuture; use super::manager::ExternalAuthRefreshContext; use super::manager::ExternalAuthTokens; -use codex_app_server_protocol::AuthMode; +use codex_protocol::auth::AuthMode; use codex_protocol::config_types::ModelProviderAuthInfo; use std::fmt; use std::io; diff --git a/codex-rs/login/src/auth/manager.rs b/codex-rs/login/src/auth/manager.rs index 26580a672d52..8418c7a2a505 100644 --- a/codex-rs/login/src/auth/manager.rs +++ b/codex-rs/login/src/auth/manager.rs @@ -20,8 +20,7 @@ use tokio::sync::watch; use tracing::instrument; use codex_agent_identity::ChatGptEnvironment; -use codex_app_server_protocol::AuthMode; -use codex_app_server_protocol::AuthMode as ApiAuthMode; +use codex_protocol::auth::AuthMode; use codex_protocol::config_types::ForcedLoginMethod; use codex_protocol::config_types::ModelProviderAuthInfo; @@ -242,13 +241,13 @@ impl CodexAuth { auth_route_config: Option<&AuthRouteConfig>, ) -> std::io::Result { let auth_mode = auth_dot_json.resolved_mode(); - if auth_mode == ApiAuthMode::ApiKey { + if auth_mode == AuthMode::ApiKey { let Some(api_key) = auth_dot_json.openai_api_key.as_deref() else { return Err(std::io::Error::other("API key auth is missing a key.")); }; return Ok(Self::from_api_key(api_key)); } - if auth_mode == ApiAuthMode::AgentIdentity { + if auth_mode == AuthMode::AgentIdentity { let Some(agent_identity) = auth_dot_json.agent_identity.clone() else { return Err(std::io::Error::other( "agent identity auth is missing agent identity auth material.", @@ -282,7 +281,7 @@ impl CodexAuth { } } } - if auth_mode == ApiAuthMode::PersonalAccessToken { + if auth_mode == AuthMode::PersonalAccessToken { let Some(personal_access_token) = auth_dot_json.personal_access_token.as_deref() else { return Err(std::io::Error::other( "personal access token auth is missing a personal access token.", @@ -291,7 +290,7 @@ impl CodexAuth { return Self::from_personal_access_token(personal_access_token, auth_route_config) .await; } - if auth_mode == ApiAuthMode::BedrockApiKey { + if auth_mode == AuthMode::BedrockApiKey { let Some(auth) = auth_dot_json.bedrock_api_key else { return Err(std::io::Error::other( "Bedrock API key auth is missing a Bedrock API key.", @@ -308,7 +307,7 @@ impl CodexAuth { }; match auth_mode { - ApiAuthMode::Chatgpt => { + AuthMode::Chatgpt => { let storage = create_auth_storage( codex_home.to_path_buf(), storage_mode, @@ -316,15 +315,13 @@ impl CodexAuth { ); Ok(Self::Chatgpt(ChatgptAuth { state, storage })) } - ApiAuthMode::ChatgptAuthTokens => { - Ok(Self::ChatgptAuthTokens(ChatgptAuthTokens { state })) - } - ApiAuthMode::ApiKey => unreachable!("api key mode is handled above"), - ApiAuthMode::AgentIdentity => unreachable!("agent identity mode is handled above"), - ApiAuthMode::PersonalAccessToken => { + AuthMode::ChatgptAuthTokens => Ok(Self::ChatgptAuthTokens(ChatgptAuthTokens { state })), + AuthMode::ApiKey => unreachable!("api key mode is handled above"), + AuthMode::AgentIdentity => unreachable!("agent identity mode is handled above"), + AuthMode::PersonalAccessToken => { unreachable!("personal access token mode is handled above") } - ApiAuthMode::BedrockApiKey => unreachable!("bedrock api key mode is handled above"), + AuthMode::BedrockApiKey => unreachable!("bedrock api key mode is handled above"), } } @@ -395,6 +392,9 @@ impl CodexAuth { )) } + /// Returns the effective backend auth mode. + /// + /// Externally managed ChatGPT tokens are normalized to [`AuthMode::Chatgpt`]. pub fn auth_mode(&self) -> AuthMode { match self { Self::ApiKey(_) => AuthMode::ApiKey, @@ -405,14 +405,15 @@ impl CodexAuth { } } - pub fn api_auth_mode(&self) -> ApiAuthMode { + /// Returns the precise kind of credentials backing this authentication. + pub fn api_auth_mode(&self) -> AuthMode { match self { - Self::ApiKey(_) => ApiAuthMode::ApiKey, - Self::Chatgpt(_) => ApiAuthMode::Chatgpt, - Self::ChatgptAuthTokens(_) => ApiAuthMode::ChatgptAuthTokens, - Self::AgentIdentity(_) => ApiAuthMode::AgentIdentity, - Self::PersonalAccessToken(_) => ApiAuthMode::PersonalAccessToken, - Self::BedrockApiKey(_) => ApiAuthMode::BedrockApiKey, + Self::ApiKey(_) => AuthMode::ApiKey, + Self::Chatgpt(_) => AuthMode::Chatgpt, + Self::ChatgptAuthTokens(_) => AuthMode::ChatgptAuthTokens, + Self::AgentIdentity(_) => AuthMode::AgentIdentity, + Self::PersonalAccessToken(_) => AuthMode::PersonalAccessToken, + Self::BedrockApiKey(_) => AuthMode::BedrockApiKey, } } @@ -660,7 +661,7 @@ impl CodexAuth { /// Consider this private to integration tests. pub fn create_dummy_chatgpt_auth_for_testing() -> Self { let auth_dot_json = AuthDotJson { - auth_mode: Some(ApiAuthMode::Chatgpt), + auth_mode: Some(AuthMode::Chatgpt), openai_api_key: None, tokens: Some(TokenData { id_token: Default::default(), @@ -851,7 +852,7 @@ pub fn login_with_api_key( keyring_backend_kind: AuthKeyringBackendKind, ) -> std::io::Result<()> { let auth_dot_json = AuthDotJson { - auth_mode: Some(ApiAuthMode::ApiKey), + auth_mode: Some(AuthMode::ApiKey), openai_api_key: Some(api_key.to_string()), tokens: None, last_refresh: None, @@ -900,7 +901,7 @@ pub async fn login_with_access_token( .to_string(); verified_record_from_jwt(jwt, &base_url, auth_route_config).await?; AuthDotJson { - auth_mode: Some(ApiAuthMode::AgentIdentity), + auth_mode: Some(AuthMode::AgentIdentity), openai_api_key: None, tokens: None, last_refresh: None, @@ -1419,7 +1420,7 @@ impl AuthDotJson { }; Ok(Self { - auth_mode: Some(ApiAuthMode::ChatgptAuthTokens), + auth_mode: Some(AuthMode::ChatgptAuthTokens), openai_api_key: None, tokens: Some(tokens), last_refresh: Some(Utc::now()), @@ -1442,27 +1443,27 @@ impl AuthDotJson { Self::from_external_tokens(&external) } - pub(super) fn resolved_mode(&self) -> ApiAuthMode { + pub(super) fn resolved_mode(&self) -> AuthMode { if let Some(mode) = self.auth_mode { return mode; } if self.personal_access_token.is_some() { - return ApiAuthMode::PersonalAccessToken; + return AuthMode::PersonalAccessToken; } if self.bedrock_api_key.is_some() { - return ApiAuthMode::BedrockApiKey; + return AuthMode::BedrockApiKey; } if self.openai_api_key.is_some() { - return ApiAuthMode::ApiKey; + return AuthMode::ApiKey; } - ApiAuthMode::Chatgpt + AuthMode::Chatgpt } fn storage_mode( &self, auth_credentials_store_mode: AuthCredentialsStoreMode, ) -> AuthCredentialsStoreMode { - if self.resolved_mode() == ApiAuthMode::ChatgptAuthTokens { + if self.resolved_mode() == AuthMode::ChatgptAuthTokens { AuthCredentialsStoreMode::Ephemeral } else { auth_credentials_store_mode @@ -2072,19 +2073,19 @@ impl AuthManager { match (a, b) { (None, None) => true, (Some(a), Some(b)) => match (a.api_auth_mode(), b.api_auth_mode()) { - (ApiAuthMode::ApiKey, ApiAuthMode::ApiKey) => a.api_key() == b.api_key(), - (ApiAuthMode::Chatgpt, ApiAuthMode::Chatgpt) - | (ApiAuthMode::ChatgptAuthTokens, ApiAuthMode::ChatgptAuthTokens) => { + (AuthMode::ApiKey, AuthMode::ApiKey) => a.api_key() == b.api_key(), + (AuthMode::Chatgpt, AuthMode::Chatgpt) + | (AuthMode::ChatgptAuthTokens, AuthMode::ChatgptAuthTokens) => { a.get_current_auth_json() == b.get_current_auth_json() } - (ApiAuthMode::AgentIdentity, ApiAuthMode::AgentIdentity) => match (a, b) { + (AuthMode::AgentIdentity, AuthMode::AgentIdentity) => match (a, b) { (CodexAuth::AgentIdentity(a), CodexAuth::AgentIdentity(b)) => { a.record() == b.record() } _ => false, }, - (ApiAuthMode::PersonalAccessToken, ApiAuthMode::PersonalAccessToken) => a == b, - (ApiAuthMode::BedrockApiKey, ApiAuthMode::BedrockApiKey) => a == b, + (AuthMode::PersonalAccessToken, AuthMode::PersonalAccessToken) => a == b, + (AuthMode::BedrockApiKey, AuthMode::BedrockApiKey) => a == b, _ => false, }, _ => false, @@ -2401,13 +2402,15 @@ impl AuthManager { Ok(result) } - pub fn get_api_auth_mode(&self) -> Option { + /// Returns the precise kind of credentials backing the current authentication. + pub fn get_api_auth_mode(&self) -> Option { if self.has_external_api_key_auth() { - return Some(ApiAuthMode::ApiKey); + return Some(AuthMode::ApiKey); } self.auth_cached().as_ref().map(CodexAuth::api_auth_mode) } + /// Returns the effective backend auth mode for the current authentication. pub fn auth_mode(&self) -> Option { if self.has_external_api_key_auth() { return Some(AuthMode::ApiKey); diff --git a/codex-rs/login/src/auth/revoke.rs b/codex-rs/login/src/auth/revoke.rs index 0f7e313c1cb1..076b8b53daf6 100644 --- a/codex-rs/login/src/auth/revoke.rs +++ b/codex-rs/login/src/auth/revoke.rs @@ -7,8 +7,8 @@ use serde::Serialize; use std::time::Duration; -use codex_app_server_protocol::AuthMode as ApiAuthMode; use codex_client::CodexHttpClient; +use codex_protocol::auth::AuthMode; use super::manager::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR; use super::manager::REVOKE_TOKEN_URL; @@ -77,21 +77,21 @@ fn revocable_token(auth_dot_json: &AuthDotJson) -> Option<(&str, RevokeTokenKind } fn managed_chatgpt_tokens(auth_dot_json: &AuthDotJson) -> Option<&TokenData> { - if resolved_auth_mode(auth_dot_json) == ApiAuthMode::Chatgpt { + if resolved_auth_mode(auth_dot_json) == AuthMode::Chatgpt { auth_dot_json.tokens.as_ref() } else { None } } -fn resolved_auth_mode(auth_dot_json: &AuthDotJson) -> ApiAuthMode { +fn resolved_auth_mode(auth_dot_json: &AuthDotJson) -> AuthMode { if let Some(mode) = auth_dot_json.auth_mode { return mode; } if auth_dot_json.openai_api_key.is_some() { - return ApiAuthMode::ApiKey; + return AuthMode::ApiKey; } - ApiAuthMode::Chatgpt + AuthMode::Chatgpt } async fn revoke_oauth_token( diff --git a/codex-rs/login/src/auth/storage.rs b/codex-rs/login/src/auth/storage.rs index b5ae190551b1..ae195f3bff62 100644 --- a/codex-rs/login/src/auth/storage.rs +++ b/codex-rs/login/src/auth/storage.rs @@ -22,12 +22,12 @@ use super::BedrockApiKeyAuth; use crate::token_data::TokenData; use codex_agent_identity::AgentIdentityJwtClaims; use codex_agent_identity::decode_agent_identity_jwt; -use codex_app_server_protocol::AuthMode; use codex_config::types::AuthCredentialsStoreMode; pub use codex_config::types::AuthKeyringBackendKind; use codex_keyring_store::DefaultKeyringStore; use codex_keyring_store::KeyringStore; use codex_protocol::account::PlanType as AccountPlanType; +use codex_protocol::auth::AuthMode; use codex_secrets::LocalSecretsNamespace; use codex_secrets::SecretName; use codex_secrets::SecretScope; diff --git a/codex-rs/login/src/server.rs b/codex-rs/login/src/server.rs index c450da0f68d5..607e47fefe4b 100644 --- a/codex-rs/login/src/server.rs +++ b/codex-rs/login/src/server.rs @@ -36,8 +36,8 @@ use crate::token_data::TokenData; use crate::token_data::parse_chatgpt_jwt_claims; use base64::Engine; use chrono::Utc; -use codex_app_server_protocol::AuthMode; use codex_config::types::AuthCredentialsStoreMode; +use codex_protocol::auth::AuthMode; use codex_utils_template::Template; use rand::RngCore; use serde_json::Value as JsonValue; diff --git a/codex-rs/login/tests/suite/auth_refresh.rs b/codex-rs/login/tests/suite/auth_refresh.rs index 9c5eb936b190..e47b2978e8d6 100644 --- a/codex-rs/login/tests/suite/auth_refresh.rs +++ b/codex-rs/login/tests/suite/auth_refresh.rs @@ -3,7 +3,6 @@ use anyhow::Result; use base64::Engine; use chrono::Duration; use chrono::Utc; -use codex_app_server_protocol::AuthMode; use codex_config::types::AuthCredentialsStoreMode; use codex_login::AuthDotJson; use codex_login::AuthKeyringBackendKind; @@ -15,6 +14,7 @@ use codex_login::load_auth_dot_json; use codex_login::save_auth; use codex_login::token_data::IdTokenInfo; use codex_login::token_data::TokenData; +use codex_protocol::auth::AuthMode; use codex_protocol::auth::RefreshTokenFailedReason; use core_test_support::skip_if_no_network; use pretty_assertions::assert_eq; diff --git a/codex-rs/login/tests/suite/logout.rs b/codex-rs/login/tests/suite/logout.rs index e8f9981a8a7f..bf2873779b32 100644 --- a/codex-rs/login/tests/suite/logout.rs +++ b/codex-rs/login/tests/suite/logout.rs @@ -1,7 +1,6 @@ use anyhow::Context; use anyhow::Result; use base64::Engine; -use codex_app_server_protocol::AuthMode; use codex_config::types::AuthCredentialsStoreMode; use codex_login::AuthDotJson; use codex_login::AuthKeyringBackendKind; @@ -14,6 +13,7 @@ use codex_login::logout_with_revoke; use codex_login::save_auth; use codex_login::token_data::IdTokenInfo; use codex_login::token_data::TokenData; +use codex_protocol::auth::AuthMode; use core_test_support::skip_if_no_network; use pretty_assertions::assert_eq; use serde_json::Value; diff --git a/codex-rs/model-provider-info/Cargo.toml b/codex-rs/model-provider-info/Cargo.toml index 23c1b3415b05..da018acf66a1 100644 --- a/codex-rs/model-provider-info/Cargo.toml +++ b/codex-rs/model-provider-info/Cargo.toml @@ -14,7 +14,6 @@ workspace = true [dependencies] codex-api = { workspace = true } -codex-app-server-protocol = { workspace = true } codex-protocol = { workspace = true } http = { workspace = true } schemars = { workspace = true } diff --git a/codex-rs/model-provider-info/src/lib.rs b/codex-rs/model-provider-info/src/lib.rs index 5f0f840508f5..ca3bf71ccc4c 100644 --- a/codex-rs/model-provider-info/src/lib.rs +++ b/codex-rs/model-provider-info/src/lib.rs @@ -8,7 +8,7 @@ use codex_api::Provider as ApiProvider; use codex_api::RetryConfig as ApiRetryConfig; use codex_api::is_azure_responses_provider; -use codex_app_server_protocol::AuthMode; +use codex_protocol::auth::AuthMode; use codex_protocol::config_types::ModelProviderAuthInfo; use codex_protocol::error::CodexErr; use codex_protocol::error::EnvVarError; diff --git a/codex-rs/models-manager/Cargo.toml b/codex-rs/models-manager/Cargo.toml index 7a157fd14088..fb5083c756c3 100644 --- a/codex-rs/models-manager/Cargo.toml +++ b/codex-rs/models-manager/Cargo.toml @@ -14,7 +14,6 @@ workspace = true [dependencies] chrono = { workspace = true, features = ["serde"] } -codex-app-server-protocol = { workspace = true } codex-collaboration-mode-templates = { workspace = true } codex-login = { workspace = true } codex-otel = { workspace = true } diff --git a/codex-rs/models-manager/src/lib.rs b/codex-rs/models-manager/src/lib.rs index 8bf30d0b602a..4a4c590afeb5 100644 --- a/codex-rs/models-manager/src/lib.rs +++ b/codex-rs/models-manager/src/lib.rs @@ -6,7 +6,7 @@ pub mod model_info; pub mod model_presets; pub mod test_support; -pub use codex_app_server_protocol::AuthMode; +pub use codex_protocol::auth::AuthMode; pub use config::ModelsManagerConfig; /// Load the bundled model catalog shipped with `codex-models-manager`. diff --git a/codex-rs/models-manager/src/manager.rs b/codex-rs/models-manager/src/manager.rs index e09518aa9658..7a68c254fee7 100644 --- a/codex-rs/models-manager/src/manager.rs +++ b/codex-rs/models-manager/src/manager.rs @@ -2,8 +2,8 @@ use super::cache::ModelsCacheManager; use crate::collaboration_mode_presets::builtin_collaboration_mode_presets; use crate::config::ModelsManagerConfig; use crate::model_info; -use codex_app_server_protocol::AuthMode; use codex_login::AuthManager; +use codex_protocol::auth::AuthMode; use codex_protocol::config_types::CollaborationModeMask; use codex_protocol::error::Result as CoreResult; use codex_protocol::openai_models::ModelInfo; diff --git a/codex-rs/models-manager/src/manager_tests.rs b/codex-rs/models-manager/src/manager_tests.rs index eb9c5a893210..bd692baad116 100644 --- a/codex-rs/models-manager/src/manager_tests.rs +++ b/codex-rs/models-manager/src/manager_tests.rs @@ -1,7 +1,6 @@ use super::*; use crate::ModelsManagerConfig; use chrono::Utc; -use codex_app_server_protocol::AuthMode; use codex_login::AuthCredentialsStoreMode; use codex_login::AuthKeyringBackendKind; use codex_login::AuthManager; @@ -10,6 +9,7 @@ use codex_login::ExternalAuth; use codex_login::ExternalAuthRefreshContext; use codex_login::ExternalAuthTokens; use codex_login::TokenData; +use codex_protocol::auth::AuthMode; use codex_protocol::openai_models::ModelsResponse; use pretty_assertions::assert_eq; use serde_json::json; diff --git a/codex-rs/otel/Cargo.toml b/codex-rs/otel/Cargo.toml index 937011990506..d96850616b16 100644 --- a/codex-rs/otel/Cargo.toml +++ b/codex-rs/otel/Cargo.toml @@ -17,7 +17,6 @@ chrono = { workspace = true } codex-utils-absolute-path = { workspace = true } codex-utils-string = { workspace = true } codex-api = { workspace = true } -codex-app-server-protocol = { workspace = true } codex-protocol = { workspace = true } eventsource-stream = { workspace = true } gethostname = { workspace = true } diff --git a/codex-rs/otel/src/lib.rs b/codex-rs/otel/src/lib.rs index fe7a171ce96b..2a36fab16d85 100644 --- a/codex-rs/otel/src/lib.rs +++ b/codex-rs/otel/src/lib.rs @@ -8,6 +8,7 @@ mod otlp; mod targets; use crate::metrics::Result as MetricsResult; +use codex_protocol::auth::AuthMode; use serde::Serialize; use strum_macros::Display; @@ -44,22 +45,21 @@ pub enum ToolDecisionSource { User, } -/// Maps to API/auth `AuthMode` to avoid a circular dependency on codex-core. +/// Coarsens the authentication domain into the dimensions used by telemetry. #[derive(Debug, Clone, Copy, PartialEq, Eq, Display)] pub enum TelemetryAuthMode { ApiKey, Chatgpt, } -impl From for TelemetryAuthMode { - fn from(mode: codex_app_server_protocol::AuthMode) -> Self { +impl From for TelemetryAuthMode { + fn from(mode: AuthMode) -> Self { match mode { - codex_app_server_protocol::AuthMode::ApiKey - | codex_app_server_protocol::AuthMode::BedrockApiKey => Self::ApiKey, - codex_app_server_protocol::AuthMode::Chatgpt - | codex_app_server_protocol::AuthMode::ChatgptAuthTokens - | codex_app_server_protocol::AuthMode::AgentIdentity - | codex_app_server_protocol::AuthMode::PersonalAccessToken => Self::Chatgpt, + AuthMode::ApiKey | AuthMode::BedrockApiKey => Self::ApiKey, + AuthMode::Chatgpt + | AuthMode::ChatgptAuthTokens + | AuthMode::AgentIdentity + | AuthMode::PersonalAccessToken => Self::Chatgpt, } } } diff --git a/codex-rs/protocol/src/auth.rs b/codex-rs/protocol/src/auth.rs index 495f138f42d0..7c506e87eb9d 100644 --- a/codex-rs/protocol/src/auth.rs +++ b/codex-rs/protocol/src/auth.rs @@ -1,7 +1,55 @@ use serde::Deserialize; use serde::Serialize; +use strum_macros::Display; use thiserror::Error; +/// Authentication mode for OpenAI-backed providers. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum AuthMode { + /// OpenAI API key provided by the caller and stored by Codex. + ApiKey, + /// ChatGPT OAuth managed by Codex (tokens persisted and refreshed by Codex). + Chatgpt, + /// ChatGPT auth tokens supplied by an external host application. + #[serde(rename = "chatgptAuthTokens")] + #[strum(serialize = "chatgptAuthTokens")] + ChatgptAuthTokens, + /// Programmatic Codex auth backed by a registered Agent Identity. + #[serde(rename = "agentIdentity")] + #[strum(serialize = "agentIdentity")] + AgentIdentity, + /// Programmatic Codex auth backed by a personal access token. + #[serde(rename = "personalAccessToken")] + #[strum(serialize = "personalAccessToken")] + PersonalAccessToken, + /// Amazon Bedrock bearer token managed by Codex. + #[serde(rename = "bedrockApiKey")] + #[strum(serialize = "bedrockApiKey")] + BedrockApiKey, +} + +impl AuthMode { + /// Returns whether this mode represents an authenticated human ChatGPT account. + pub fn has_chatgpt_account(self) -> bool { + match self { + Self::Chatgpt | Self::ChatgptAuthTokens | Self::PersonalAccessToken => true, + Self::ApiKey | Self::AgentIdentity | Self::BedrockApiKey => false, + } + } + + /// Returns whether this mode is backed by Codex services rather than a direct model API. + pub fn uses_codex_backend(self) -> bool { + match self { + Self::Chatgpt + | Self::ChatgptAuthTokens + | Self::AgentIdentity + | Self::PersonalAccessToken => true, + Self::ApiKey | Self::BedrockApiKey => false, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum PlanType { diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 290430604d3e..6219432b39e2 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -32,7 +32,6 @@ use codex_app_server_client::RemoteAppServerConnectArgs; pub use codex_app_server_client::RemoteAppServerEndpoint; use codex_app_server_protocol::Account as AppServerAccount; use codex_app_server_protocol::AskForApproval; -use codex_app_server_protocol::AuthMode as AppServerAuthMode; use codex_app_server_protocol::ConfigWarningNotification; use codex_app_server_protocol::Thread as AppServerThread; use codex_app_server_protocol::ThreadListCwdFilter; @@ -51,6 +50,7 @@ use codex_login::default_client::originator; use codex_login::default_client::set_default_client_residency_requirement; use codex_login::enforce_login_restrictions; use codex_protocol::ThreadId; +use codex_protocol::auth::AuthMode; use codex_protocol::config_types::AltScreenMode; use codex_protocol::config_types::SandboxMode; #[cfg(target_os = "windows")] @@ -1869,7 +1869,7 @@ fn determine_alt_screen_mode(no_alt_screen: bool, tui_alternate_screen: AltScree #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LoginStatus { - AuthMode(AppServerAuthMode), + AuthMode(AuthMode), NotAuthenticated, } @@ -1886,8 +1886,8 @@ async fn get_login_status( let account = app_server.read_account().await?; Ok(match account.account { - Some(AppServerAccount::ApiKey {}) => LoginStatus::AuthMode(AppServerAuthMode::ApiKey), - Some(AppServerAccount::Chatgpt { .. }) => LoginStatus::AuthMode(AppServerAuthMode::Chatgpt), + Some(AppServerAccount::ApiKey {}) => LoginStatus::AuthMode(AuthMode::ApiKey), + Some(AppServerAccount::Chatgpt { .. }) => LoginStatus::AuthMode(AuthMode::Chatgpt), Some(AppServerAccount::AmazonBedrock { .. }) => LoginStatus::NotAuthenticated, None => LoginStatus::NotAuthenticated, }) diff --git a/codex-rs/tui/src/local_chatgpt_auth.rs b/codex-rs/tui/src/local_chatgpt_auth.rs index 13b88d3d56f2..a09d8bc9b95f 100644 --- a/codex-rs/tui/src/local_chatgpt_auth.rs +++ b/codex-rs/tui/src/local_chatgpt_auth.rs @@ -2,10 +2,10 @@ use std::path::Path; -use codex_app_server_protocol::AuthMode; use codex_config::types::AuthCredentialsStoreMode; use codex_login::AuthKeyringBackendKind; use codex_login::load_auth_dot_json; +use codex_protocol::auth::AuthMode; #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct LocalChatgptAuth { @@ -64,11 +64,11 @@ mod tests { use base64::Engine; use chrono::Utc; - use codex_app_server_protocol::AuthMode; use codex_login::AuthDotJson; use codex_login::auth::login_with_chatgpt_auth_tokens; use codex_login::save_auth; use codex_login::token_data::TokenData; + use codex_protocol::auth::AuthMode; use pretty_assertions::assert_eq; use serde::Serialize; use serde_json::json; diff --git a/codex-rs/tui/src/onboarding/auth.rs b/codex-rs/tui/src/onboarding/auth.rs index 8ba409f7c9ed..f9392eb2e868 100644 --- a/codex-rs/tui/src/onboarding/auth.rs +++ b/codex-rs/tui/src/onboarding/auth.rs @@ -10,13 +10,13 @@ use codex_app_server_client::AppServerRequestHandle; use codex_app_server_protocol::AccountLoginCompletedNotification; use codex_app_server_protocol::AccountUpdatedNotification; -#[cfg(test)] -use codex_app_server_protocol::AuthMode as AppServerAuthMode; +use codex_app_server_protocol::AuthMode as ApiAuthMode; use codex_app_server_protocol::CancelLoginAccountParams; use codex_app_server_protocol::ClientRequest; use codex_app_server_protocol::LoginAccountParams; use codex_app_server_protocol::LoginAccountResponse; use codex_login::read_openai_api_key_from_env; +use codex_protocol::auth::AuthMode; use crossterm::event::KeyCode; use crossterm::event::KeyEvent; use crossterm::event::KeyEventKind; @@ -945,7 +945,16 @@ impl AuthModeWidget { pub(crate) fn on_account_updated(&mut self, notification: AccountUpdatedNotification) { self.login_status = notification .auth_mode - .map(LoginStatus::AuthMode) + .map(|auth_mode| { + LoginStatus::AuthMode(match auth_mode { + ApiAuthMode::ApiKey => AuthMode::ApiKey, + ApiAuthMode::Chatgpt => AuthMode::Chatgpt, + ApiAuthMode::ChatgptAuthTokens => AuthMode::ChatgptAuthTokens, + ApiAuthMode::AgentIdentity => AuthMode::AgentIdentity, + ApiAuthMode::PersonalAccessToken => AuthMode::PersonalAccessToken, + ApiAuthMode::BedrockApiKey => AuthMode::BedrockApiKey, + }) + }) .unwrap_or(LoginStatus::NotAuthenticated); } } @@ -1111,10 +1120,7 @@ mod tests { #[tokio::test] async fn existing_non_oauth_chatgpt_login_counts_as_signed_in() { - for auth_mode in [ - AppServerAuthMode::ChatgptAuthTokens, - AppServerAuthMode::PersonalAccessToken, - ] { + for auth_mode in [AuthMode::ChatgptAuthTokens, AuthMode::PersonalAccessToken] { let (mut widget, _tmp) = widget_forced_chatgpt().await; widget.login_status = LoginStatus::AuthMode(auth_mode);