Decouple managed configuration loading from ChatGPT authentication (#56295)

## What changed

Introduce a `BundleSource` interface for authentication, cache identity, bundle fetching, and unauthorized-request recovery. Move ChatGPT plan eligibility and credential recovery into `ChatGptSource`, while keeping validation, caching, and retry orchestration in the shared service.

Support source-specific cache namespaces while preserving the existing ChatGPT cache location. Retain one authentication recovery context across all retries of a bundle fetch.

## Testing

Extend the external authentication test to verify that recovery happens only once per bundle fetch, including when an intervening request failure triggers a retry.

GitOrigin-RevId: f5009f72dc4479f96d4ed1da9232f250988444a5
This commit is contained in:
smurching-oai
2026-10-22 23:38:09 +00:00
committed by copyberry
parent eca79b000c
commit 5a1c247dd5
8 changed files with 334 additions and 203 deletions
+6 -5
View File
@@ -1,5 +1,6 @@
use crate::backend::BackendBundleClient;
use crate::backend::BundleClient;
use crate::chatgpt::ChatGptSource;
use crate::config_source::BundleSource;
use crate::service::CLOUD_CONFIG_BUNDLE_TIMEOUT;
use crate::service::CloudConfigBundleService;
use codex_config::CloudConfigBundleLoader;
@@ -11,12 +12,12 @@ use std::sync::Arc;
use tokio::task::AbortHandle;
use tokio::task::JoinHandle;
struct CloudConfigBundleLoaderLifetime<C> {
struct CloudConfigBundleLoaderLifetime<C: BundleSource> {
service: Arc<CloudConfigBundleService<C>>,
refresh_task: AbortHandle,
}
impl<C> Drop for CloudConfigBundleLoaderLifetime<C> {
impl<C: BundleSource> Drop for CloudConfigBundleLoaderLifetime<C> {
fn drop(&mut self) {
self.refresh_task.abort();
}
@@ -45,7 +46,7 @@ pub(crate) fn cloud_config_bundle_loader_for_service<C>(
service: CloudConfigBundleService<C>,
) -> (CloudConfigBundleLoader, JoinHandle<()>)
where
C: BundleClient + 'static,
C: BundleSource + 'static,
{
let service = Arc::new(service);
let background_service = Arc::clone(&service);
@@ -102,7 +103,7 @@ pub async fn cloud_config_bundle_loader_for_storage_without_cache(
async fn cloud_config_bundle_service_for_storage(
auth_config: AuthConfig,
enable_codex_api_key_env: bool,
) -> std::io::Result<CloudConfigBundleService<BackendBundleClient>> {
) -> std::io::Result<CloudConfigBundleService<ChatGptSource<BackendBundleClient>>> {
let auth_manager =
AuthManager::shared_from_auth_config(auth_config.clone(), enable_codex_api_key_env).await?;
Ok(CloudConfigBundleService::new(