mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
ec848dde0e
## Stack This is PR 2 of the simplified HAI single-run-task stack: - [#19047](https://github.com/openai/codex/pull/19047) Agent Identity assertion and task-registration primitives, including the shared run-task helper used by existing Agent Identity JWT auth. - [#19049](https://github.com/openai/codex/pull/19049) Disabled-by-default ChatGPT auth opt-in that provisions/reuses persisted Agent Identity runtime auth and its single run task. - [#19051](https://github.com/openai/codex/pull/19051) Run-scoped provider auth that uses one backend-owned task id for first-party inference and compaction requests. [#19054](https://github.com/openai/codex/pull/19054) collapsed out of the active stack because the simplified design no longer needs a separate background/control-plane task helper. ## Summary This PR adds the disabled-by-default path for normal ChatGPT-login Codex sessions to obtain Agent Identity runtime auth through the Codex backend. Existing Agent Identity JWT startup mode remains a separate path and does not require the feature flag. What changed: - adds the experimental `use_agent_identity` feature flag and config schema entry - adds an explicit `AgentIdentityAuthPolicy` so call sites choose `JwtOnly` or `ChatGptAuth` instead of passing a bare boolean - stores standalone Agent Identity JWT credentials separately from backend-registered Agent Identity records - persists the registered Agent Identity record, private key, and single run task id in `auth.json` so process restarts reuse the same identity - derives the agent/task registration base URL from ChatGPT/Codex auth config while keeping JWT JWKS lookup separate - provisions and caches ChatGPT-derived Agent Identity runtime auth when `use_agent_identity` is enabled - reuses the shared run-task registration helper from PR1 rather than adding a second task-registration path This PR intentionally does not switch model inference over to `AgentAssertion` auth. The provider-auth integration lands in the next PR. ## Testing - `just test -p codex-login`
73 lines
2.6 KiB
Rust
73 lines
2.6 KiB
Rust
use crate::backend::BackendBundleClient;
|
|
use crate::service::CLOUD_CONFIG_BUNDLE_TIMEOUT;
|
|
use crate::service::CloudConfigBundleService;
|
|
use codex_config::CloudConfigBundleLoadError;
|
|
use codex_config::CloudConfigBundleLoadErrorCode;
|
|
use codex_config::CloudConfigBundleLoader;
|
|
use codex_config::types::AuthCredentialsStoreMode;
|
|
use codex_login::AuthKeyringBackendKind;
|
|
use codex_login::AuthManager;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
use std::sync::Mutex;
|
|
use std::sync::OnceLock;
|
|
use tokio::task::JoinHandle;
|
|
|
|
fn refresher_task_slot() -> &'static Mutex<Option<JoinHandle<()>>> {
|
|
static REFRESHER_TASK: OnceLock<Mutex<Option<JoinHandle<()>>>> = OnceLock::new();
|
|
REFRESHER_TASK.get_or_init(|| Mutex::new(None))
|
|
}
|
|
|
|
pub fn cloud_config_bundle_loader(
|
|
auth_manager: Arc<AuthManager>,
|
|
chatgpt_base_url: String,
|
|
codex_home: PathBuf,
|
|
) -> CloudConfigBundleLoader {
|
|
let service = CloudConfigBundleService::new(
|
|
auth_manager,
|
|
Arc::new(BackendBundleClient::new(chatgpt_base_url)),
|
|
codex_home,
|
|
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
|
);
|
|
let refresh_service = service.clone();
|
|
let task = tokio::spawn(async move { service.load_startup_bundle_with_timeout().await });
|
|
let refresh_task =
|
|
tokio::spawn(async move { refresh_service.refresh_cache_in_background().await });
|
|
let mut refresher_guard = refresher_task_slot().lock().unwrap_or_else(|err| {
|
|
tracing::warn!("cloud config bundle refresher task slot was poisoned");
|
|
err.into_inner()
|
|
});
|
|
if let Some(existing_task) = refresher_guard.replace(refresh_task) {
|
|
existing_task.abort();
|
|
}
|
|
CloudConfigBundleLoader::new(async move {
|
|
task.await.map_err(|err| {
|
|
tracing::error!(error = %err, "Cloud config bundle task failed");
|
|
CloudConfigBundleLoadError::new(
|
|
CloudConfigBundleLoadErrorCode::Internal,
|
|
/*status_code*/ None,
|
|
format!("cloud config bundle load failed: {err}"),
|
|
)
|
|
})?
|
|
})
|
|
}
|
|
|
|
pub async fn cloud_config_bundle_loader_for_storage(
|
|
codex_home: PathBuf,
|
|
enable_codex_api_key_env: bool,
|
|
credentials_store_mode: AuthCredentialsStoreMode,
|
|
keyring_backend_kind: AuthKeyringBackendKind,
|
|
chatgpt_base_url: String,
|
|
) -> CloudConfigBundleLoader {
|
|
let auth_manager = AuthManager::shared(
|
|
codex_home.clone(),
|
|
enable_codex_api_key_env,
|
|
credentials_store_mode,
|
|
/*forced_chatgpt_workspace_id*/ None,
|
|
Some(chatgpt_base_url.clone()),
|
|
keyring_backend_kind,
|
|
)
|
|
.await;
|
|
cloud_config_bundle_loader(auth_manager, chatgpt_base_url, codex_home)
|
|
}
|