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
This commit is contained in:
@@ -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))?;
|
fs::create_dir_all(parent).map_err(|e| format!("创建 Codex 目录失败: {}", e))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 复制 auth.json
|
// 复制 auth.json(必需)
|
||||||
if provider_auth_path.exists() {
|
if provider_auth_path.exists() {
|
||||||
copy_file(&provider_auth_path, &auth_path)?;
|
copy_file(&provider_auth_path, &auth_path)?;
|
||||||
log::info!("已恢复 Codex auth.json");
|
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() {
|
if provider_config_path.exists() {
|
||||||
copy_file(&provider_config_path, &config_path)?;
|
copy_file(&provider_config_path, &config_path)?;
|
||||||
log::info!("已恢复 Codex config.toml");
|
log::info!("已恢复 Codex config.toml");
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
// 写入空文件
|
||||||
"供应商 config.toml 不存在: {}",
|
fs::write(&config_path, "").map_err(|e| format!("创建空的 config.toml 失败: {}", e))?;
|
||||||
provider_config_path.display()
|
log::info!("供应商 config.toml 缺失,已创建空文件");
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -134,17 +133,21 @@ pub fn import_current_codex_config() -> Result<Value, String> {
|
|||||||
let auth_path = get_codex_auth_path();
|
let auth_path = get_codex_auth_path();
|
||||||
let config_path = get_codex_config_path();
|
let config_path = get_codex_config_path();
|
||||||
|
|
||||||
// 参考 Claude Code 行为:主配置缺失时不导入
|
// 行为放宽:仅要求 auth.json 存在;config.toml 可缺失
|
||||||
if !auth_path.exists() || !config_path.exists() {
|
if !auth_path.exists() {
|
||||||
return Err("Codex 配置文件不存在".to_string());
|
return Err("Codex 配置文件不存在".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取 auth.json
|
// 读取 auth.json
|
||||||
let auth = read_json_file::<Value>(&auth_path)?;
|
let auth = read_json_file::<Value>(&auth_path)?;
|
||||||
|
|
||||||
// 读取 config.toml
|
// 读取 config.toml(允许不存在或读取失败时为空)
|
||||||
let config_str = fs::read_to_string(&config_path)
|
let config_str = if config_path.exists() {
|
||||||
.map_err(|e| format!("读取 config.toml 失败: {}", e))?;
|
fs::read_to_string(&config_path)
|
||||||
|
.map_err(|e| format!("读取 config.toml 失败: {}", e))?
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
// 组合成完整配置
|
// 组合成完整配置
|
||||||
let settings_config = serde_json::json!({
|
let settings_config = serde_json::json!({
|
||||||
|
|||||||
@@ -383,20 +383,27 @@ pub async fn get_claude_config_status() -> Result<ConfigStatus, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 获取应用配置状态(通用)
|
/// 获取应用配置状态(通用)
|
||||||
|
/// 兼容两种参数:`app_type`(推荐)或 `app`(字符串)
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_config_status(app_type: Option<AppType>) -> Result<ConfigStatus, String> {
|
pub async fn get_config_status(
|
||||||
let app = app_type.unwrap_or(AppType::Claude);
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
|
appType: Option<String>,
|
||||||
|
) -> Result<ConfigStatus, String> {
|
||||||
|
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 {
|
match app {
|
||||||
AppType::Claude => Ok(crate::config::get_claude_config_status()),
|
AppType::Claude => Ok(crate::config::get_claude_config_status()),
|
||||||
AppType::Codex => {
|
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 auth_path = get_codex_auth_path();
|
||||||
let config_path = get_codex_config_path();
|
|
||||||
|
|
||||||
// Codex 需要两个文件都存在才算配置存在
|
// 放宽:只要 auth.json 存在即可认为已配置;config.toml 允许为空
|
||||||
let exists = auth_path.exists() && config_path.exists();
|
let exists = auth_path.exists();
|
||||||
let path = format!("~/.codex/");
|
let path = get_codex_config_dir().to_string_lossy().to_string();
|
||||||
|
|
||||||
Ok(ConfigStatus { exists, path })
|
Ok(ConfigStatus { exists, path })
|
||||||
}
|
}
|
||||||
@@ -410,9 +417,18 @@ pub async fn get_claude_code_config_path() -> Result<String, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 打开配置文件夹
|
/// 打开配置文件夹
|
||||||
|
/// 兼容两种参数:`app_type`(推荐)或 `app`(字符串)
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn open_config_folder(app: tauri::AppHandle, app_type: Option<AppType>) -> Result<bool, String> {
|
pub async fn open_config_folder(
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
app: tauri::AppHandle,
|
||||||
|
app_type: Option<AppType>,
|
||||||
|
app_str: Option<String>,
|
||||||
|
appType: Option<String>,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
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 {
|
let config_dir = match app_type {
|
||||||
AppType::Claude => crate::config::get_claude_config_dir(),
|
AppType::Claude => crate::config::get_claude_config_dir(),
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
setCodexConfig(config.config || "");
|
setCodexConfig(config.config || "");
|
||||||
try {
|
try {
|
||||||
const auth = config.auth || {};
|
const auth = config.auth || {};
|
||||||
if (auth && typeof auth.api_key === "string") {
|
if (auth && typeof auth.OPENAI_API_KEY === "string") {
|
||||||
setCodexApiKey(auth.api_key);
|
setCodexApiKey(auth.OPENAI_API_KEY);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
@@ -95,9 +95,9 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
let settingsConfig: Record<string, any>;
|
let settingsConfig: Record<string, any>;
|
||||||
|
|
||||||
if (isCodex) {
|
if (isCodex) {
|
||||||
// Codex: 验证两个文件
|
// Codex: 仅要求 auth.json 必填;config.toml 可为空
|
||||||
if (!codexAuth.trim() || !codexConfig.trim()) {
|
if (!codexAuth.trim()) {
|
||||||
setError("请填写 auth.json 和 config.toml 配置");
|
setError("请填写 auth.json 配置");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
const authJson = JSON.parse(codexAuth);
|
const authJson = JSON.parse(codexAuth);
|
||||||
settingsConfig = {
|
settingsConfig = {
|
||||||
auth: authJson,
|
auth: authJson,
|
||||||
config: codexConfig,
|
config: codexConfig ?? "",
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError("auth.json 格式错误,请检查JSON语法");
|
setError("auth.json 格式错误,请检查JSON语法");
|
||||||
@@ -246,7 +246,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
setCodexApiKey(key);
|
setCodexApiKey(key);
|
||||||
try {
|
try {
|
||||||
const auth = JSON.parse(codexAuth || "{}");
|
const auth = JSON.parse(codexAuth || "{}");
|
||||||
auth.api_key = key.trim();
|
auth.OPENAI_API_KEY = key.trim();
|
||||||
setCodexAuth(JSON.stringify(auth, null, 2));
|
setCodexAuth(JSON.stringify(auth, null, 2));
|
||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
@@ -266,7 +266,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
const getCodexAuthApiKey = (authString: string): string => {
|
const getCodexAuthApiKey = (authString: string): string => {
|
||||||
try {
|
try {
|
||||||
const auth = JSON.parse(authString || "{}");
|
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 {
|
} catch {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -472,7 +472,9 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
try {
|
try {
|
||||||
const auth = JSON.parse(value || "{}");
|
const auth = JSON.parse(value || "{}");
|
||||||
const key =
|
const key =
|
||||||
typeof auth.api_key === "string" ? auth.api_key : "";
|
typeof auth.OPENAI_API_KEY === "string"
|
||||||
|
? auth.OPENAI_API_KEY
|
||||||
|
: "";
|
||||||
setCodexApiKey(key);
|
setCodexApiKey(key);
|
||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
@@ -489,7 +491,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label htmlFor="codexConfig">config.toml (TOML) *</label>
|
<label htmlFor="codexConfig">config.toml (TOML)</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="codexConfig"
|
id="codexConfig"
|
||||||
value={codexConfig}
|
value={codexConfig}
|
||||||
@@ -497,10 +499,9 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
|
|||||||
placeholder={``}
|
placeholder={``}
|
||||||
rows={8}
|
rows={8}
|
||||||
style={{ fontFamily: "monospace", fontSize: "14px" }}
|
style={{ fontFamily: "monospace", fontSize: "14px" }}
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<small className="field-hint">
|
<small className="field-hint">
|
||||||
Codex config.toml 配置内容
|
Codex config.toml 配置内容(可留空)
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export const tauriAPI = {
|
|||||||
// 获取应用配置状态(通用)
|
// 获取应用配置状态(通用)
|
||||||
getConfigStatus: async (app?: AppType): Promise<ConfigStatus> => {
|
getConfigStatus: async (app?: AppType): Promise<ConfigStatus> => {
|
||||||
try {
|
try {
|
||||||
return await invoke("get_config_status", { appType: app });
|
return await invoke("get_config_status", { app_type: app, app });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取配置状态失败:", error);
|
console.error("获取配置状态失败:", error);
|
||||||
return {
|
return {
|
||||||
@@ -145,7 +145,7 @@ export const tauriAPI = {
|
|||||||
// 打开配置文件夹
|
// 打开配置文件夹
|
||||||
openConfigFolder: async (app?: AppType): Promise<void> => {
|
openConfigFolder: async (app?: AppType): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await invoke("open_config_folder", { appType: app });
|
await invoke("open_config_folder", { app_type: app, app });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("打开配置文件夹失败:", error);
|
console.error("打开配置文件夹失败:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export interface Provider {
|
export interface Provider {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
settingsConfig: Record<string, any>; // 完整的 Claude Code settings.json 配置
|
settingsConfig: Record<string, any>; // 应用配置对象:Claude 为 settings.json;Codex 为 { auth, config }
|
||||||
websiteUrl?: string;
|
websiteUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user