diff --git a/src-tauri/src/gemini_mcp.rs b/src-tauri/src/gemini_mcp.rs index 62a959c18..72d7a86d5 100644 --- a/src-tauri/src/gemini_mcp.rs +++ b/src-tauri/src/gemini_mcp.rs @@ -134,6 +134,33 @@ pub fn set_mcp_servers_map( obj.remove("homepage"); obj.remove("docs"); + // Timeout 转换:Claude/Codex 使用 startup_timeout_sec/tool_timeout_sec + // Gemini CLI 只支持 timeout(单位 ms) + // 默认值:startup=10s, tool=60s + const DEFAULT_STARTUP_MS: u64 = 10_000; + const DEFAULT_TOOL_MS: u64 = 60_000; + + let extract_timeout = + |obj: &mut Map, key: &str, multiplier: u64| -> Option { + obj.remove(key).and_then(|val| { + val.as_u64() + .map(|n| n * multiplier) + .or_else(|| val.as_f64().map(|f| (f * multiplier as f64) as u64)) + }) + }; + + // 分别收集 startup 和 tool timeout,未设置时使用默认值 + let startup_ms = extract_timeout(&mut obj, "startup_timeout_sec", 1000) + .or_else(|| extract_timeout(&mut obj, "startup_timeout_ms", 1)) + .unwrap_or(DEFAULT_STARTUP_MS); + let tool_ms = extract_timeout(&mut obj, "tool_timeout_sec", 1000) + .or_else(|| extract_timeout(&mut obj, "tool_timeout_ms", 1)) + .unwrap_or(DEFAULT_TOOL_MS); + + // 取最大值作为 Gemini timeout + let final_timeout = startup_ms.max(tool_ms); + obj.insert("timeout".to_string(), Value::Number(final_timeout.into())); + out.insert(id.clone(), Value::Object(obj)); } diff --git a/src-tauri/src/proxy/providers/claude.rs b/src-tauri/src/proxy/providers/claude.rs index 87a0d85c9..04d4ec6ff 100644 --- a/src-tauri/src/proxy/providers/claude.rs +++ b/src-tauri/src/proxy/providers/claude.rs @@ -62,7 +62,8 @@ impl ClaudeAdapter { let normalized = value.trim().to_lowercase(); normalized == "true" || normalized == "1" } - _ => true, + // OpenRouter now supports Claude Code compatible API, default to passthrough + _ => false, } } @@ -465,12 +466,22 @@ mod tests { })); assert!(!adapter.needs_transform(&anthropic_provider)); + // OpenRouter provider without explicit setting now defaults to passthrough (no transform) let openrouter_provider = create_provider(json!({ "env": { "ANTHROPIC_BASE_URL": "https://openrouter.ai/api" } })); - assert!(adapter.needs_transform(&openrouter_provider)); + assert!(!adapter.needs_transform(&openrouter_provider)); + + // OpenRouter provider with explicit compat mode enabled should transform + let openrouter_enabled = create_provider(json!({ + "env": { + "ANTHROPIC_BASE_URL": "https://openrouter.ai/api" + }, + "openrouter_compat_mode": true + })); + assert!(adapter.needs_transform(&openrouter_enabled)); let openrouter_disabled = create_provider(json!({ "env": { diff --git a/src-tauri/src/services/provider/live.rs b/src-tauri/src/services/provider/live.rs index 6389827b9..0192f6ecd 100644 --- a/src-tauri/src/services/provider/live.rs +++ b/src-tauri/src/services/provider/live.rs @@ -148,6 +148,15 @@ pub fn sync_current_to_live(state: &AppState) -> Result<(), AppError> { // MCP sync McpService::sync_all_enabled(state)?; + + // Skill sync + for app_type in [AppType::Claude, AppType::Codex, AppType::Gemini] { + if let Err(e) = crate::services::skill::SkillService::sync_to_app(&state.db, &app_type) { + log::warn!("同步 Skill 到 {:?} 失败: {}", app_type, e); + // Continue syncing other apps, don't abort + } + } + Ok(()) } diff --git a/src/App.tsx b/src/App.tsx index 62bc543d5..6bbc34386 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -808,6 +808,7 @@ function App() { {effectiveUsageProvider && ( diff --git a/src/components/providers/forms/hooks/useApiKeyState.ts b/src/components/providers/forms/hooks/useApiKeyState.ts index 9cd5fde39..9101d2e35 100644 --- a/src/components/providers/forms/hooks/useApiKeyState.ts +++ b/src/components/providers/forms/hooks/useApiKeyState.ts @@ -43,11 +43,7 @@ export function useApiKeyState({ return; } - // 仅当配置确实包含 API Key 字段时才同步(避免无意清空用户正在输入的 key) - if (!hasApiKeyField(initialConfig, appType)) { - return; - } - + // 从配置中提取 API Key(如果不存在则返回空字符串) const extracted = getApiKeyFromConfig(initialConfig, appType); if (extracted !== apiKey) { setApiKey(extracted); diff --git a/src/components/providers/forms/hooks/useBaseUrlState.ts b/src/components/providers/forms/hooks/useBaseUrlState.ts index 61b1ad997..2deffcbac 100644 --- a/src/components/providers/forms/hooks/useBaseUrlState.ts +++ b/src/components/providers/forms/hooks/useBaseUrlState.ts @@ -41,8 +41,9 @@ export function useBaseUrlState({ try { const config = JSON.parse(settingsConfig || "{}"); const envUrl: unknown = config?.env?.ANTHROPIC_BASE_URL; - if (typeof envUrl === "string" && envUrl && envUrl.trim() !== baseUrl) { - setBaseUrl(envUrl.trim()); + const nextUrl = typeof envUrl === "string" ? envUrl.trim() : ""; + if (nextUrl !== baseUrl) { + setBaseUrl(nextUrl); } } catch { // ignore