mirror of
https://github.com/openai/codex.git
synced 2026-10-27 18:48:37 +00:00
Clarify config loader APIs and share interactive login setup (#57169)
## What changed - Rename ChatGPT-specific bundle loaders to `chatgpt_config_bundle_loader*` and use managed-config terminology for shared loader plumbing. - Pass `Config` directly when updating the app-server's managed configuration loader. - Share configuration loading, logging setup, and result handling between browser and device-code login. - Expose the CLI cloud-config helpers and `provider_uses_first_party_auth_path` through their library APIs. ## Testing Extend CLI login coverage to verify that `forced_login_method="api"` rejects both `login` and `login --device-auth` while preserving saved API-key credentials. GitOrigin-RevId: 9e8fa6110dc80882ba365e550b48ac77da604940
This commit is contained in:
@@ -75,7 +75,7 @@ impl ConfigManager {
|
||||
.map_err(io::Error::other)?;
|
||||
let revision = self.network_policy.policy().revision();
|
||||
let local_sources = self.load_local_network_policy(revision).await?;
|
||||
let cloud_config = self.current_cloud_config_bundle();
|
||||
let cloud_config = self.current_managed_config_loader();
|
||||
let result = async {
|
||||
let cloud_snapshot = cloud_config.get_snapshot().await;
|
||||
let cloud = cloud_snapshot.bundle.clone().map_err(io::Error::other)?;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_cloud_config::cloud_config_bundle_loader;
|
||||
use codex_cloud_config::chatgpt_config_bundle_loader;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLayerStack;
|
||||
use codex_config::LoaderOverrides;
|
||||
@@ -174,7 +174,7 @@ impl ConfigManager {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) fn current_cloud_config_bundle(&self) -> CloudConfigBundleLoader {
|
||||
pub(crate) fn current_managed_config_loader(&self) -> CloudConfigBundleLoader {
|
||||
self.cloud_config_bundle
|
||||
.read()
|
||||
.map(|guard| guard.clone())
|
||||
@@ -191,12 +191,13 @@ impl ConfigManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn replace_cloud_config_bundle_loader(
|
||||
pub(crate) fn update_managed_config_loader(
|
||||
&self,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
chatgpt_base_url: String,
|
||||
http_client_factory: codex_http_client::HttpClientFactory,
|
||||
config: &Config,
|
||||
) {
|
||||
let chatgpt_base_url = config.chatgpt_base_url.clone();
|
||||
let http_client_factory = config.http_client_factory();
|
||||
let endpoint = codex_backend_client::Client::new(
|
||||
chatgpt_base_url.clone(),
|
||||
http_client_factory.clone(),
|
||||
@@ -207,7 +208,7 @@ impl ConfigManager {
|
||||
.policy()
|
||||
.restrict_to_endpoints(endpoint.parse().into_iter().collect()),
|
||||
);
|
||||
let loader = cloud_config_bundle_loader(
|
||||
let loader = chatgpt_config_bundle_loader(
|
||||
auth_manager,
|
||||
chatgpt_base_url,
|
||||
self.codex_home.clone(),
|
||||
@@ -222,7 +223,7 @@ impl ConfigManager {
|
||||
self.revoke_mcp_ema();
|
||||
}
|
||||
|
||||
pub(crate) fn clear_cloud_config_bundle_loader(&self) {
|
||||
pub(crate) fn clear_chatgpt_config_loader(&self) {
|
||||
if let Ok(mut guard) = self.cloud_config_bundle.write() {
|
||||
guard.retire_ema_policy();
|
||||
*guard = CloudConfigBundleLoader::default();
|
||||
@@ -409,7 +410,7 @@ impl ConfigManager {
|
||||
|
||||
/// Load local policy before auth, then fetch the account's cloud configuration.
|
||||
/// Callers choose their error/recovery policy before initializing any runtimes.
|
||||
pub(crate) async fn load_cloud_startup_config(
|
||||
pub(crate) async fn load_managed_startup_config(
|
||||
&self,
|
||||
cwd: Option<PathBuf>,
|
||||
) -> std::io::Result<CloudStartupConfig> {
|
||||
@@ -418,11 +419,7 @@ impl ConfigManager {
|
||||
AuthManager::shared_from_config(&bootstrap, /*enable_codex_api_key_env*/ false)
|
||||
.await
|
||||
.map_err(std::io::Error::other)?;
|
||||
self.replace_cloud_config_bundle_loader(
|
||||
auth,
|
||||
bootstrap.chatgpt_base_url.clone(),
|
||||
bootstrap.http_client_factory(),
|
||||
);
|
||||
self.update_managed_config_loader(auth, &bootstrap);
|
||||
let config = self.load_latest_config(cwd).await;
|
||||
Ok(CloudStartupConfig { bootstrap, config })
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ async fn catalog_reload_uses_latest_model_provider() -> Result<()> {
|
||||
);
|
||||
let startup = manager.load_non_project_config().await?;
|
||||
assert_eq!(startup.model_provider_id, "gateway");
|
||||
manager.clear_cloud_config_bundle_loader();
|
||||
manager.clear_chatgpt_config_loader();
|
||||
let refreshed = manager
|
||||
.load_startup_config(/*fallback_cwd*/ None)
|
||||
.await?
|
||||
|
||||
@@ -126,11 +126,7 @@ pub(super) async fn configure(
|
||||
)
|
||||
.await
|
||||
.map_err(IoError::other)?;
|
||||
config_manager.replace_cloud_config_bundle_loader(
|
||||
bootstrap_auth_manager,
|
||||
bootstrap_config.chatgpt_base_url.clone(),
|
||||
bootstrap_config.http_client_factory(),
|
||||
);
|
||||
config_manager.update_managed_config_loader(bootstrap_auth_manager, &bootstrap_config);
|
||||
let policy_config = config_manager
|
||||
.load_startup_config(Some(config.cwd.to_path_buf()))
|
||||
.await?
|
||||
@@ -168,11 +164,7 @@ pub(super) async fn configure(
|
||||
config.application_auth_route_config = policy_config.application_auth_route_config;
|
||||
config.forced_login_method = policy_config.forced_login_method;
|
||||
config.forced_chatgpt_workspace_id = policy_config.forced_chatgpt_workspace_id;
|
||||
config_manager.replace_cloud_config_bundle_loader(
|
||||
policy_auth_manager,
|
||||
config.chatgpt_base_url.clone(),
|
||||
config.http_client_factory(),
|
||||
);
|
||||
config_manager.update_managed_config_loader(policy_auth_manager, config);
|
||||
codex_login::default_client::set_default_client_residency_requirement(
|
||||
config.enforce_residency.value(),
|
||||
);
|
||||
|
||||
@@ -569,7 +569,7 @@ pub async fn run_main_with_transport_options(
|
||||
Arc::new(NoopThreadConfigLoader),
|
||||
);
|
||||
let startup = config_manager
|
||||
.load_cloud_startup_config(startup_cwd.clone())
|
||||
.load_managed_startup_config(startup_cwd.clone())
|
||||
.await?;
|
||||
let mut config_warnings = Vec::new();
|
||||
let mut plugin_startup_config = PluginStartupConfig::Current;
|
||||
@@ -600,11 +600,7 @@ pub async fn run_main_with_transport_options(
|
||||
AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false)
|
||||
.await
|
||||
.map_err(std::io::Error::other)?;
|
||||
config_manager.replace_cloud_config_bundle_loader(
|
||||
auth_manager.clone(),
|
||||
config.chatgpt_base_url.clone(),
|
||||
config.http_client_factory(),
|
||||
);
|
||||
config_manager.update_managed_config_loader(auth_manager.clone(), &config);
|
||||
config_manager
|
||||
.sync_default_client_residency_requirement()
|
||||
.await;
|
||||
|
||||
@@ -476,7 +476,7 @@ impl AccountRequestProcessor {
|
||||
) {
|
||||
Ok(()) => {
|
||||
self.auth_manager.reload().await;
|
||||
self.config_manager.clear_cloud_config_bundle_loader();
|
||||
self.config_manager.clear_chatgpt_config_loader();
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => Err(internal_error(format!("failed to save api key: {err}"))),
|
||||
@@ -575,7 +575,7 @@ impl AccountRequestProcessor {
|
||||
}
|
||||
.map_err(|err| internal_error(format!("failed to save Amazon Bedrock auth: {err}")))?;
|
||||
self.auth_manager.reload().await;
|
||||
self.config_manager.clear_cloud_config_bundle_loader();
|
||||
self.config_manager.clear_chatgpt_config_loader();
|
||||
Ok(LoginAccountResponse::AmazonBedrock {})
|
||||
}
|
||||
.await;
|
||||
@@ -910,11 +910,8 @@ impl AccountRequestProcessor {
|
||||
)))
|
||||
.await
|
||||
.map_err(|err| internal_error(format!("failed to set external auth: {err}")))?;
|
||||
self.config_manager.replace_cloud_config_bundle_loader(
|
||||
self.auth_manager.clone(),
|
||||
self.config.chatgpt_base_url.clone(),
|
||||
self.config.http_client_factory(),
|
||||
);
|
||||
self.config_manager
|
||||
.update_managed_config_loader(self.auth_manager.clone(), &self.config);
|
||||
self.config_manager
|
||||
.sync_default_client_residency_requirement()
|
||||
.await;
|
||||
@@ -986,11 +983,8 @@ impl AccountRequestProcessor {
|
||||
let auth_changes = self.auth_manager.auth_change_state_receiver();
|
||||
let owner_generation = auth_changes.borrow().owner_generation;
|
||||
self.thread_manager.invalidate_mcp_runtimes().await;
|
||||
self.config_manager.replace_cloud_config_bundle_loader(
|
||||
self.auth_manager.clone(),
|
||||
self.config.chatgpt_base_url.clone(),
|
||||
self.config.http_client_factory(),
|
||||
);
|
||||
self.config_manager
|
||||
.update_managed_config_loader(self.auth_manager.clone(), &self.config);
|
||||
self.config_manager
|
||||
.sync_default_client_residency_requirement()
|
||||
.await;
|
||||
@@ -1542,11 +1536,7 @@ pub(crate) async fn logout_configured_account(
|
||||
// Another process may have changed the persisted workspace. Reload both
|
||||
// account authority and its policy before selecting a grant to remove.
|
||||
auth_manager.reload().await;
|
||||
config_manager.replace_cloud_config_bundle_loader(
|
||||
Arc::clone(auth_manager),
|
||||
fallback_config.chatgpt_base_url.clone(),
|
||||
fallback_config.http_client_factory(),
|
||||
);
|
||||
config_manager.update_managed_config_loader(Arc::clone(auth_manager), fallback_config);
|
||||
let config = config_manager
|
||||
.load_latest_config(/*fallback_cwd*/ None)
|
||||
.await;
|
||||
@@ -1602,7 +1592,7 @@ pub(crate) async fn clear_account_configuration(
|
||||
config_manager: &ConfigManager,
|
||||
config: &Config,
|
||||
) -> Result<(), JSONRPCErrorError> {
|
||||
config_manager.clear_cloud_config_bundle_loader();
|
||||
config_manager.clear_chatgpt_config_loader();
|
||||
if config.model_provider.is_amazon_bedrock() {
|
||||
clear_user_model_provider_if_bedrock(config_manager, config).await?;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ async fn read_startup_config(
|
||||
);
|
||||
let cwd = std::env::current_dir().ok();
|
||||
let loaded = manager
|
||||
.load_cloud_startup_config(cwd.clone())
|
||||
.load_managed_startup_config(cwd.clone())
|
||||
.await
|
||||
.map_err(startup_error)?;
|
||||
let config = match loaded.config {
|
||||
@@ -143,11 +143,7 @@ async fn read_startup_config(
|
||||
set_default_client_residency_requirement(config.enforce_residency.value());
|
||||
let config = logout_configured_account(&manager, &auth, &config).await?;
|
||||
clear_account_configuration(&manager, &config).await?;
|
||||
manager.replace_cloud_config_bundle_loader(
|
||||
Arc::clone(&auth),
|
||||
config.chatgpt_base_url.clone(),
|
||||
config.http_client_factory(),
|
||||
);
|
||||
manager.update_managed_config_loader(Arc::clone(&auth), &config);
|
||||
manager
|
||||
.load_latest_config(cwd)
|
||||
.await
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use codex_app_server::in_process::EmbeddedNetworkPolicy;
|
||||
use codex_cloud_config::cloud_config_bundle_loader_for_storage;
|
||||
use codex_cloud_config::chatgpt_config_bundle_loader_for_storage;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLoadOptions;
|
||||
use codex_core::config::Config;
|
||||
@@ -14,7 +14,7 @@ use codex_core::config::load_config_toml_with_layer_stack;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_cli::CliConfigOverrides;
|
||||
|
||||
pub(crate) async fn load_config(
|
||||
pub async fn load_config(
|
||||
config_overrides: &CliConfigOverrides,
|
||||
loader_overrides: LoaderOverrides,
|
||||
) -> Result<Config> {
|
||||
@@ -31,7 +31,7 @@ pub(crate) async fn load_config(
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub(crate) async fn config_builder(
|
||||
pub async fn config_builder(
|
||||
config_overrides: &CliConfigOverrides,
|
||||
loader_overrides: LoaderOverrides,
|
||||
harness_overrides: ConfigOverrides,
|
||||
@@ -58,7 +58,7 @@ pub(crate) async fn config_builder(
|
||||
)
|
||||
.await
|
||||
.context("failed to load bootstrap configuration")?;
|
||||
let cloud_config_bundle = cloud_config_bundle_loader_for_storage(
|
||||
let cloud_config_bundle = chatgpt_config_bundle_loader_for_storage(
|
||||
network_policy.bind_bootstrap_auth(
|
||||
bootstrap_auth_config(codex_home.as_path(), &bootstrap_config)
|
||||
.context("failed to resolve cloud configuration authentication")?,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use codex_cloud_config::cloud_config_bundle_loader_for_storage;
|
||||
use codex_cloud_config::chatgpt_config_bundle_loader_for_storage;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLoadOptions;
|
||||
use codex_core::config::bootstrap_auth_config;
|
||||
@@ -40,7 +40,7 @@ pub(super) async fn bootstrap_cloud_config_bundle(
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(cloud_config_bundle_loader_for_storage(
|
||||
Ok(chatgpt_config_bundle_loader_for_storage(
|
||||
bootstrap_auth_config(codex_home.as_path(), &bootstrap_config)?,
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_cloud_config::cloud_config_bundle_loader_for_storage;
|
||||
use codex_cloud_config::chatgpt_config_bundle_loader_for_storage;
|
||||
use codex_config::LoaderOverrides;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
@@ -506,7 +506,7 @@ async fn load_exec_server_config(
|
||||
)
|
||||
.await?;
|
||||
let bootstrap_auth_config = bootstrap_auth_config(&codex_home, &bootstrap_config)?;
|
||||
let cloud_config_bundle = cloud_config_bundle_loader_for_storage(
|
||||
let cloud_config_bundle = chatgpt_config_bundle_loader_for_storage(
|
||||
bootstrap_auth_config,
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod cloud_config;
|
||||
pub(crate) mod debug_sandbox;
|
||||
mod exit_status;
|
||||
pub(crate) mod login;
|
||||
|
||||
+69
-61
@@ -172,29 +172,8 @@ pub async fn login_with_chatgpt(
|
||||
server.block_until_done().await
|
||||
}
|
||||
|
||||
pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
let config = load_config_or_exit(cli_config_overrides).await;
|
||||
let _login_log_guard = init_login_file_logging(&config);
|
||||
tracing::info!("starting browser login flow");
|
||||
|
||||
if !config
|
||||
.auth_config()
|
||||
.is_login_method_allowed(ForcedLoginMethod::Chatgpt)
|
||||
{
|
||||
eprintln!("{CHATGPT_LOGIN_DISABLED_MESSAGE}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let effective_chatgpt_workspaces = config.auth_config().effective_chatgpt_workspaces();
|
||||
match login_with_chatgpt(
|
||||
config.codex_home.to_path_buf(),
|
||||
effective_chatgpt_workspaces,
|
||||
config.cli_auth_credentials_store_mode,
|
||||
config.auth_keyring_backend_kind(),
|
||||
config.auth_route_config(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
fn finish_login(result: std::io::Result<()>) -> ! {
|
||||
match result {
|
||||
Ok(_) => {
|
||||
eprintln!("{LOGIN_SUCCESS_MESSAGE}");
|
||||
std::process::exit(0);
|
||||
@@ -206,6 +185,43 @@ pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) ->
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_interactive_login<F>(
|
||||
cli_config_overrides: CliConfigOverrides,
|
||||
login: impl FnOnce(Config) -> F,
|
||||
) -> !
|
||||
where
|
||||
F: std::future::Future<Output = std::io::Result<()>>,
|
||||
{
|
||||
let config = load_config_or_exit(cli_config_overrides).await;
|
||||
let _login_log_guard = init_login_file_logging(&config);
|
||||
finish_login(login(config).await)
|
||||
}
|
||||
|
||||
pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
run_interactive_login(cli_config_overrides, |config| async move {
|
||||
tracing::info!("starting browser login flow");
|
||||
|
||||
if !config
|
||||
.auth_config()
|
||||
.is_login_method_allowed(ForcedLoginMethod::Chatgpt)
|
||||
{
|
||||
eprintln!("{CHATGPT_LOGIN_DISABLED_MESSAGE}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let effective_chatgpt_workspaces = config.auth_config().effective_chatgpt_workspaces();
|
||||
login_with_chatgpt(
|
||||
config.codex_home.to_path_buf(),
|
||||
effective_chatgpt_workspaces,
|
||||
config.cli_auth_credentials_store_mode,
|
||||
config.auth_keyring_backend_kind(),
|
||||
config.auth_route_config(),
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn run_login_with_api_key(
|
||||
cli_config_overrides: CliConfigOverrides,
|
||||
api_key: String,
|
||||
@@ -326,46 +342,38 @@ pub async fn run_login_with_device_code(
|
||||
issuer_base_url: Option<String>,
|
||||
client_id: Option<String>,
|
||||
) -> ! {
|
||||
let config = load_config_or_exit(cli_config_overrides).await;
|
||||
let _login_log_guard = init_login_file_logging(&config);
|
||||
tracing::info!("starting device code login flow");
|
||||
if !config
|
||||
.auth_config()
|
||||
.is_login_method_allowed(ForcedLoginMethod::Chatgpt)
|
||||
{
|
||||
eprintln!("{CHATGPT_LOGIN_DISABLED_MESSAGE}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let auth_route_config = config.auth_route_config();
|
||||
clear_existing_auth_before_login(
|
||||
&config.codex_home,
|
||||
config.cli_auth_credentials_store_mode,
|
||||
config.auth_keyring_backend_kind(),
|
||||
&auth_route_config,
|
||||
)
|
||||
.await;
|
||||
let effective_chatgpt_workspaces = config.auth_config().effective_chatgpt_workspaces();
|
||||
let mut opts = ServerOptions::new(
|
||||
config.codex_home.to_path_buf(),
|
||||
client_id.unwrap_or(CLIENT_ID.to_string()),
|
||||
effective_chatgpt_workspaces,
|
||||
config.cli_auth_credentials_store_mode,
|
||||
config.auth_keyring_backend_kind(),
|
||||
auth_route_config,
|
||||
);
|
||||
if let Some(iss) = issuer_base_url {
|
||||
opts.issuer = iss;
|
||||
}
|
||||
match run_device_code_login(opts).await {
|
||||
Ok(()) => {
|
||||
eprintln!("{LOGIN_SUCCESS_MESSAGE}");
|
||||
std::process::exit(0);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error logging in with device code: {e}");
|
||||
run_interactive_login(cli_config_overrides, |config| async move {
|
||||
tracing::info!("starting device code login flow");
|
||||
if !config
|
||||
.auth_config()
|
||||
.is_login_method_allowed(ForcedLoginMethod::Chatgpt)
|
||||
{
|
||||
eprintln!("{CHATGPT_LOGIN_DISABLED_MESSAGE}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
let auth_route_config = config.auth_route_config();
|
||||
clear_existing_auth_before_login(
|
||||
&config.codex_home,
|
||||
config.cli_auth_credentials_store_mode,
|
||||
config.auth_keyring_backend_kind(),
|
||||
&auth_route_config,
|
||||
)
|
||||
.await;
|
||||
let effective_chatgpt_workspaces = config.auth_config().effective_chatgpt_workspaces();
|
||||
let mut opts = ServerOptions::new(
|
||||
config.codex_home.to_path_buf(),
|
||||
client_id.unwrap_or(CLIENT_ID.to_string()),
|
||||
effective_chatgpt_workspaces,
|
||||
config.cli_auth_credentials_store_mode,
|
||||
config.auth_keyring_backend_kind(),
|
||||
auth_route_config,
|
||||
);
|
||||
if let Some(iss) = issuer_base_url {
|
||||
opts.issuer = iss;
|
||||
}
|
||||
run_device_code_login(opts).await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Prefers device-code login (with `open_browser = false`) when headless environment is detected, but keeps
|
||||
|
||||
@@ -10,6 +10,7 @@ use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_arg0::arg0_dispatch_or_else;
|
||||
use codex_chatgpt::apply_command::ApplyCommand;
|
||||
use codex_chatgpt::apply_command::run_apply_command;
|
||||
use codex_cli::cloud_config;
|
||||
use codex_cli::read_access_token_from_stdin;
|
||||
use codex_cli::read_api_key_from_stdin;
|
||||
use codex_cli::run_login_status;
|
||||
@@ -52,7 +53,6 @@ static ALLOCATOR: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows"))]
|
||||
mod app_cmd;
|
||||
mod cloud_config;
|
||||
mod config_override_args;
|
||||
mod daemon_install;
|
||||
mod daemon_telemetry;
|
||||
|
||||
@@ -84,6 +84,16 @@ fn login_with_api_key_reads_stdin_and_writes_auth_json() -> Result<()> {
|
||||
Authentication is checked when you send a request.
|
||||
");
|
||||
|
||||
for args in [vec!["login"], vec!["login", "--device-auth"]] {
|
||||
codex_command(codex_home.path())?
|
||||
.args(["-c", "forced_login_method=\"api\""])
|
||||
.args(args)
|
||||
.assert()
|
||||
.failure()
|
||||
.stderr(contains("ChatGPT login is disabled"));
|
||||
assert_eq!(read_auth_json(codex_home.path())?, auth);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn managed_config_bundle_loader(
|
||||
.network_policy()
|
||||
.clone()
|
||||
.restrict_to_endpoints(endpoint.parse().into_iter().collect());
|
||||
cloud_config_bundle_loader(
|
||||
chatgpt_config_bundle_loader(
|
||||
auth_manager,
|
||||
chatgpt_base_url,
|
||||
codex_home,
|
||||
@@ -68,8 +68,8 @@ pub async fn managed_config_bundle_loader_for_storage(
|
||||
));
|
||||
}
|
||||
let service =
|
||||
cloud_config_bundle_service_for_storage(auth_config, enable_codex_api_key_env).await?;
|
||||
Ok(cloud_config_bundle_loader_for_service(service).0)
|
||||
chatgpt_config_bundle_service_for_storage(auth_config, enable_codex_api_key_env).await?;
|
||||
Ok(managed_config_bundle_loader_for_service(service).0)
|
||||
}
|
||||
|
||||
struct CloudConfigBundleLoaderLifetime<C: BundleSource> {
|
||||
@@ -83,7 +83,7 @@ impl<C: BundleSource> Drop for CloudConfigBundleLoaderLifetime<C> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cloud_config_bundle_loader(
|
||||
pub fn chatgpt_config_bundle_loader(
|
||||
auth_manager: Arc<AuthManager>,
|
||||
chatgpt_base_url: String,
|
||||
codex_home: PathBuf,
|
||||
@@ -98,11 +98,11 @@ pub fn cloud_config_bundle_loader(
|
||||
codex_home,
|
||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||
);
|
||||
let (loader, _) = cloud_config_bundle_loader_for_service(service);
|
||||
let (loader, _) = managed_config_bundle_loader_for_service(service);
|
||||
loader
|
||||
}
|
||||
|
||||
pub(crate) fn cloud_config_bundle_loader_for_service<C>(
|
||||
pub(crate) fn managed_config_bundle_loader_for_service<C>(
|
||||
service: CloudConfigBundleService<C>,
|
||||
) -> (CloudConfigBundleLoader, JoinHandle<()>)
|
||||
where
|
||||
@@ -133,24 +133,24 @@ where
|
||||
(loader, refresh_task)
|
||||
}
|
||||
|
||||
pub async fn cloud_config_bundle_loader_for_storage(
|
||||
pub async fn chatgpt_config_bundle_loader_for_storage(
|
||||
auth_config: AuthConfig,
|
||||
enable_codex_api_key_env: bool,
|
||||
) -> std::io::Result<CloudConfigBundleLoader> {
|
||||
let service =
|
||||
cloud_config_bundle_service_for_storage(auth_config, enable_codex_api_key_env).await?;
|
||||
let (loader, _) = cloud_config_bundle_loader_for_service(service);
|
||||
chatgpt_config_bundle_service_for_storage(auth_config, enable_codex_api_key_env).await?;
|
||||
let (loader, _) = managed_config_bundle_loader_for_service(service);
|
||||
Ok(loader)
|
||||
}
|
||||
|
||||
/// Fetches directly from the network on each load, without reading or writing
|
||||
/// the disk cache or starting a background refresher.
|
||||
pub async fn cloud_config_bundle_loader_for_storage_without_cache(
|
||||
pub async fn chatgpt_config_bundle_loader_for_storage_without_cache(
|
||||
auth_config: AuthConfig,
|
||||
enable_codex_api_key_env: bool,
|
||||
) -> std::io::Result<CloudConfigBundleLoader> {
|
||||
let service = Arc::new(
|
||||
cloud_config_bundle_service_for_storage(auth_config, enable_codex_api_key_env)
|
||||
chatgpt_config_bundle_service_for_storage(auth_config, enable_codex_api_key_env)
|
||||
.await?
|
||||
.without_cache(),
|
||||
);
|
||||
@@ -160,7 +160,7 @@ pub async fn cloud_config_bundle_loader_for_storage_without_cache(
|
||||
}))
|
||||
}
|
||||
|
||||
async fn cloud_config_bundle_service_for_storage(
|
||||
async fn chatgpt_config_bundle_service_for_storage(
|
||||
auth_config: AuthConfig,
|
||||
enable_codex_api_key_env: bool,
|
||||
) -> std::io::Result<CloudConfigBundleService<ChatGptSource<BackendBundleClient>>> {
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn config_server_bundle_loader(
|
||||
codex_home,
|
||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||
);
|
||||
crate::bundle_loader::cloud_config_bundle_loader_for_service(service).0
|
||||
crate::bundle_loader::managed_config_bundle_loader_for_service(service).0
|
||||
}
|
||||
Err(error) => CloudConfigBundleLoader::new(std::future::ready(Err(error))),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![cfg(test)]
|
||||
|
||||
use super::*;
|
||||
use crate::bundle_loader::cloud_config_bundle_loader_for_service;
|
||||
use crate::bundle_loader::managed_config_bundle_loader_for_service;
|
||||
use crate::service::CLOUD_CONFIG_BUNDLE_TIMEOUT;
|
||||
use crate::service::CloudConfigBundleService;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
@@ -27,7 +27,7 @@ fn remote_loader(url: String, codex_home: PathBuf) -> CloudConfigBundleLoader {
|
||||
.http_client_factory()
|
||||
.clone();
|
||||
let source = ConfigServerSource::new(url, /*oauth*/ None, codex_home.clone(), factory).unwrap();
|
||||
cloud_config_bundle_loader_for_service(CloudConfigBundleService::from_source(
|
||||
managed_config_bundle_loader_for_service(CloudConfigBundleService::from_source(
|
||||
source,
|
||||
codex_home,
|
||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||
|
||||
@@ -13,9 +13,9 @@ mod metrics;
|
||||
mod service;
|
||||
mod validation;
|
||||
|
||||
pub use bundle_loader::cloud_config_bundle_loader;
|
||||
pub use bundle_loader::cloud_config_bundle_loader_for_storage;
|
||||
pub use bundle_loader::cloud_config_bundle_loader_for_storage_without_cache;
|
||||
pub use bundle_loader::chatgpt_config_bundle_loader;
|
||||
pub use bundle_loader::chatgpt_config_bundle_loader_for_storage;
|
||||
pub use bundle_loader::chatgpt_config_bundle_loader_for_storage_without_cache;
|
||||
pub use bundle_loader::managed_config_auth_manager;
|
||||
pub use bundle_loader::managed_config_bundle_loader;
|
||||
pub use bundle_loader::managed_config_bundle_loader_for_storage;
|
||||
|
||||
@@ -1347,7 +1347,7 @@ async fn production_loader_recovers_startup_timeouts_in_background() {
|
||||
codex_home.path().to_path_buf(),
|
||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||
);
|
||||
let (loader, _) = crate::bundle_loader::cloud_config_bundle_loader_for_service(service);
|
||||
let (loader, _) = crate::bundle_loader::managed_config_bundle_loader_for_service(service);
|
||||
let (initial, ()) = tokio::join!(loader.get(), async {
|
||||
fetcher.request_started.notified().await;
|
||||
tokio::time::advance(CLOUD_CONFIG_BUNDLE_TIMEOUT + Duration::from_millis(1)).await;
|
||||
@@ -1427,7 +1427,7 @@ async fn production_loader_restores_normal_refresh_interval_after_non_timeout_er
|
||||
codex_home.path().to_path_buf(),
|
||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||
);
|
||||
let (loader, _) = crate::bundle_loader::cloud_config_bundle_loader_for_service(service);
|
||||
let (loader, _) = crate::bundle_loader::managed_config_bundle_loader_for_service(service);
|
||||
let (initial, ()) = tokio::join!(loader.get(), async {
|
||||
fetcher.request_started.notified().await;
|
||||
tokio::time::advance(CLOUD_CONFIG_BUNDLE_TIMEOUT + Duration::from_millis(1)).await;
|
||||
@@ -1499,7 +1499,7 @@ async fn production_loader_refreshes_later_configs_and_preserves_failed_refreshe
|
||||
codex_home.path().to_path_buf(),
|
||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||
);
|
||||
let (loader, _) = crate::bundle_loader::cloud_config_bundle_loader_for_service(service);
|
||||
let (loader, _) = crate::bundle_loader::managed_config_bundle_loader_for_service(service);
|
||||
|
||||
let (first, second) = tokio::join!(loader.get(), loader.get());
|
||||
assert_eq!(first, Ok(Some(initial_bundle.clone())));
|
||||
@@ -1556,7 +1556,7 @@ async fn each_loader_owns_refresh_until_its_last_clone_is_dropped() {
|
||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||
);
|
||||
let (loader, original_task) =
|
||||
crate::bundle_loader::cloud_config_bundle_loader_for_service(service);
|
||||
crate::bundle_loader::managed_config_bundle_loader_for_service(service);
|
||||
let cloned_loader = loader.clone();
|
||||
assert_eq!(loader.get().await, Ok(Some(test_bundle())));
|
||||
fetcher.request_started.notified().await;
|
||||
@@ -1582,7 +1582,7 @@ async fn each_loader_owns_refresh_until_its_last_clone_is_dropped() {
|
||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||
);
|
||||
let (replacement_loader, replacement_task) =
|
||||
crate::bundle_loader::cloud_config_bundle_loader_for_service(replacement_service);
|
||||
crate::bundle_loader::managed_config_bundle_loader_for_service(replacement_service);
|
||||
assert_eq!(replacement_loader.get().await, Ok(Some(test_bundle())));
|
||||
replacement_fetcher.request_started.notified().await;
|
||||
|
||||
@@ -1629,7 +1629,7 @@ async fn dropping_loader_cancels_in_flight_startup_and_refresh() {
|
||||
codex_home.path().to_path_buf(),
|
||||
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
||||
);
|
||||
let (loader, _) = crate::bundle_loader::cloud_config_bundle_loader_for_service(service);
|
||||
let (loader, _) = crate::bundle_loader::managed_config_bundle_loader_for_service(service);
|
||||
if starts_from_cache {
|
||||
assert_eq!(loader.get().await, Ok(Some(test_bundle())));
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
@@ -59,7 +59,7 @@ use codex_app_server_protocol::TurnStartParams;
|
||||
use codex_app_server_protocol::TurnStartResponse;
|
||||
use codex_app_server_protocol::TurnStartedNotification;
|
||||
use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_cloud_config::cloud_config_bundle_loader_for_storage;
|
||||
use codex_cloud_config::chatgpt_config_bundle_loader_for_storage;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLoadError;
|
||||
use codex_config::ConfigLoadOptions;
|
||||
@@ -408,7 +408,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
CloudConfigBundleLoader::default(),
|
||||
)
|
||||
.await;
|
||||
let gate_cloud_config = cloud_config_bundle_loader_for_storage(
|
||||
let gate_cloud_config = chatgpt_config_bundle_loader_for_storage(
|
||||
embedded_network_policy
|
||||
.bind_bootstrap_auth(bootstrap_auth_config(&codex_home, &gate_bootstrap)?),
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
@@ -508,7 +508,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
// API keys cannot fetch workspace-managed configuration. Preserve the
|
||||
// existing ChatGPT bootstrap identity even when model requests allow
|
||||
// CODEX_API_KEY.
|
||||
let cloud_config_bundle = cloud_config_bundle_loader_for_storage(
|
||||
let cloud_config_bundle = chatgpt_config_bundle_loader_for_storage(
|
||||
bootstrap_auth_config,
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
)
|
||||
|
||||
@@ -42,6 +42,7 @@ pub use provider::ProviderAuthRecoveryMessages;
|
||||
pub use provider::ProviderUnauthorizedRecovery;
|
||||
pub use provider::SharedModelProvider;
|
||||
pub use provider::create_model_provider;
|
||||
pub use provider::provider_uses_first_party_auth_path;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "workspace_routing_tests.rs"]
|
||||
|
||||
@@ -333,7 +333,7 @@ pub type ModelProviderFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a
|
||||
/// Shared runtime model provider handle.
|
||||
pub type SharedModelProvider = Arc<dyn ModelProvider>;
|
||||
|
||||
fn provider_uses_first_party_auth_path(provider: &ModelProviderInfo) -> bool {
|
||||
pub fn provider_uses_first_party_auth_path(provider: &ModelProviderInfo) -> bool {
|
||||
provider.requires_openai_auth
|
||||
&& provider.env_key.is_none()
|
||||
&& provider.experimental_bearer_token.is_none()
|
||||
|
||||
@@ -47,7 +47,7 @@ use codex_app_server_protocol::ThreadListCwdFilter;
|
||||
use codex_app_server_protocol::ThreadListParams;
|
||||
use codex_app_server_protocol::ThreadSortKey as AppServerThreadSortKey;
|
||||
use codex_app_server_protocol::ThreadSourceKind;
|
||||
use codex_cloud_config::cloud_config_bundle_loader_for_storage;
|
||||
use codex_cloud_config::chatgpt_config_bundle_loader_for_storage;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLoadError;
|
||||
use codex_config::LoaderOverrides;
|
||||
@@ -1098,7 +1098,7 @@ async fn cloud_config_bundle_for_app_server_target(
|
||||
codex_home: &Path,
|
||||
embedded_network_policy: &codex_app_server_client::EmbeddedNetworkPolicy,
|
||||
) -> std::io::Result<CloudConfigBundleLoader> {
|
||||
cloud_config_bundle_loader_for_storage(
|
||||
chatgpt_config_bundle_loader_for_storage(
|
||||
embedded_network_policy
|
||||
.bind_bootstrap_auth(app_server_target.auth_config_for_cloud_loader(
|
||||
bootstrap_auth_config(codex_home, bootstrap_config)?,
|
||||
@@ -1415,7 +1415,7 @@ async fn run_ratatui_app(
|
||||
// bundle and rebuild config. This avoids missing newly available cloud-managed
|
||||
// policy due to login status detection edge cases.
|
||||
if show_login_screen && !uses_remote_workspace && !workload_identity_selected {
|
||||
cloud_config_bundle = cloud_config_bundle_loader_for_storage(
|
||||
cloud_config_bundle = chatgpt_config_bundle_loader_for_storage(
|
||||
embedded_network_policy.bind_bootstrap_auth(initial_config.auth_config()),
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
)
|
||||
|
||||
@@ -1126,7 +1126,7 @@ mod tests {
|
||||
use codex_app_server_client::InProcessClientStartArgs;
|
||||
use codex_app_server_client::RemoteAppServerEndpoint;
|
||||
use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_cloud_config::cloud_config_bundle_loader_for_storage;
|
||||
use codex_cloud_config::chatgpt_config_bundle_loader_for_storage;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::sync::Arc;
|
||||
@@ -1234,7 +1234,7 @@ mod tests {
|
||||
cli_overrides: Vec::new(),
|
||||
loader_overrides: Default::default(),
|
||||
strict_config: false,
|
||||
cloud_config_bundle: cloud_config_bundle_loader_for_storage(
|
||||
cloud_config_bundle: chatgpt_config_bundle_loader_for_storage(
|
||||
auth_config.clone(),
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
)
|
||||
|
||||
@@ -508,7 +508,7 @@ pub(super) async fn run_main_inner(
|
||||
cloud_config_bundle
|
||||
} else {
|
||||
startup_draft
|
||||
.run_until(cloud_config_bundle_loader_for_storage(
|
||||
.run_until(chatgpt_config_bundle_loader_for_storage(
|
||||
embedded_network_policy.bind_bootstrap_auth(
|
||||
app_server_target.auth_config_for_cloud_loader(config.auth_config()),
|
||||
),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::bail;
|
||||
use codex_cloud_config::cloud_config_bundle_loader_for_storage_without_cache;
|
||||
use codex_cloud_config::chatgpt_config_bundle_loader_for_storage_without_cache;
|
||||
use codex_config::ConfigLoadOptions;
|
||||
use codex_config::ConfigRequirementsToml;
|
||||
use codex_config::WindowsSandboxImplementationToml;
|
||||
@@ -75,7 +75,7 @@ pub(crate) fn validate_provisioning_request(
|
||||
.context("load bootstrap configuration")?;
|
||||
// Use the default cloud-policy endpoint unless managed requirements override it.
|
||||
bootstrap_config.config_toml.chatgpt_base_url = None;
|
||||
let cloud_config_bundle = cloud_config_bundle_loader_for_storage_without_cache(
|
||||
let cloud_config_bundle = chatgpt_config_bundle_loader_for_storage_without_cache(
|
||||
bootstrap_auth_config(codex_home, &bootstrap_config)
|
||||
.context("resolve cloud configuration authentication")?,
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
|
||||
Reference in New Issue
Block a user