diff --git a/src/main.rs b/src/main.rs index 729fc98..4e808fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -367,8 +367,14 @@ async fn main() -> Result<()> { api_key, provider, memory, - } = cli.command + } = &cli.command { + let interactive = *interactive; + let channels_only = *channels_only; + let api_key = api_key.clone(); + let provider = provider.clone(); + let memory = memory.clone(); + if interactive && channels_only { bail!("Use either --interactive or --channels-only, not both"); } diff --git a/src/onboard/wizard.rs b/src/onboard/wizard.rs index b352d2a..94305b6 100644 --- a/src/onboard/wizard.rs +++ b/src/onboard/wizard.rs @@ -467,7 +467,7 @@ fn default_model_for_provider(provider: &str) -> String { "groq" => "llama-3.3-70b-versatile".into(), "deepseek" => "deepseek-chat".into(), "gemini" => "gemini-2.5-pro".into(), - _ => "anthropic/claude-sonnet-4-5".into(), + _ => "anthropic/claude-sonnet-4.5".into(), } } @@ -475,7 +475,7 @@ fn curated_models_for_provider(provider_name: &str) -> Vec<(String, String)> { match canonical_provider_name(provider_name) { "openrouter" => vec![ ( - "anthropic/claude-sonnet-4-5".to_string(), + "anthropic/claude-sonnet-4.5".to_string(), "Claude Sonnet 4.5 (balanced, recommended)".to_string(), ), ( @@ -4345,6 +4345,16 @@ mod tests { assert!(ids.contains(&"gpt-5-mini".to_string())); } + #[test] + fn curated_models_for_openrouter_use_valid_anthropic_id() { + let ids: Vec = curated_models_for_provider("openrouter") + .into_iter() + .map(|(id, _)| id) + .collect(); + + assert!(ids.contains(&"anthropic/claude-sonnet-4.5".to_string())); + } + #[test] fn supports_live_model_fetch_for_supported_and_unsupported_providers() { assert!(supports_live_model_fetch("openai")); diff --git a/src/providers/anthropic.rs b/src/providers/anthropic.rs index fb940e9..4216853 100644 --- a/src/providers/anthropic.rs +++ b/src/providers/anthropic.rs @@ -139,7 +139,9 @@ impl AnthropicProvider { credential: &str, ) -> reqwest::RequestBuilder { if Self::is_setup_token(credential) { - request.header("Authorization", format!("Bearer {credential}")) + request + .header("Authorization", format!("Bearer {credential}")) + .header("anthropic-beta", "oauth-2025-04-20") } else { request.header("x-api-key", credential) } @@ -474,6 +476,56 @@ mod tests { assert!(!AnthropicProvider::is_setup_token("sk-ant-api-key")); } + #[test] + fn apply_auth_uses_bearer_and_beta_for_setup_tokens() { + let provider = AnthropicProvider::new(None); + let request = provider + .apply_auth( + provider.client.get("https://api.anthropic.com/v1/models"), + "sk-ant-oat01-test-token", + ) + .build() + .expect("request should build"); + + assert_eq!( + request + .headers() + .get("authorization") + .and_then(|v| v.to_str().ok()), + Some("Bearer sk-ant-oat01-test-token") + ); + assert_eq!( + request + .headers() + .get("anthropic-beta") + .and_then(|v| v.to_str().ok()), + Some("oauth-2025-04-20") + ); + assert!(request.headers().get("x-api-key").is_none()); + } + + #[test] + fn apply_auth_uses_x_api_key_for_regular_tokens() { + let provider = AnthropicProvider::new(None); + let request = provider + .apply_auth( + provider.client.get("https://api.anthropic.com/v1/models"), + "sk-ant-api-key", + ) + .build() + .expect("request should build"); + + assert_eq!( + request + .headers() + .get("x-api-key") + .and_then(|v| v.to_str().ok()), + Some("sk-ant-api-key") + ); + assert!(request.headers().get("authorization").is_none()); + assert!(request.headers().get("anthropic-beta").is_none()); + } + #[tokio::test] async fn chat_with_system_fails_without_key() { let p = AnthropicProvider::new(None);