mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 21:30:17 +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 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> => {
|
const isPlainObject = (value: unknown): value is Record<string, any> => {
|
||||||
return Object.prototype.toString.call(value) === "[object Object]";
|
return Object.prototype.toString.call(value) === "[object Object]";
|
||||||
@@ -359,76 +360,52 @@ export interface UpdateTomlCommonConfigResult {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存之前的通用配置片段,用于替换操作
|
// Write/remove common config snippet to/from TOML config (structural merge)
|
||||||
let previousCommonSnippet = "";
|
|
||||||
|
|
||||||
// 将通用配置片段写入/移除 TOML 配置
|
|
||||||
export const updateTomlCommonConfigSnippet = (
|
export const updateTomlCommonConfigSnippet = (
|
||||||
tomlString: string,
|
tomlString: string,
|
||||||
snippetString: string,
|
snippetString: string,
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
): UpdateTomlCommonConfigResult => {
|
): UpdateTomlCommonConfigResult => {
|
||||||
if (!snippetString.trim()) {
|
if (!snippetString.trim()) {
|
||||||
// 如果片段为空,直接返回原始配置
|
return { updatedConfig: tomlString };
|
||||||
return {
|
|
||||||
updatedConfig: tomlString,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const config = parseToml(normalizeTomlText(tomlString || ""));
|
||||||
|
const snippet = parseToml(normalizeTomlText(snippetString));
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
// 添加通用配置
|
const merged = deepMerge(
|
||||||
// 先移除旧的通用配置(如果有)
|
deepClone(config) as Record<string, any>,
|
||||||
let updatedConfig = tomlString;
|
deepClone(snippet) as Record<string, any>,
|
||||||
if (previousCommonSnippet && tomlString.includes(previousCommonSnippet)) {
|
);
|
||||||
updatedConfig = tomlString.replace(previousCommonSnippet, "");
|
return { updatedConfig: stringifyToml(merged) };
|
||||||
}
|
|
||||||
|
|
||||||
// 在文件末尾添加新的通用配置
|
|
||||||
// 确保有适当的换行
|
|
||||||
const needsNewline = updatedConfig && !updatedConfig.endsWith("\n");
|
|
||||||
updatedConfig =
|
|
||||||
updatedConfig + (needsNewline ? "\n\n" : "\n") + snippetString;
|
|
||||||
|
|
||||||
// 保存当前通用配置片段
|
|
||||||
previousCommonSnippet = snippetString;
|
|
||||||
|
|
||||||
return {
|
|
||||||
updatedConfig: updatedConfig.trim() + "\n",
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
// 移除通用配置
|
const result = deepClone(config) as Record<string, any>;
|
||||||
if (tomlString.includes(snippetString)) {
|
deepRemove(result, snippet as Record<string, any>);
|
||||||
const updatedConfig = tomlString.replace(snippetString, "");
|
return { updatedConfig: stringifyToml(result) };
|
||||||
// 清理多余的空行
|
|
||||||
const cleaned = updatedConfig.replace(/\n{3,}/g, "\n\n").trim();
|
|
||||||
|
|
||||||
// 清空保存的状态
|
|
||||||
previousCommonSnippet = "";
|
|
||||||
|
|
||||||
return {
|
|
||||||
updatedConfig: cleaned ? cleaned + "\n" : "",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return {
|
} catch (e) {
|
||||||
updatedConfig: tomlString,
|
return { updatedConfig: tomlString, error: String(e) };
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 检查 TOML 配置是否已包含通用配置片段
|
// Check if TOML config already contains the common config snippet (structural subset check)
|
||||||
export const hasTomlCommonConfigSnippet = (
|
export const hasTomlCommonConfigSnippet = (
|
||||||
tomlString: string,
|
tomlString: string,
|
||||||
snippetString: string,
|
snippetString: string,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if (!snippetString.trim()) return false;
|
if (!snippetString.trim()) return false;
|
||||||
|
|
||||||
// 简单检查配置是否包含片段内容
|
try {
|
||||||
// 去除空白字符后比较,避免格式差异影响
|
const config = parseToml(normalizeTomlText(tomlString || ""));
|
||||||
const normalizeWhitespace = (str: string) => str.replace(/\s+/g, " ").trim();
|
const snippet = parseToml(normalizeTomlText(snippetString));
|
||||||
|
return isSubset(config, snippet);
|
||||||
return normalizeWhitespace(tomlString).includes(
|
} catch {
|
||||||
normalizeWhitespace(snippetString),
|
// 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 ==========
|
// ========== Codex base_url utils ==========
|
||||||
|
|||||||
Reference in New Issue
Block a user