diff --git a/src-tauri/src/mcp/mod.rs b/src-tauri/src/mcp/mod.rs index 1081a2800..2f2b7f424 100644 --- a/src-tauri/src/mcp/mod.rs +++ b/src-tauri/src/mcp/mod.rs @@ -29,6 +29,5 @@ pub use gemini::{ sync_single_server_to_gemini, }; pub use opencode::{ - import_from_opencode, remove_server_from_opencode, sync_enabled_to_opencode, - sync_single_server_to_opencode, + import_from_opencode, remove_server_from_opencode, sync_single_server_to_opencode, }; diff --git a/src-tauri/src/mcp/opencode.rs b/src-tauri/src/mcp/opencode.rs index 971d28e3f..0b5873fbb 100644 --- a/src-tauri/src/mcp/opencode.rs +++ b/src-tauri/src/mcp/opencode.rs @@ -15,11 +15,11 @@ use serde_json::{json, Value}; use std::collections::HashMap; -use crate::app_config::{McpApps, McpConfig, McpServer, MultiAppConfig}; +use crate::app_config::{McpApps, McpServer, MultiAppConfig}; use crate::error::AppError; use crate::opencode_config; -use super::validation::{extract_server_spec, validate_server_spec}; +use super::validation::validate_server_spec; // ============================================================================ // Helper Functions @@ -31,29 +31,6 @@ fn should_sync_opencode_mcp() -> bool { opencode_config::get_opencode_dir().exists() } -/// Collect enabled MCP servers for OpenCode -fn collect_enabled_servers(cfg: &McpConfig) -> HashMap { - let mut out = HashMap::new(); - for (id, entry) in cfg.servers.iter() { - let enabled = entry - .get("enabled") - .and_then(|v| v.as_bool()) - .unwrap_or(false); - if !enabled { - continue; - } - match extract_server_spec(entry) { - Ok(spec) => { - out.insert(id.clone(), spec); - } - Err(err) => { - log::warn!("Skip invalid MCP entry '{}': {}", id, err); - } - } - } - out -} - // ============================================================================ // Format Conversion: CC Switch → OpenCode // ============================================================================ @@ -207,31 +184,6 @@ pub fn convert_from_opencode_format(spec: &Value) -> Result { // Public API: Sync Functions // ============================================================================ -/// Sync all enabled_opencode=true servers to OpenCode config -pub fn sync_enabled_to_opencode(config: &MultiAppConfig) -> Result<(), AppError> { - if !should_sync_opencode_mcp() { - return Ok(()); - } - - let enabled = collect_enabled_servers(&config.mcp.opencode); - - // Convert all servers to OpenCode format - let mut opencode_servers = serde_json::Map::new(); - for (id, spec) in enabled { - match convert_to_opencode_format(&spec) { - Ok(opencode_spec) => { - opencode_servers.insert(id, opencode_spec); - } - Err(e) => { - log::warn!("Skip converting MCP server to OpenCode format: {}", e); - } - } - } - - // Write to OpenCode config - opencode_config::set_mcp_servers_batch(&opencode_servers) -} - /// Sync a single MCP server to OpenCode live config pub fn sync_single_server_to_opencode( _config: &MultiAppConfig, diff --git a/src-tauri/src/opencode_config.rs b/src-tauri/src/opencode_config.rs index 769723f9e..36d4f2e95 100644 --- a/src-tauri/src/opencode_config.rs +++ b/src-tauri/src/opencode_config.rs @@ -23,11 +23,10 @@ use crate::config::write_json_file; use crate::error::AppError; -use crate::provider::{OpenCodeModel, OpenCodeProviderConfig, OpenCodeProviderOptions}; +use crate::provider::OpenCodeProviderConfig; use crate::settings::get_opencode_override_dir; use indexmap::IndexMap; use serde_json::{json, Map, Value}; -use std::collections::HashMap; use std::path::PathBuf; // ============================================================================ @@ -108,11 +107,6 @@ pub fn write_opencode_config(config: &Value) -> Result<(), AppError> { Ok(()) } -/// 检查 OpenCode 配置文件是否存在 -pub fn config_exists() -> bool { - get_opencode_config_path().exists() -} - // ============================================================================ // Provider Functions (Untyped - for raw JSON operations) // ============================================================================ @@ -180,43 +174,12 @@ pub fn get_typed_providers() -> Result, Ok(result) } -/// 获取单个供应商配置(类型化) -pub fn get_typed_provider(id: &str) -> Result, AppError> { - let providers = get_providers()?; - - match providers.get(id) { - Some(value) => { - let config = serde_json::from_value::(value.clone()) - .map_err(|e| AppError::JsonSerialize { source: e })?; - Ok(Some(config)) - } - None => Ok(None), - } -} - /// 设置供应商配置(类型化) pub fn set_typed_provider(id: &str, config: &OpenCodeProviderConfig) -> Result<(), AppError> { let value = serde_json::to_value(config).map_err(|e| AppError::JsonSerialize { source: e })?; set_provider(id, value) } -/// 批量设置供应商配置 -pub fn set_providers_batch( - providers: &IndexMap, -) -> Result<(), AppError> { - let mut full_config = read_opencode_config()?; - - let mut provider_map = Map::new(); - for (id, config) in providers { - let value = - serde_json::to_value(config).map_err(|e| AppError::JsonSerialize { source: e })?; - provider_map.insert(id.clone(), value); - } - - full_config["provider"] = Value::Object(provider_map); - write_opencode_config(&full_config) -} - // ============================================================================ // MCP Functions // ============================================================================ @@ -257,86 +220,3 @@ pub fn remove_mcp_server(id: &str) -> Result<(), AppError> { write_opencode_config(&config) } -/// 批量设置 MCP 服务器配置 -pub fn set_mcp_servers_batch(servers: &Map) -> Result<(), AppError> { - let mut full_config = read_opencode_config()?; - full_config["mcp"] = Value::Object(servers.clone()); - write_opencode_config(&full_config) -} - -/// 清空所有 MCP 服务器配置 -pub fn clear_mcp_servers() -> Result<(), AppError> { - let mut config = read_opencode_config()?; - config["mcp"] = json!({}); - write_opencode_config(&config) -} - -// ============================================================================ -// Utility Functions -// ============================================================================ - -/// 创建新的供应商配置 -/// -/// 便捷方法,用于创建 OpenAI 兼容的供应商配置 -pub fn create_provider_config( - npm_package: &str, - base_url: Option<&str>, - api_key: Option<&str>, - models: HashMap, -) -> OpenCodeProviderConfig { - let options = OpenCodeProviderOptions { - base_url: base_url.map(|s| s.to_string()), - api_key: api_key.map(|s| s.to_string()), - headers: None, - }; - - let model_map: HashMap = models - .into_iter() - .map(|(id, name)| (id, OpenCodeModel { name, limit: None })) - .collect(); - - OpenCodeProviderConfig { - npm: npm_package.to_string(), - name: None, - options, - models: model_map, - } -} - -/// 验证供应商配置 -pub fn validate_provider_config(config: &OpenCodeProviderConfig) -> Result<(), AppError> { - // npm package must not be empty - if config.npm.trim().is_empty() { - return Err(AppError::localized( - "opencode.provider.npm.empty", - "npm 包名不能为空", - "npm package name cannot be empty", - )); - } - - // npm package should start with @ or be a valid package name - if !config.npm.starts_with('@') && !config.npm.chars().all(|c| c.is_alphanumeric() || c == '-') - { - log::warn!( - "Unusual npm package name: {}. Expected format like '@ai-sdk/openai'", - config.npm - ); - } - - Ok(()) -} - -/// 从通用 Provider 转换为 OpenCode 配置 -/// -/// 用于从数据库 Provider 结构转换为 OpenCode 配置格式 -pub fn provider_to_opencode_config( - settings_config: &Value, -) -> Result { - serde_json::from_value(settings_config.clone()) - .map_err(|e| AppError::JsonSerialize { source: e }) -} - -/// 将 OpenCode 配置转换为通用 Provider settings_config -pub fn opencode_config_to_provider(config: &OpenCodeProviderConfig) -> Result { - serde_json::to_value(config).map_err(|e| AppError::JsonSerialize { source: e }) -} diff --git a/src-tauri/src/provider.rs b/src-tauri/src/provider.rs index b681411ae..e4a032a8f 100644 --- a/src-tauri/src/provider.rs +++ b/src-tauri/src/provider.rs @@ -545,29 +545,3 @@ pub struct OpenCodeModelLimit { #[serde(skip_serializing_if = "Option::is_none")] pub output: Option, } - -impl OpenCodeProviderConfig { - /// 从 serde_json::Value 解析 - pub fn from_value(value: &Value) -> Result { - serde_json::from_value(value.clone()) - } - - /// 转换为 serde_json::Value - pub fn to_value(&self) -> Result { - serde_json::to_value(self) - } - - /// 获取第一个模型 ID(用于默认模型选择) - pub fn first_model_id(&self) -> Option { - self.models.keys().next().cloned() - } - - /// 检查是否有有效的 API 密钥配置 - pub fn has_api_key(&self) -> bool { - self.options - .api_key - .as_ref() - .map(|k| !k.trim().is_empty()) - .unwrap_or(false) - } -}