fix(auth): rebase PR #200 onto main and restore auth CLI flow

This commit is contained in:
Chummy 2026-02-18 12:56:00 +08:00
parent 96109d46d1
commit d42cb1e906
11 changed files with 594 additions and 44 deletions

View file

@ -6,7 +6,6 @@ use crate::tools::ToolSpec;
use async_trait::async_trait;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
pub struct AnthropicProvider {
credential: Option<String>,
@ -408,6 +407,7 @@ impl Provider for AnthropicProvider {
#[cfg(test)]
mod tests {
use super::*;
use crate::auth::anthropic_token::{detect_auth_kind, AnthropicAuthKind};
#[test]
fn creates_with_key() {

View file

@ -374,7 +374,21 @@ fn parse_custom_provider_url(
/// Factory: create the right provider from config (without custom URL)
pub fn create_provider(name: &str, api_key: Option<&str>) -> anyhow::Result<Box<dyn Provider>> {
create_provider_with_url(name, api_key, None)
create_provider_with_options(name, api_key, &ProviderRuntimeOptions::default())
}
/// Factory: create provider with runtime options (auth profile override, state dir).
pub fn create_provider_with_options(
name: &str,
api_key: Option<&str>,
options: &ProviderRuntimeOptions,
) -> anyhow::Result<Box<dyn Provider>> {
match name {
"openai-codex" | "openai_codex" | "codex" => {
Ok(Box::new(openai_codex::OpenAiCodexProvider::new(options)))
}
_ => create_provider_with_url(name, api_key, None),
}
}
/// Factory: create the right provider from config with optional custom base URL
@ -561,6 +575,7 @@ pub fn create_resilient_provider(
create_resilient_provider_with_options(
primary_name,
api_key,
api_url,
reliability,
&ProviderRuntimeOptions::default(),
)
@ -570,23 +585,27 @@ pub fn create_resilient_provider(
pub fn create_resilient_provider_with_options(
primary_name: &str,
api_key: Option<&str>,
api_url: Option<&str>,
reliability: &crate::config::ReliabilityConfig,
options: &ProviderRuntimeOptions,
) -> anyhow::Result<Box<dyn Provider>> {
let mut providers: Vec<(String, Box<dyn Provider>)> = Vec::new();
providers.push((
primary_name.to_string(),
create_provider_with_url(primary_name, api_key, api_url)?,
));
let primary_provider = match primary_name {
"openai-codex" | "openai_codex" | "codex" => {
create_provider_with_options(primary_name, api_key, options)?
}
_ => create_provider_with_url(primary_name, api_key, api_url)?,
};
providers.push((primary_name.to_string(), primary_provider));
for fallback in &reliability.fallback_providers {
if fallback == primary_name || providers.iter().any(|(name, _)| name == fallback) {
continue;
}
// Fallback providers don't use the custom api_url (it's specific to primary)
match create_provider(fallback, api_key) {
// Fallback providers don't use the custom api_url (it's specific to primary).
match create_provider_with_options(fallback, api_key, options) {
Ok(provider) => providers.push((fallback.clone(), provider)),
Err(_error) => {
tracing::warn!(
@ -718,6 +737,12 @@ pub fn list_providers() -> Vec<ProviderInfo> {
aliases: &[],
local: false,
},
ProviderInfo {
name: "openai-codex",
display_name: "OpenAI Codex (OAuth)",
aliases: &["openai_codex", "codex"],
local: false,
},
ProviderInfo {
name: "ollama",
display_name: "Ollama",

View file

@ -195,7 +195,7 @@ fn extract_stream_event_text(event: &Value, saw_delta: bool) -> Option<String> {
Some("response.output_text.done") if !saw_delta => {
nonempty_preserve(event.get("text").and_then(Value::as_str))
}
Some("response.completed") | Some("response.done") => event
Some("response.completed" | "response.done") => event
.get("response")
.and_then(|value| serde_json::from_value::<ResponsesResponse>(value.clone()).ok())
.and_then(|response| extract_responses_text(&response)),