diff --git a/src-tauri/src/proxy/forwarder.rs b/src-tauri/src/proxy/forwarder.rs index 864e5771..109264c9 100644 --- a/src-tauri/src/proxy/forwarder.rs +++ b/src-tauri/src/proxy/forwarder.rs @@ -856,9 +856,22 @@ impl RequestForwarder { // 过滤黑名单 Headers,保护隐私并避免冲突 for (key, value) in headers { + let key_str = key.as_str(); if HEADER_BLACKLIST .iter() - .any(|h| key.as_str().eq_ignore_ascii_case(h)) + .any(|h| key_str.eq_ignore_ascii_case(h)) + { + continue; + } + // Copilot 请求:过滤会由 add_auth_headers 注入的固定指纹头, + // 防止客户端原始头与注入头重复(reqwest header() 是追加语义) + if is_copilot + && (key_str.eq_ignore_ascii_case("user-agent") + || key_str.eq_ignore_ascii_case("editor-version") + || key_str.eq_ignore_ascii_case("editor-plugin-version") + || key_str.eq_ignore_ascii_case("copilot-integration-id") + || key_str.eq_ignore_ascii_case("x-github-api-version") + || key_str.eq_ignore_ascii_case("openai-intent")) { continue; } diff --git a/src-tauri/src/proxy/providers/claude.rs b/src-tauri/src/proxy/providers/claude.rs index 6c63a050..e20449dd 100644 --- a/src-tauri/src/proxy/providers/claude.rs +++ b/src-tauri/src/proxy/providers/claude.rs @@ -346,12 +346,15 @@ impl ProviderAdapter for ClaudeAdapter { AuthStrategy::Bearer => { request.header("Authorization", format!("Bearer {}", auth.api_key)) } - // GitHub Copilot: Bearer + 特定的 Editor headers + // GitHub Copilot: Bearer + 统一指纹头 AuthStrategy::GitHubCopilot => request .header("Authorization", format!("Bearer {}", auth.api_key)) - .header("Editor-Version", "vscode/1.85.0") - .header("Editor-Plugin-Version", "copilot/1.150.0") - .header("Copilot-Integration-Id", "vscode-chat"), + .header("editor-version", super::copilot_auth::COPILOT_EDITOR_VERSION) + .header("editor-plugin-version", super::copilot_auth::COPILOT_PLUGIN_VERSION) + .header("copilot-integration-id", super::copilot_auth::COPILOT_INTEGRATION_ID) + .header("user-agent", super::copilot_auth::COPILOT_USER_AGENT) + .header("x-github-api-version", super::copilot_auth::COPILOT_API_VERSION) + .header("openai-intent", "conversation-panel"), _ => request, } } diff --git a/src-tauri/src/proxy/providers/copilot_auth.rs b/src-tauri/src/proxy/providers/copilot_auth.rs index 7fcbf428..589c5742 100644 --- a/src-tauri/src/proxy/providers/copilot_auth.rs +++ b/src-tauri/src/proxy/providers/copilot_auth.rs @@ -46,10 +46,11 @@ const TOKEN_REFRESH_BUFFER_SECONDS: i64 = 60; const COPILOT_MODELS_URL: &str = "https://api.githubcopilot.com/models"; /// Copilot API Header 常量 -const COPILOT_EDITOR_VERSION: &str = "vscode/1.96.0"; -const COPILOT_PLUGIN_VERSION: &str = "copilot-chat/0.26.7"; -const COPILOT_USER_AGENT: &str = "GitHubCopilotChat/0.26.7"; -const COPILOT_API_VERSION: &str = "2025-04-01"; +pub const COPILOT_EDITOR_VERSION: &str = "vscode/1.96.0"; +pub const COPILOT_PLUGIN_VERSION: &str = "copilot-chat/0.26.7"; +pub const COPILOT_USER_AGENT: &str = "GitHubCopilotChat/0.26.7"; +pub const COPILOT_API_VERSION: &str = "2025-04-01"; +pub const COPILOT_INTEGRATION_ID: &str = "vscode-chat"; /// Copilot 使用量 API URL const COPILOT_USAGE_URL: &str = "https://api.github.com/copilot_internal/user"; @@ -465,6 +466,7 @@ impl CopilotAuthManager { .http_client .post(GITHUB_DEVICE_CODE_URL) .header("Accept", "application/json") + .header("User-Agent", COPILOT_USER_AGENT) .form(&[("client_id", GITHUB_CLIENT_ID), ("scope", "read:user")]) .send() .await?; @@ -502,6 +504,7 @@ impl CopilotAuthManager { .http_client .post(GITHUB_OAUTH_TOKEN_URL) .header("Accept", "application/json") + .header("User-Agent", COPILOT_USER_AGENT) .form(&[ ("client_id", GITHUB_CLIENT_ID), ("device_code", device_code), @@ -948,7 +951,9 @@ impl CopilotAuthManager { .http_client .get(GITHUB_USER_URL) .header("Authorization", format!("token {}", github_token)) - .header("User-Agent", "CC-Switch") + .header("User-Agent", COPILOT_USER_AGENT) + .header("Editor-Version", COPILOT_EDITOR_VERSION) + .header("Editor-Plugin-Version", COPILOT_PLUGIN_VERSION) .send() .await?; @@ -978,9 +983,9 @@ impl CopilotAuthManager { .http_client .get(COPILOT_TOKEN_URL) .header("Authorization", format!("token {}", github_token)) - .header("User-Agent", "CC-Switch") - .header("Editor-Version", "vscode/1.85.0") - .header("Editor-Plugin-Version", "copilot/1.150.0") + .header("User-Agent", COPILOT_USER_AGENT) + .header("Editor-Version", COPILOT_EDITOR_VERSION) + .header("Editor-Plugin-Version", COPILOT_PLUGIN_VERSION) .send() .await?; diff --git a/src-tauri/src/services/stream_check.rs b/src-tauri/src/services/stream_check.rs index b889ad46..69f91723 100644 --- a/src-tauri/src/services/stream_check.rs +++ b/src-tauri/src/services/stream_check.rs @@ -13,6 +13,7 @@ use crate::app_config::AppType; use crate::error::AppError; use crate::provider::Provider; use crate::proxy::providers::transform::anthropic_to_openai; +use crate::proxy::providers::copilot_auth; use crate::proxy::providers::{get_adapter, AuthInfo, AuthStrategy}; /// 健康状态枚举 @@ -362,9 +363,12 @@ impl StreamCheckService { .header("content-type", "application/json") .header("accept", "text/event-stream") .header("accept-encoding", "identity") - .header("editor-version", "vscode/1.85.0") - .header("editor-plugin-version", "copilot/1.150.0") - .header("copilot-integration-id", "vscode-chat"); + .header("user-agent", copilot_auth::COPILOT_USER_AGENT) + .header("editor-version", copilot_auth::COPILOT_EDITOR_VERSION) + .header("editor-plugin-version", copilot_auth::COPILOT_PLUGIN_VERSION) + .header("copilot-integration-id", copilot_auth::COPILOT_INTEGRATION_ID) + .header("x-github-api-version", copilot_auth::COPILOT_API_VERSION) + .header("openai-intent", "conversation-panel"); } else if is_openai_chat { // OpenAI-compatible: Bearer auth + standard headers only request_builder = request_builder