@@ -2561,6 +2561,28 @@ function buildClaudeSettingsDiff(params = {}) {
25612561 };
25622562}
25632563
2564+
2565+ function normalizeOpenaiBridgeMaxRetries(value, fallback = 2) {
2566+ const raw = Number(value);
2567+ const fallbackRaw = Number(fallback);
2568+ const base = Number.isFinite(raw) ? raw : (Number.isFinite(fallbackRaw) ? fallbackRaw : 2);
2569+ return Math.min(10, Math.max(2, Math.floor(base)));
2570+ }
2571+
2572+ function resolveProviderOpenaiBridgeMaxRetries(provider) {
2573+ if (!provider || typeof provider !== 'object') return 2;
2574+ if (provider.codexmate_bridge_max_retries !== undefined) {
2575+ return normalizeOpenaiBridgeMaxRetries(provider.codexmate_bridge_max_retries);
2576+ }
2577+ if (provider.openai_bridge_max_retries !== undefined) {
2578+ return normalizeOpenaiBridgeMaxRetries(provider.openai_bridge_max_retries);
2579+ }
2580+ if (provider.max_retries !== undefined) {
2581+ return normalizeOpenaiBridgeMaxRetries(provider.max_retries);
2582+ }
2583+ return 2;
2584+ }
2585+
25642586function addProviderToConfig(params = {}) {
25652587 const name = typeof params.name === 'string' ? params.name.trim() : '';
25662588 const url = typeof params.url === 'string' ? params.url.trim() : '';
@@ -2575,6 +2597,8 @@ function addProviderToConfig(params = {}) {
25752597 ? params.model.trim()
25762598 : fallbackModel;
25772599 const useTransform = !!params.useTransform;
2600+ const hasOpenaiBridgeMaxRetries = params.openaiBridgeMaxRetries !== undefined || params.maxRetries !== undefined;
2601+ const openaiBridgeMaxRetries = normalizeOpenaiBridgeMaxRetries(params.openaiBridgeMaxRetries ?? params.maxRetries);
25782602 const allowManaged = !!params.allowManaged;
25792603 const normalizedUrl = normalizeBaseUrl(url);
25802604
@@ -2634,7 +2658,7 @@ function addProviderToConfig(params = {}) {
26342658 const requiresOpenaiAuth = useTransform;
26352659
26362660 if (useTransform) {
2637- const saveRes = upsertOpenaiBridgeProvider(OPENAI_BRIDGE_SETTINGS_FILE, name, normalizedUrl, key);
2661+ const saveRes = upsertOpenaiBridgeProvider(OPENAI_BRIDGE_SETTINGS_FILE, name, normalizedUrl, key, undefined, { maxRetries: openaiBridgeMaxRetries } );
26382662 if (saveRes && saveRes.error) {
26392663 return { error: String(saveRes.error) };
26402664 }
@@ -2646,6 +2670,7 @@ function addProviderToConfig(params = {}) {
26462670 ).toString().replace(/\/+$/g, '');
26472671 authKeyForConfig = 'codexmate';
26482672 extraLines.push(`codexmate_bridge = "openai"`);
2673+ extraLines.push(`codexmate_bridge_max_retries = ${openaiBridgeMaxRetries}`);
26492674 }
26502675
26512676 const safeUrl = escapeTomlBasicString(baseUrlForConfig);
@@ -2689,6 +2714,8 @@ function updateProviderInConfig(params = {}) {
26892714 ? String(params.key).trim()
26902715 : undefined;
26912716 const useTransform = !!params.useTransform;
2717+ const hasOpenaiBridgeMaxRetries = params.openaiBridgeMaxRetries !== undefined || params.maxRetries !== undefined;
2718+ const openaiBridgeMaxRetries = normalizeOpenaiBridgeMaxRetries(params.openaiBridgeMaxRetries ?? params.maxRetries);
26922719 const allowManaged = !!params.allowManaged;
26932720
26942721 if (!name) return { error: '名称不能为空' };
@@ -2703,7 +2730,7 @@ function updateProviderInConfig(params = {}) {
27032730 }
27042731
27052732 try {
2706- cmdUpdate(name, url || undefined, key, true, { allowManaged, useTransform });
2733+ cmdUpdate(name, url || undefined, key, true, { allowManaged, useTransform, ...(hasOpenaiBridgeMaxRetries ? { openaiBridgeMaxRetries } : {}) });
27072734 return { success: true };
27082735 } catch (e) {
27092736 return { error: e.message || '更新失败' };
@@ -9846,6 +9873,8 @@ function cmdDelete(name, silent = false) {
98469873function cmdUpdate(name, baseUrl, apiKey, silent = false, options = {}) {
98479874 const allowManaged = !!(options && options.allowManaged);
98489875 const forceUseTransform = !!(options && options.useTransform);
9876+ const hasOpenaiBridgeMaxRetries = options && Object.prototype.hasOwnProperty.call(options, 'openaiBridgeMaxRetries');
9877+ const openaiBridgeMaxRetries = normalizeOpenaiBridgeMaxRetries(options && options.openaiBridgeMaxRetries);
98499878 const normalizedBaseUrl = baseUrl === undefined ? undefined : normalizeBaseUrl(baseUrl);
98509879 if (!name) {
98519880 if (!silent) console.error('错误: 提供商名称必填');
@@ -10005,6 +10034,32 @@ function cmdUpdate(name, baseUrl, apiKey, silent = false, options = {}) {
1000510034 return next;
1000610035 };
1000710036
10037+ const replaceTomlNumberField = (block, fieldName, rawValue) => {
10038+ const numberValue = String(Math.floor(Number(rawValue)));
10039+ const escapedFieldName = escapeRegex(fieldName);
10040+ const multilineRanges = collectTomlMultilineStringRanges(block);
10041+ const withCommentRegex = new RegExp(`^(\\s*${escapedFieldName}\\s*=\\s*)([-+]?\\d+(?:\\.\\d+)?)(\\s+#.*)?$`, 'mg');
10042+ let replaced = false;
10043+ let next = block.replace(withCommentRegex, (full, prefix, _value, suffix = '', offset) => {
10044+ if (replaced || isIndexInRanges(offset, multilineRanges)) {
10045+ return full;
10046+ }
10047+ replaced = true;
10048+ return `${prefix}${numberValue}${suffix}`;
10049+ });
10050+ if (!replaced) {
10051+ const keyIndentMatch = block.match(/^(\s*)[A-Za-z0-9_.-]+\s*=/m);
10052+ const indent = keyIndentMatch ? keyIndentMatch[1] : '';
10053+ const lineEnding = block.includes('\r\n') ? '\r\n' : '\n';
10054+ const tailMatch = block.match(/(\s*)$/);
10055+ const tail = tailMatch ? tailMatch[1] : '';
10056+ const body = block.slice(0, block.length - tail.length);
10057+ const separator = body.endsWith('\n') || body.endsWith('\r') ? '' : lineEnding;
10058+ next = `${body}${separator}${indent}${fieldName} = ${numberValue}${tail}`;
10059+ }
10060+ return next;
10061+ };
10062+
1000810063 const replaceTomlBooleanField = (block, fieldName, rawValue) => {
1000910064 const boolValue = rawValue ? 'true' : 'false';
1001010065 const escapedFieldName = escapeRegex(fieldName);
@@ -10061,7 +10116,9 @@ function cmdUpdate(name, baseUrl, apiKey, silent = false, options = {}) {
1006110116 : existingApiKey;
1006210117
1006310118 if (upstreamBaseUrl) {
10064- const saveRes = upsertOpenaiBridgeProvider(OPENAI_BRIDGE_SETTINGS_FILE, name, upstreamBaseUrl, upstreamApiKey);
10119+ const saveRes = upsertOpenaiBridgeProvider(OPENAI_BRIDGE_SETTINGS_FILE, name, upstreamBaseUrl, upstreamApiKey, undefined, {
10120+ maxRetries: hasOpenaiBridgeMaxRetries ? openaiBridgeMaxRetries : resolveProviderOpenaiBridgeMaxRetries(providerConfig)
10121+ });
1006510122 if (saveRes && saveRes.error) {
1006610123 throw new Error(String(saveRes.error));
1006710124 }
@@ -10077,6 +10134,9 @@ function cmdUpdate(name, baseUrl, apiKey, silent = false, options = {}) {
1007710134 updatedBlock = replaceTomlBooleanField(updatedBlock, 'requires_openai_auth', true);
1007810135 updatedBlock = replaceTomlStringField(updatedBlock, 'preferred_auth_method', 'codexmate');
1007910136 updatedBlock = replaceTomlStringField(updatedBlock, 'codexmate_bridge', 'openai');
10137+ if (hasOpenaiBridgeMaxRetries) {
10138+ updatedBlock = replaceTomlNumberField(updatedBlock, 'codexmate_bridge_max_retries', openaiBridgeMaxRetries);
10139+ }
1008010140 } else {
1008110141 if (normalizedBaseUrl) {
1008210142 updatedBlock = replaceTomlStringField(updatedBlock, 'base_url', normalizedBaseUrl);
@@ -12819,7 +12879,10 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
1281912879 break;
1282012880 }
1282112881 // 不返回 apiKey(敏感信息),仅返回用户填过的上游 URL
12822- result = { baseUrl: upstream.baseUrl, hasApiKey: !!(upstream.apiKey) };
12882+ const config = readConfig();
12883+ const provider = config.model_providers && config.model_providers[name];
12884+ const providerMaxRetries = resolveProviderOpenaiBridgeMaxRetries(provider);
12885+ result = { baseUrl: upstream.baseUrl, hasApiKey: !!(upstream.apiKey), maxRetries: providerMaxRetries };
1282312886 break;
1282412887 }
1282512888 case 'list-sessions':
@@ -15059,11 +15122,13 @@ function buildMcpProviderListPayload() {
1505915122 upstreamUrl = upstream.baseUrl.trim();
1506015123 }
1506115124 }
15125+ const openaiBridgeMaxRetries = resolveProviderOpenaiBridgeMaxRetries(p);
1506215126 return {
1506315127 name,
1506415128 url: p.base_url || '',
1506515129 upstreamUrl,
1506615130 codexmate_bridge: bridge,
15131+ openaiBridgeMaxRetries,
1506715132 key: maskKey(p.preferred_auth_method || ''),
1506815133 hasKey: !!(p.preferred_auth_method && p.preferred_auth_method.trim()),
1506915134 models: Array.isArray(p.models)
0 commit comments