From 30fe800ebe9fcdfa969c51c44b0e33c5d5210c20 Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 31 Aug 2025 16:39:38 +0800 Subject: [PATCH] fix(codex): correct config path reporting and folder opening; allow empty config.toml; unify API key field as OPENAI_API_KEY; front-end invoke uses app_type/app fallback for Tauri commands --- src-tauri/src/codex_config.rs | 25 ++++++++++++--------- src-tauri/src/commands.rs | 40 +++++++++++++++++++++++---------- src/components/ProviderForm.tsx | 25 +++++++++++---------- src/lib/tauri-api.ts | 4 ++-- src/types.ts | 2 +- 5 files changed, 58 insertions(+), 38 deletions(-) diff --git a/src-tauri/src/codex_config.rs b/src-tauri/src/codex_config.rs index a3b42b4..7028b64 100644 --- a/src-tauri/src/codex_config.rs +++ b/src-tauri/src/codex_config.rs @@ -104,7 +104,7 @@ pub fn restore_codex_provider_config(provider_id: &str, provider_name: &str) -> fs::create_dir_all(parent).map_err(|e| format!("创建 Codex 目录失败: {}", e))?; } - // 复制 auth.json + // 复制 auth.json(必需) if provider_auth_path.exists() { copy_file(&provider_auth_path, &auth_path)?; log::info!("已恢复 Codex auth.json"); @@ -115,15 +115,14 @@ pub fn restore_codex_provider_config(provider_id: &str, provider_name: &str) -> )); } - // 复制 config.toml + // 复制 config.toml(可选,允许为空;不存在则创建空文件以保持一致性) if provider_config_path.exists() { copy_file(&provider_config_path, &config_path)?; log::info!("已恢复 Codex config.toml"); } else { - return Err(format!( - "供应商 config.toml 不存在: {}", - provider_config_path.display() - )); + // 写入空文件 + fs::write(&config_path, "").map_err(|e| format!("创建空的 config.toml 失败: {}", e))?; + log::info!("供应商 config.toml 缺失,已创建空文件"); } Ok(()) @@ -134,17 +133,21 @@ pub fn import_current_codex_config() -> Result { let auth_path = get_codex_auth_path(); let config_path = get_codex_config_path(); - // 参考 Claude Code 行为:主配置缺失时不导入 - if !auth_path.exists() || !config_path.exists() { + // 行为放宽:仅要求 auth.json 存在;config.toml 可缺失 + if !auth_path.exists() { return Err("Codex 配置文件不存在".to_string()); } // 读取 auth.json let auth = read_json_file::(&auth_path)?; - // 读取 config.toml - let config_str = fs::read_to_string(&config_path) - .map_err(|e| format!("读取 config.toml 失败: {}", e))?; + // 读取 config.toml(允许不存在或读取失败时为空) + let config_str = if config_path.exists() { + fs::read_to_string(&config_path) + .map_err(|e| format!("读取 config.toml 失败: {}", e))? + } else { + String::new() + }; // 组合成完整配置 let settings_config = serde_json::json!({ diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 36c04bd..d2152b5 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -383,21 +383,28 @@ pub async fn get_claude_config_status() -> Result { } /// 获取应用配置状态(通用) +/// 兼容两种参数:`app_type`(推荐)或 `app`(字符串) #[tauri::command] -pub async fn get_config_status(app_type: Option) -> Result { - let app = app_type.unwrap_or(AppType::Claude); - +pub async fn get_config_status( + app_type: Option, + app: Option, + appType: Option, +) -> Result { + let app = app_type + .or_else(|| app.as_deref().map(|s| s.into())) + .or_else(|| appType.as_deref().map(|s| s.into())) + .unwrap_or(AppType::Claude); + match app { AppType::Claude => Ok(crate::config::get_claude_config_status()), AppType::Codex => { - use crate::codex_config::{get_codex_auth_path, get_codex_config_path}; + use crate::codex_config::{get_codex_auth_path, get_codex_config_dir}; let auth_path = get_codex_auth_path(); - let config_path = get_codex_config_path(); - - // Codex 需要两个文件都存在才算配置存在 - let exists = auth_path.exists() && config_path.exists(); - let path = format!("~/.codex/"); - + + // 放宽:只要 auth.json 存在即可认为已配置;config.toml 允许为空 + let exists = auth_path.exists(); + let path = get_codex_config_dir().to_string_lossy().to_string(); + Ok(ConfigStatus { exists, path }) } } @@ -410,9 +417,18 @@ pub async fn get_claude_code_config_path() -> Result { } /// 打开配置文件夹 +/// 兼容两种参数:`app_type`(推荐)或 `app`(字符串) #[tauri::command] -pub async fn open_config_folder(app: tauri::AppHandle, app_type: Option) -> Result { - let app_type = app_type.unwrap_or(AppType::Claude); +pub async fn open_config_folder( + app: tauri::AppHandle, + app_type: Option, + app_str: Option, + appType: Option, +) -> Result { + let app_type = app_type + .or_else(|| app_str.as_deref().map(|s| s.into())) + .or_else(|| appType.as_deref().map(|s| s.into())) + .unwrap_or(AppType::Claude); let config_dir = match app_type { AppType::Claude => crate::config::get_claude_config_dir(), diff --git a/src/components/ProviderForm.tsx b/src/components/ProviderForm.tsx index 9799977..74e2bca 100644 --- a/src/components/ProviderForm.tsx +++ b/src/components/ProviderForm.tsx @@ -60,8 +60,8 @@ const ProviderForm: React.FC = ({ setCodexConfig(config.config || ""); try { const auth = config.auth || {}; - if (auth && typeof auth.api_key === "string") { - setCodexApiKey(auth.api_key); + if (auth && typeof auth.OPENAI_API_KEY === "string") { + setCodexApiKey(auth.OPENAI_API_KEY); } } catch { // ignore @@ -95,9 +95,9 @@ const ProviderForm: React.FC = ({ let settingsConfig: Record; if (isCodex) { - // Codex: 验证两个文件 - if (!codexAuth.trim() || !codexConfig.trim()) { - setError("请填写 auth.json 和 config.toml 配置"); + // Codex: 仅要求 auth.json 必填;config.toml 可为空 + if (!codexAuth.trim()) { + setError("请填写 auth.json 配置"); return; } @@ -105,7 +105,7 @@ const ProviderForm: React.FC = ({ const authJson = JSON.parse(codexAuth); settingsConfig = { auth: authJson, - config: codexConfig, + config: codexConfig ?? "", }; } catch (err) { setError("auth.json 格式错误,请检查JSON语法"); @@ -246,7 +246,7 @@ const ProviderForm: React.FC = ({ setCodexApiKey(key); try { const auth = JSON.parse(codexAuth || "{}"); - auth.api_key = key.trim(); + auth.OPENAI_API_KEY = key.trim(); setCodexAuth(JSON.stringify(auth, null, 2)); } catch { // ignore @@ -266,7 +266,7 @@ const ProviderForm: React.FC = ({ const getCodexAuthApiKey = (authString: string): string => { try { const auth = JSON.parse(authString || "{}"); - return typeof auth.api_key === "string" ? auth.api_key : ""; + return typeof auth.OPENAI_API_KEY === "string" ? auth.OPENAI_API_KEY : ""; } catch { return ""; } @@ -472,7 +472,9 @@ const ProviderForm: React.FC = ({ try { const auth = JSON.parse(value || "{}"); const key = - typeof auth.api_key === "string" ? auth.api_key : ""; + typeof auth.OPENAI_API_KEY === "string" + ? auth.OPENAI_API_KEY + : ""; setCodexApiKey(key); } catch { // ignore @@ -489,7 +491,7 @@ const ProviderForm: React.FC = ({
- +