fix(config): defer common config save until form submission

- Change handleExtract and handleCommonConfigSnippetChange to use delayed save pattern
- Add hasUnsavedCommonConfig state to track pending changes
- Add getPendingCommonConfigSnippet and markCommonConfigSaved helper functions
- Remove immediate backend API calls, save only on form submit
- Add extractSuccessNeedSave and saveCommonConfigFailed i18n keys
This commit is contained in:
YoVinchen
2026-01-29 00:54:48 +08:00
parent 40373538d3
commit 3f68f78331
7 changed files with 174 additions and 149 deletions
@@ -8,6 +8,7 @@ import { Form, FormField, FormItem, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { providerSchema, type ProviderFormData } from "@/lib/schemas/provider";
import type { AppId } from "@/lib/api";
import { configApi } from "@/lib/api";
import type {
ProviderCategory,
ProviderMeta,
@@ -478,6 +479,8 @@ export function ProviderForm({
handleExtract: handleClaudeExtract,
isLoading: isClaudeCommonConfigLoading,
finalConfig: claudeFinalConfig,
getPendingCommonConfigSnippet: getPendingClaudeCommonConfigSnippet,
markCommonConfigSaved: markClaudeCommonConfigSaved,
} = useCommonConfigSnippet({
settingsConfig: form.getValues("settingsConfig"),
onConfigChange: (config) => form.setValue("settingsConfig", config),
@@ -498,6 +501,8 @@ export function ProviderForm({
handleExtract: handleCodexExtract,
isLoading: isCodexCommonConfigLoading,
finalConfig: codexFinalConfig,
getPendingCommonConfigSnippet: getPendingCodexCommonConfigSnippet,
markCommonConfigSaved: markCodexCommonConfigSaved,
} = useCodexCommonConfig({
codexConfig,
onConfigChange: handleCodexConfigChange,
@@ -587,6 +592,8 @@ export function ProviderForm({
handleExtract: handleGeminiExtract,
isLoading: isGeminiCommonConfigLoading,
finalEnv: geminiFinalEnv,
getPendingCommonConfigSnippet: getPendingGeminiCommonConfigSnippet,
markCommonConfigSaved: markGeminiCommonConfigSaved,
} = useGeminiCommonConfig({
envValue: geminiEnv,
onEnvChange: handleGeminiEnvChange,
@@ -817,7 +824,7 @@ export function ProviderForm({
const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false);
const handleSubmit = (values: ProviderFormData) => {
const handleSubmit = async (values: ProviderFormData) => {
// 检查通用配置是否仍在加载中
const isCommonConfigLoading =
(appId === "claude" && isClaudeCommonConfigLoading) ||
@@ -970,6 +977,37 @@ export function ProviderForm({
}
}
// 保存待保存的通用配置(延迟保存模式:在表单提交时统一保存)
try {
if (appId === "claude") {
const pendingSnippet = getPendingClaudeCommonConfigSnippet();
if (pendingSnippet !== null) {
await configApi.setCommonConfigSnippet("claude", pendingSnippet);
markClaudeCommonConfigSaved();
}
} else if (appId === "codex") {
const pendingSnippet = getPendingCodexCommonConfigSnippet();
if (pendingSnippet !== null) {
await configApi.setCommonConfigSnippet("codex", pendingSnippet);
markCodexCommonConfigSaved();
}
} else if (appId === "gemini") {
const pendingSnippet = getPendingGeminiCommonConfigSnippet();
if (pendingSnippet !== null) {
await configApi.setCommonConfigSnippet("gemini", pendingSnippet);
markGeminiCommonConfigSaved();
}
}
} catch (error) {
console.error("保存通用配置失败:", error);
toast.error(
t("providerForm.saveCommonConfigFailed", {
defaultValue: "保存通用配置失败",
}),
);
return;
}
let settingsConfig: string;
// Codex: 组合 auth 和 config
@@ -88,6 +88,12 @@ export interface UseCodexCommonConfigReturn {
handleExtract: () => Promise<void>;
/** 最终配置(运行时合并结果,只读) */
finalConfig: string;
/** 是否有待保存的通用配置变更 */
hasUnsavedCommonConfig: boolean;
/** 获取待保存的通用配置片段(用于 handleSubmit */
getPendingCommonConfigSnippet: () => string | null;
/** 标记通用配置已保存 */
markCommonConfigSaved: () => void;
}
/**
@@ -118,18 +124,8 @@ export function useCodexCommonConfig({
const [commonConfigError, setCommonConfigError] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [isExtracting, setIsExtracting] = useState(false);
// 用于避免异步保存乱序
const saveSequenceRef = useRef(0);
const saveQueueRef = useRef<Promise<void>>(Promise.resolve());
const enqueueSave = useCallback((saveFn: () => Promise<void>) => {
const next = saveQueueRef.current.then(saveFn);
// Log errors to help with debugging, but don't block subsequent saves
saveQueueRef.current = next.catch((e) => {
console.error("[SaveQueue] Codex common config save failed:", e);
});
return next;
}, []);
// 是否有待保存的通用配置变更
const [hasUnsavedCommonConfig, setHasUnsavedCommonConfig] = useState(false);
// 用于跟踪编辑模式是否已初始化
const hasInitializedEditMode = useRef(false);
@@ -340,24 +336,15 @@ export function useCodexCommonConfig({
);
// ============================================================================
// 处理通用配置片段变化
// 处理通用配置片段变化(延迟保存模式:只更新本地状态,实际保存在表单提交时)
// ============================================================================
const handleCommonConfigSnippetChange = useCallback(
(value: string) => {
setCommonConfigSnippetState(value);
if (!value.trim()) {
const saveId = ++saveSequenceRef.current;
setCommonConfigError("");
enqueueSave(() => configApi.setCommonConfigSnippet("codex", "")).catch(
(error) => {
if (saveSequenceRef.current !== saveId) return;
console.error("保存 Codex 通用配置失败:", error);
setCommonConfigError(
t("codexConfig.saveFailed", { error: String(error) }),
);
},
);
setHasUnsavedCommonConfig(true);
return;
}
@@ -372,26 +359,17 @@ export function useCodexCommonConfig({
return;
}
const saveId = ++saveSequenceRef.current;
setCommonConfigError("");
enqueueSave(() => configApi.setCommonConfigSnippet("codex", value)).catch(
(error) => {
if (saveSequenceRef.current !== saveId) return;
console.error("保存 Codex 通用配置失败:", error);
setCommonConfigError(
t("codexConfig.saveFailed", { error: String(error) }),
);
},
);
setHasUnsavedCommonConfig(true);
// 注意:新架构下不再需要同步到其他供应商的 settingsConfig
// 因为 finalConfig 是运行时计算的
},
[enqueueSave, t],
[t],
);
// ============================================================================
// 从当前最终配置提取通用配置片段
// 从当前最终配置提取通用配置片段(延迟保存模式:只更新本地状态,实际保存在表单提交时)
// ============================================================================
const handleExtract = useCallback(async () => {
setIsExtracting(true);
@@ -420,11 +398,9 @@ export function useCodexCommonConfig({
return;
}
// 更新片段状态
// 更新片段状态(延迟保存:不立即调用后端 API
setCommonConfigSnippetState(extracted);
// 保存到后端
await configApi.setCommonConfigSnippet("codex", extracted);
setHasUnsavedCommonConfig(true);
// 提取成功后,从 config 中移除与 extracted 相同的部分
const customToml = extractConfigToml(codexConfig);
@@ -435,10 +411,10 @@ export function useCodexCommonConfig({
const parsed = JSON.parse(codexConfig);
parsed.config = diffResult.customToml;
onConfigChange(JSON.stringify(parsed, null, 2));
// Notify user that config was modified
// Notify user that config was modified (提示用户需要保存)
toast.success(
t("codexConfig.extractSuccess", {
defaultValue: "已提取通用配置,自定义配置已自动更新",
t("codexConfig.extractSuccessNeedSave", {
defaultValue: "已提取通用配置,点击保存按钮完成保存",
}),
);
} catch (parseError) {
@@ -458,6 +434,20 @@ export function useCodexCommonConfig({
}
}, [finalConfig, codexConfig, onConfigChange, extractConfigToml, t]);
// ============================================================================
// 获取待保存的通用配置片段(用于 handleSubmit 中统一保存)
// ============================================================================
const getPendingCommonConfigSnippet = useCallback(() => {
return hasUnsavedCommonConfig ? commonConfigSnippet : null;
}, [hasUnsavedCommonConfig, commonConfigSnippet]);
// ============================================================================
// 标记通用配置已保存
// ============================================================================
const markCommonConfigSaved = useCallback(() => {
setHasUnsavedCommonConfig(false);
}, []);
return {
useCommonConfig,
commonConfigSnippet,
@@ -468,5 +458,8 @@ export function useCodexCommonConfig({
handleCommonConfigSnippetChange,
handleExtract,
finalConfig,
hasUnsavedCommonConfig,
getPendingCommonConfigSnippet,
markCommonConfigSaved,
};
}
@@ -65,6 +65,12 @@ export interface UseCommonConfigSnippetReturn {
handleExtract: () => Promise<void>;
/** 最终配置(运行时合并结果,只读) */
finalConfig: string;
/** 是否有待保存的通用配置变更 */
hasUnsavedCommonConfig: boolean;
/** 获取待保存的通用配置片段(用于 handleSubmit */
getPendingCommonConfigSnippet: () => string | null;
/** 标记通用配置已保存 */
markCommonConfigSaved: () => void;
}
/**
@@ -96,18 +102,8 @@ export function useCommonConfigSnippet({
const [commonConfigError, setCommonConfigError] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [isExtracting, setIsExtracting] = useState(false);
// 用于避免异步保存乱序
const saveSequenceRef = useRef(0);
const saveQueueRef = useRef<Promise<void>>(Promise.resolve());
const enqueueSave = useCallback((saveFn: () => Promise<void>) => {
const next = saveQueueRef.current.then(saveFn);
// Log errors to help with debugging, but don't block subsequent saves
saveQueueRef.current = next.catch((e) => {
console.error("[SaveQueue] Claude common config save failed:", e);
});
return next;
}, []);
// 是否有待保存的通用配置变更
const [hasUnsavedCommonConfig, setHasUnsavedCommonConfig] = useState(false);
// 用于跟踪编辑模式是否已初始化
const hasInitializedEditMode = useRef(false);
@@ -344,54 +340,33 @@ export function useCommonConfigSnippet({
);
// ============================================================================
// 处理通用配置片段变化
// 处理通用配置片段变化(延迟保存模式:只更新本地状态,实际保存在表单提交时)
// ============================================================================
const handleCommonConfigSnippetChange = useCallback(
(value: string) => {
setCommonConfigSnippetState(value);
const handleCommonConfigSnippetChange = useCallback((value: string) => {
setCommonConfigSnippetState(value);
if (!value.trim()) {
const saveId = ++saveSequenceRef.current;
setCommonConfigError("");
enqueueSave(() => configApi.setCommonConfigSnippet("claude", "")).catch(
(error) => {
if (saveSequenceRef.current !== saveId) return;
console.error("保存通用配置失败:", error);
setCommonConfigError(
t("claudeConfig.saveFailed", { error: String(error) }),
);
},
);
return;
}
// JSON 格式校验
const validationError = validateJsonConfig(value, "通用配置片段");
if (validationError) {
setCommonConfigError(validationError);
return;
}
const saveId = ++saveSequenceRef.current;
if (!value.trim()) {
setCommonConfigError("");
enqueueSave(() =>
configApi.setCommonConfigSnippet("claude", value),
).catch((error) => {
if (saveSequenceRef.current !== saveId) return;
console.error("保存通用配置失败:", error);
setCommonConfigError(
t("claudeConfig.saveFailed", { error: String(error) }),
);
});
setHasUnsavedCommonConfig(true);
return;
}
// 注意:新架构下不再需要同步到其他供应商的 settingsConfig
// 因为 finalConfig 是运行时计算的
},
[enqueueSave, t],
);
// JSON 格式校验
const validationError = validateJsonConfig(value, "通用配置片段");
if (validationError) {
setCommonConfigError(validationError);
return;
}
setCommonConfigError("");
setHasUnsavedCommonConfig(true);
// 注意:新架构下不再需要同步到其他供应商的 settingsConfig
// 因为 finalConfig 是运行时计算的
}, []);
// ============================================================================
// 从当前最终配置提取通用配置片段
// 从当前最终配置提取通用配置片段(延迟保存模式:只更新本地状态,实际保存在表单提交时)
// ============================================================================
const handleExtract = useCallback(async () => {
setIsExtracting(true);
@@ -414,11 +389,9 @@ export function useCommonConfigSnippet({
return;
}
// 更新片段状态
// 更新片段状态(延迟保存:不立即调用后端 API
setCommonConfigSnippetState(extracted);
// 保存到后端
await configApi.setCommonConfigSnippet("claude", extracted);
setHasUnsavedCommonConfig(true);
// 提取成功后,从 settingsConfig 中移除与 extracted 相同的部分
try {
@@ -428,10 +401,10 @@ export function useCommonConfigSnippet({
if (isPlainObject(customParsed) && isPlainObject(extractedParsed)) {
const diffResult = extractDifference(customParsed, extractedParsed);
onConfigChange(JSON.stringify(diffResult.customConfig, null, 2));
// Notify user that config was modified
// Notify user that config was modified (提示用户需要保存)
toast.success(
t("claudeConfig.extractSuccess", {
defaultValue: "已提取通用配置,自定义配置已自动更新",
t("claudeConfig.extractSuccessNeedSave", {
defaultValue: "已提取通用配置,点击保存按钮完成保存",
}),
);
}
@@ -451,6 +424,20 @@ export function useCommonConfigSnippet({
}
}, [finalConfig, settingsConfig, onConfigChange, t]);
// ============================================================================
// 获取待保存的通用配置片段(用于 handleSubmit 中统一保存)
// ============================================================================
const getPendingCommonConfigSnippet = useCallback(() => {
return hasUnsavedCommonConfig ? commonConfigSnippet : null;
}, [hasUnsavedCommonConfig, commonConfigSnippet]);
// ============================================================================
// 标记通用配置已保存
// ============================================================================
const markCommonConfigSaved = useCallback(() => {
setHasUnsavedCommonConfig(false);
}, []);
return {
useCommonConfig,
commonConfigSnippet,
@@ -461,5 +448,8 @@ export function useCommonConfigSnippet({
handleCommonConfigSnippetChange,
handleExtract,
finalConfig,
hasUnsavedCommonConfig,
getPendingCommonConfigSnippet,
markCommonConfigSaved,
};
}
@@ -65,6 +65,12 @@ export interface UseGeminiCommonConfigReturn {
handleExtract: () => Promise<void>;
/** 最终 env 对象(运行时合并结果,只读) */
finalEnv: Record<string, string>;
/** 是否有待保存的通用配置变更 */
hasUnsavedCommonConfig: boolean;
/** 获取待保存的通用配置片段(用于 handleSubmit */
getPendingCommonConfigSnippet: () => string | null;
/** 标记通用配置已保存 */
markCommonConfigSaved: () => void;
}
/**
@@ -97,18 +103,8 @@ export function useGeminiCommonConfig({
const [commonConfigError, setCommonConfigError] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [isExtracting, setIsExtracting] = useState(false);
// 用于避免异步保存乱序
const saveSequenceRef = useRef(0);
const saveQueueRef = useRef<Promise<void>>(Promise.resolve());
const enqueueSave = useCallback((saveFn: () => Promise<void>) => {
const next = saveQueueRef.current.then(saveFn);
// Log errors to help with debugging, but don't block subsequent saves
saveQueueRef.current = next.catch((e) => {
console.error("[SaveQueue] Gemini common config save failed:", e);
});
return next;
}, []);
// 是否有待保存的通用配置变更
const [hasUnsavedCommonConfig, setHasUnsavedCommonConfig] = useState(false);
// 用于跟踪编辑模式是否已初始化
const hasInitializedEditMode = useRef(false);
@@ -330,24 +326,15 @@ export function useGeminiCommonConfig({
);
// ============================================================================
// 处理通用配置片段变化
// 处理通用配置片段变化(延迟保存模式:只更新本地状态,实际保存在表单提交时)
// ============================================================================
const handleCommonConfigSnippetChange = useCallback(
(value: string) => {
setCommonConfigSnippetState(value);
if (!value.trim()) {
const saveId = ++saveSequenceRef.current;
setCommonConfigError("");
enqueueSave(() => configApi.setCommonConfigSnippet("gemini", "")).catch(
(error) => {
if (saveSequenceRef.current !== saveId) return;
console.error("保存 Gemini 通用配置失败:", error);
setCommonConfigError(
t("geminiConfig.saveFailed", { error: String(error) }),
);
},
);
setHasUnsavedCommonConfig(true);
return;
}
@@ -358,26 +345,17 @@ export function useGeminiCommonConfig({
return;
}
const saveId = ++saveSequenceRef.current;
setCommonConfigError("");
enqueueSave(() =>
configApi.setCommonConfigSnippet("gemini", value),
).catch((error) => {
if (saveSequenceRef.current !== saveId) return;
console.error("保存 Gemini 通用配置失败:", error);
setCommonConfigError(
t("geminiConfig.saveFailed", { error: String(error) }),
);
});
setHasUnsavedCommonConfig(true);
// 注意:新架构下不再需要同步到其他供应商的 settingsConfig
// 因为 finalEnv 是运行时计算的
},
[enqueueSave, parseSnippetEnv, t],
[parseSnippetEnv],
);
// ============================================================================
// 从当前最终 env 提取通用配置片段
// 从当前最终 env 提取通用配置片段(延迟保存模式:只更新本地状态,实际保存在表单提交时)
// ============================================================================
const handleExtract = useCallback(async () => {
setIsExtracting(true);
@@ -402,11 +380,9 @@ export function useGeminiCommonConfig({
return;
}
// 更新片段状态
// 更新片段状态(延迟保存:不立即调用后端 API
setCommonConfigSnippetState(extracted);
// 保存到后端
await configApi.setCommonConfigSnippet("gemini", extracted);
setHasUnsavedCommonConfig(true);
// 提取成功后,从 customEnv 中移除与 extracted 相同的部分
const diffResult = extractDifference(
@@ -420,10 +396,10 @@ export function useGeminiCommonConfig({
}
}
onEnvChange(envObjToString(newCustomEnv));
// Notify user that config was modified
// Notify user that config was modified (提示用户需要保存)
toast.success(
t("geminiConfig.extractSuccess", {
defaultValue: "已提取通用配置,自定义配置已自动更新",
t("geminiConfig.extractSuccessNeedSave", {
defaultValue: "已提取通用配置,点击保存按钮完成保存",
}),
);
} catch (error) {
@@ -436,6 +412,20 @@ export function useGeminiCommonConfig({
}
}, [finalEnv, customEnv, onEnvChange, envObjToString, parseSnippetEnv, t]);
// ============================================================================
// 获取待保存的通用配置片段(用于 handleSubmit 中统一保存)
// ============================================================================
const getPendingCommonConfigSnippet = useCallback(() => {
return hasUnsavedCommonConfig ? commonConfigSnippet : null;
}, [hasUnsavedCommonConfig, commonConfigSnippet]);
// ============================================================================
// 标记通用配置已保存
// ============================================================================
const markCommonConfigSaved = useCallback(() => {
setHasUnsavedCommonConfig(false);
}, []);
return {
useCommonConfig,
commonConfigSnippet,
@@ -446,5 +436,8 @@ export function useGeminiCommonConfig({
handleCommonConfigSnippetChange,
handleExtract,
finalEnv,
hasUnsavedCommonConfig,
getPendingCommonConfigSnippet,
markCommonConfigSaved,
};
}
+5 -1
View File
@@ -59,6 +59,7 @@
"extractFromCurrent": "Extract from Editor",
"extractNoCommonConfig": "No common config available to extract from editor",
"extractFailed": "Extract failed: {{error}}",
"extractSuccessNeedSave": "Common config extracted, click Save to complete",
"saveFailed": "Save failed: {{error}}",
"hidePreview": "Hide merged preview",
"showPreview": "Show merged preview",
@@ -493,7 +494,8 @@
"categoryAggregation": "Aggregation",
"categoryThirdParty": "Third Party",
"commonConfigLoading": "Common config is still loading, please wait before saving",
"commonConfigSyncFailed": "Common config sync failed for some providers. Please retry."
"commonConfigSyncFailed": "Common config sync failed for some providers. Please retry.",
"saveCommonConfigFailed": "Failed to save common config"
},
"endpointTest": {
"title": "API Endpoint Management",
@@ -563,6 +565,7 @@
"extractFromCurrent": "Extract from Editor",
"extractNoCommonConfig": "No common config available to extract from editor",
"extractFailed": "Extract failed: {{error}}",
"extractSuccessNeedSave": "Common config extracted, click Save to complete",
"saveFailed": "Save failed: {{error}}"
},
"geminiConfig": {
@@ -577,6 +580,7 @@
"extractFromCurrent": "Extract from Editor",
"extractNoCommonConfig": "No common config available to extract from editor",
"extractFailed": "Extract failed: {{error}}",
"extractSuccessNeedSave": "Common config extracted, click Save to complete",
"saveFailed": "Save failed: {{error}}",
"extractedConfigInvalid": "Extracted config format is invalid",
"invalidJsonFormat": "Common config snippet format error (must be valid JSON)",
+5 -1
View File
@@ -59,6 +59,7 @@
"extractFromCurrent": "編集内容から抽出",
"extractNoCommonConfig": "編集内容から抽出できる共通設定がありません",
"extractFailed": "抽出に失敗しました: {{error}}",
"extractSuccessNeedSave": "共通設定を抽出しました。保存ボタンをクリックして完了してください",
"saveFailed": "保存に失敗しました: {{error}}",
"hidePreview": "マージプレビューを非表示",
"showPreview": "マージプレビューを表示",
@@ -493,7 +494,8 @@
"categoryAggregation": "アグリゲーター",
"categoryThirdParty": "サードパーティ",
"commonConfigLoading": "共通設定を読み込み中です。保存する前にお待ちください",
"commonConfigSyncFailed": "共通設定の同期に失敗しました。再試行してください"
"commonConfigSyncFailed": "共通設定の同期に失敗しました。再試行してください",
"saveCommonConfigFailed": "共通設定の保存に失敗しました"
},
"endpointTest": {
"title": "API エンドポイント管理",
@@ -563,6 +565,7 @@
"extractFromCurrent": "編集内容から抽出",
"extractNoCommonConfig": "編集内容から抽出できる共通設定がありません",
"extractFailed": "抽出に失敗しました: {{error}}",
"extractSuccessNeedSave": "共通設定を抽出しました。保存ボタンをクリックして完了してください",
"saveFailed": "保存に失敗しました: {{error}}"
},
"geminiConfig": {
@@ -577,6 +580,7 @@
"extractFromCurrent": "編集内容から抽出",
"extractNoCommonConfig": "編集内容から抽出できる共通設定がありません",
"extractFailed": "抽出に失敗しました: {{error}}",
"extractSuccessNeedSave": "共通設定を抽出しました。保存ボタンをクリックして完了してください",
"saveFailed": "保存に失敗しました: {{error}}",
"extractedConfigInvalid": "抽出した設定のフォーマットが不正です",
"invalidJsonFormat": "共通設定スニペットの形式が不正です(有効な JSON でなければなりません)",
+4 -1
View File
@@ -59,6 +59,7 @@
"extractFromCurrent": "从编辑内容提取",
"extractNoCommonConfig": "当前编辑内容没有可提取的通用配置",
"extractFailed": "提取失败: {{error}}",
"extractSuccessNeedSave": "已提取通用配置,点击保存按钮完成保存",
"saveFailed": "保存失败: {{error}}",
"hidePreview": "隐藏合并预览",
"showPreview": "显示合并预览",
@@ -493,7 +494,8 @@
"categoryAggregation": "聚合服务",
"categoryThirdParty": "第三方",
"commonConfigLoading": "通用配置正在加载中,请稍候再保存",
"commonConfigSyncFailed": "通用配置同步部分失败,请重试"
"commonConfigSyncFailed": "通用配置同步部分失败,请重试",
"saveCommonConfigFailed": "保存通用配置失败"
},
"endpointTest": {
"title": "请求地址管理",
@@ -577,6 +579,7 @@
"extractFromCurrent": "从编辑内容提取",
"extractNoCommonConfig": "当前编辑内容没有可提取的通用配置",
"extractFailed": "提取失败: {{error}}",
"extractSuccessNeedSave": "已提取通用配置,点击保存按钮完成保存",
"saveFailed": "保存失败: {{error}}",
"extractedConfigInvalid": "提取的配置格式错误",
"invalidJsonFormat": "通用配置片段格式错误(必须是有效的 JSON)",