feat(codex): preserve OAuth login state during third-party provider switching

Codex provider switches now only write config.toml for third-party providers,
injecting the API key as experimental_bearer_token. The user's auth.json
(ChatGPT OAuth tokens) is preserved. Official providers with login material
still write auth.json normally. Backfill restores bearer tokens into stored
provider auth.OPENAI_API_KEY to maintain canonical shape.
This commit is contained in:
Jason
2026-05-25 17:46:23 +08:00
parent 88ba908bc4
commit 95f2dd4126
17 changed files with 1664 additions and 154 deletions
+38 -2
View File
@@ -427,14 +427,19 @@ impl CodexAdapter {
fn extract_key(&self, provider: &Provider) -> Option<String> {
// 1. 尝试从 env 中获取
if let Some(env) = provider.settings_config.get("env") {
if let Some(key) = env.get("OPENAI_API_KEY").and_then(|v| v.as_str()) {
if let Some(key) = env
.get("OPENAI_API_KEY")
.and_then(|v| v.as_str())
.map(str::trim)
.filter(|key| !key.is_empty())
{
return Some(key.to_string());
}
}
// 2. 尝试从 auth 中获取 (Codex CLI 格式)
if let Some(auth) = provider.settings_config.get("auth") {
if let Some(key) = auth.get("OPENAI_API_KEY").and_then(|v| v.as_str()) {
if let Some(key) = crate::codex_config::extract_codex_auth_api_key(auth) {
return Some(key.to_string());
}
}
@@ -445,6 +450,8 @@ impl CodexAdapter {
.get("apiKey")
.or_else(|| provider.settings_config.get("api_key"))
.and_then(|v| v.as_str())
.map(str::trim)
.filter(|key| !key.is_empty())
{
return Some(key.to_string());
}
@@ -455,9 +462,19 @@ impl CodexAdapter {
.get("api_key")
.or_else(|| config.get("apiKey"))
.and_then(|v| v.as_str())
.map(str::trim)
.filter(|key| !key.is_empty())
{
return Some(key.to_string());
}
if let Some(config_str) = config.as_str() {
if let Some(key) =
crate::codex_config::extract_codex_experimental_bearer_token(config_str)
{
return Some(key);
}
}
}
None
@@ -619,6 +636,25 @@ mod tests {
assert_eq!(auth.strategy, AuthStrategy::Bearer);
}
#[test]
fn test_extract_auth_falls_back_to_config_bearer_when_auth_key_empty() {
let adapter = CodexAdapter::new();
let provider = create_provider(json!({
"auth": {
"OPENAI_API_KEY": ""
},
"config": r#"model_provider = "custom"
[model_providers.custom]
experimental_bearer_token = "sk-config-key"
"#
}));
let auth = adapter.extract_auth(&provider).unwrap();
assert_eq!(auth.api_key, "sk-config-key");
assert_eq!(auth.strategy, AuthStrategy::Bearer);
}
#[test]
fn test_extract_auth_from_env() {
let adapter = CodexAdapter::new();