mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
120ac92e77
- Fix envObjToString to preserve custom environment variables - Add bidirectional MCP format conversion (httpUrl <-> url) - Merge provider config with existing settings.json instead of overwriting - Remove redundant is_packycode_gemini function - Simplify is_google_official_gemini using detect_gemini_auth_type - Add handleGeminiModelChange for consistent field handling
143 lines
4.4 KiB
Rust
143 lines
4.4 KiB
Rust
//! Gemini authentication type detection
|
|
//!
|
|
//! Detects whether a Gemini provider uses PackyCode API Key, Google OAuth, or generic API Key.
|
|
|
|
use crate::error::AppError;
|
|
use crate::provider::Provider;
|
|
|
|
/// Gemini authentication type enumeration
|
|
///
|
|
/// Used to optimize performance by avoiding repeated provider type detection.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub(crate) enum GeminiAuthType {
|
|
/// PackyCode provider (uses API Key)
|
|
Packycode,
|
|
/// Google Official (uses OAuth)
|
|
GoogleOfficial,
|
|
/// Generic Gemini provider (uses API Key)
|
|
Generic,
|
|
}
|
|
|
|
// Partner Promotion Key constants
|
|
const PACKYCODE_PARTNER_KEY: &str = "packycode";
|
|
const GOOGLE_OFFICIAL_PARTNER_KEY: &str = "google-official";
|
|
|
|
// PackyCode keyword constants
|
|
const PACKYCODE_KEYWORDS: [&str; 3] = ["packycode", "packyapi", "packy"];
|
|
|
|
/// Detect Gemini provider authentication type
|
|
///
|
|
/// One-time detection to avoid repeated calls to `is_packycode_gemini` and `is_google_official_gemini`.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// - `GeminiAuthType::GoogleOfficial`: Google official, uses OAuth
|
|
/// - `GeminiAuthType::Packycode`: PackyCode provider, uses API Key
|
|
/// - `GeminiAuthType::Generic`: Other generic providers, uses API Key
|
|
pub(crate) fn detect_gemini_auth_type(provider: &Provider) -> GeminiAuthType {
|
|
// Priority 1: Check partner_promotion_key (most reliable)
|
|
if let Some(key) = provider
|
|
.meta
|
|
.as_ref()
|
|
.and_then(|meta| meta.partner_promotion_key.as_deref())
|
|
{
|
|
if key.eq_ignore_ascii_case(GOOGLE_OFFICIAL_PARTNER_KEY) {
|
|
return GeminiAuthType::GoogleOfficial;
|
|
}
|
|
if key.eq_ignore_ascii_case(PACKYCODE_PARTNER_KEY) {
|
|
return GeminiAuthType::Packycode;
|
|
}
|
|
}
|
|
|
|
// Priority 2: Check Google Official (name matching)
|
|
let name_lower = provider.name.to_ascii_lowercase();
|
|
if name_lower == "google" || name_lower.starts_with("google ") {
|
|
return GeminiAuthType::GoogleOfficial;
|
|
}
|
|
|
|
// Priority 3: Check PackyCode keywords
|
|
if contains_packycode_keyword(&provider.name) {
|
|
return GeminiAuthType::Packycode;
|
|
}
|
|
|
|
if let Some(site) = provider.website_url.as_deref() {
|
|
if contains_packycode_keyword(site) {
|
|
return GeminiAuthType::Packycode;
|
|
}
|
|
}
|
|
|
|
if let Some(base_url) = provider
|
|
.settings_config
|
|
.pointer("/env/GOOGLE_GEMINI_BASE_URL")
|
|
.and_then(|v| v.as_str())
|
|
{
|
|
if contains_packycode_keyword(base_url) {
|
|
return GeminiAuthType::Packycode;
|
|
}
|
|
}
|
|
|
|
GeminiAuthType::Generic
|
|
}
|
|
|
|
/// Check if string contains PackyCode related keywords (case-insensitive)
|
|
///
|
|
/// Keyword list: ["packycode", "packyapi", "packy"]
|
|
fn contains_packycode_keyword(value: &str) -> bool {
|
|
let lower = value.to_ascii_lowercase();
|
|
PACKYCODE_KEYWORDS
|
|
.iter()
|
|
.any(|keyword| lower.contains(keyword))
|
|
}
|
|
|
|
/// Detect if provider is Google Official Gemini (uses OAuth authentication)
|
|
///
|
|
/// Google Official Gemini uses OAuth personal authentication, no API Key needed.
|
|
///
|
|
/// This is a convenience wrapper around `detect_gemini_auth_type`.
|
|
pub(crate) fn is_google_official_gemini(provider: &Provider) -> bool {
|
|
detect_gemini_auth_type(provider) == GeminiAuthType::GoogleOfficial
|
|
}
|
|
|
|
/// Ensure Google Official Gemini provider security flag is correctly set (OAuth mode)
|
|
///
|
|
/// Google Official Gemini uses OAuth personal authentication, no API Key needed.
|
|
///
|
|
/// # What it does
|
|
///
|
|
/// Writes to **`~/.gemini/settings.json`** (Gemini client config).
|
|
///
|
|
/// # Value set
|
|
///
|
|
/// ```json
|
|
/// {
|
|
/// "security": {
|
|
/// "auth": {
|
|
/// "selectedType": "oauth-personal"
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// # OAuth authentication flow
|
|
///
|
|
/// 1. User switches to Google Official provider
|
|
/// 2. CC-Switch sets `selectedType = "oauth-personal"`
|
|
/// 3. User's first use of Gemini CLI will auto-open browser for OAuth login
|
|
/// 4. After successful login, credentials saved in Gemini credential store
|
|
/// 5. Subsequent requests auto-use saved credentials
|
|
///
|
|
/// # Error handling
|
|
///
|
|
/// If provider is not Google Official, function returns `Ok(())` immediately without any operation.
|
|
pub(crate) fn ensure_google_oauth_security_flag(provider: &Provider) -> Result<(), AppError> {
|
|
if !is_google_official_gemini(provider) {
|
|
return Ok(());
|
|
}
|
|
|
|
// Write to Gemini directory settings.json (~/.gemini/settings.json)
|
|
use crate::gemini_config::write_google_oauth_settings;
|
|
write_google_oauth_settings()?;
|
|
|
|
Ok(())
|
|
}
|