mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-03-28 14:13:40 +08:00
fix: use structural TOML merge/subset for Codex common config snippet
The text-based approach (string append + substring matching) failed to detect already-merged snippets when config.toml was reformatted by external tools (MCP sync, Codex CLI). Replace with smol-toml parse/ stringify + existing deepMerge/isSubset/deepRemove for correct structural operations. Falls back to text matching on parse failure.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
// 供应商配置处理工具函数
|
||||
|
||||
import type { TemplateValueConfig } from "../config/claudeProviderPresets";
|
||||
import { normalizeQuotes } from "@/utils/textNormalization";
|
||||
import { normalizeQuotes, normalizeTomlText } from "@/utils/textNormalization";
|
||||
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
||||
|
||||
const isPlainObject = (value: unknown): value is Record<string, any> => {
|
||||
return Object.prototype.toString.call(value) === "[object Object]";
|
||||
@@ -359,76 +360,52 @@ export interface UpdateTomlCommonConfigResult {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
// 保存之前的通用配置片段,用于替换操作
|
||||
let previousCommonSnippet = "";
|
||||
|
||||
// 将通用配置片段写入/移除 TOML 配置
|
||||
// Write/remove common config snippet to/from TOML config (structural merge)
|
||||
export const updateTomlCommonConfigSnippet = (
|
||||
tomlString: string,
|
||||
snippetString: string,
|
||||
enabled: boolean,
|
||||
): UpdateTomlCommonConfigResult => {
|
||||
if (!snippetString.trim()) {
|
||||
// 如果片段为空,直接返回原始配置
|
||||
return {
|
||||
updatedConfig: tomlString,
|
||||
};
|
||||
return { updatedConfig: tomlString };
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
// 添加通用配置
|
||||
// 先移除旧的通用配置(如果有)
|
||||
let updatedConfig = tomlString;
|
||||
if (previousCommonSnippet && tomlString.includes(previousCommonSnippet)) {
|
||||
updatedConfig = tomlString.replace(previousCommonSnippet, "");
|
||||
try {
|
||||
const config = parseToml(normalizeTomlText(tomlString || ""));
|
||||
const snippet = parseToml(normalizeTomlText(snippetString));
|
||||
|
||||
if (enabled) {
|
||||
const merged = deepMerge(
|
||||
deepClone(config) as Record<string, any>,
|
||||
deepClone(snippet) as Record<string, any>,
|
||||
);
|
||||
return { updatedConfig: stringifyToml(merged) };
|
||||
} else {
|
||||
const result = deepClone(config) as Record<string, any>;
|
||||
deepRemove(result, snippet as Record<string, any>);
|
||||
return { updatedConfig: stringifyToml(result) };
|
||||
}
|
||||
|
||||
// 在文件末尾添加新的通用配置
|
||||
// 确保有适当的换行
|
||||
const needsNewline = updatedConfig && !updatedConfig.endsWith("\n");
|
||||
updatedConfig =
|
||||
updatedConfig + (needsNewline ? "\n\n" : "\n") + snippetString;
|
||||
|
||||
// 保存当前通用配置片段
|
||||
previousCommonSnippet = snippetString;
|
||||
|
||||
return {
|
||||
updatedConfig: updatedConfig.trim() + "\n",
|
||||
};
|
||||
} else {
|
||||
// 移除通用配置
|
||||
if (tomlString.includes(snippetString)) {
|
||||
const updatedConfig = tomlString.replace(snippetString, "");
|
||||
// 清理多余的空行
|
||||
const cleaned = updatedConfig.replace(/\n{3,}/g, "\n\n").trim();
|
||||
|
||||
// 清空保存的状态
|
||||
previousCommonSnippet = "";
|
||||
|
||||
return {
|
||||
updatedConfig: cleaned ? cleaned + "\n" : "",
|
||||
};
|
||||
}
|
||||
return {
|
||||
updatedConfig: tomlString,
|
||||
};
|
||||
} catch (e) {
|
||||
return { updatedConfig: tomlString, error: String(e) };
|
||||
}
|
||||
};
|
||||
|
||||
// 检查 TOML 配置是否已包含通用配置片段
|
||||
// Check if TOML config already contains the common config snippet (structural subset check)
|
||||
export const hasTomlCommonConfigSnippet = (
|
||||
tomlString: string,
|
||||
snippetString: string,
|
||||
): boolean => {
|
||||
if (!snippetString.trim()) return false;
|
||||
|
||||
// 简单检查配置是否包含片段内容
|
||||
// 去除空白字符后比较,避免格式差异影响
|
||||
const normalizeWhitespace = (str: string) => str.replace(/\s+/g, " ").trim();
|
||||
|
||||
return normalizeWhitespace(tomlString).includes(
|
||||
normalizeWhitespace(snippetString),
|
||||
);
|
||||
try {
|
||||
const config = parseToml(normalizeTomlText(tomlString || ""));
|
||||
const snippet = parseToml(normalizeTomlText(snippetString));
|
||||
return isSubset(config, snippet);
|
||||
} catch {
|
||||
// Fallback to text-based matching if TOML parsing fails
|
||||
const norm = (s: string) => s.replace(/\s+/g, " ").trim();
|
||||
return norm(tomlString).includes(norm(snippetString));
|
||||
}
|
||||
};
|
||||
|
||||
// ========== Codex base_url utils ==========
|
||||
|
||||
Reference in New Issue
Block a user