codexmate 0.0.57 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README.vi.md +1 -0
- package/README.zh.md +2 -2
- package/cli/openai-bridge-retry.js +62 -0
- package/cli/openai-bridge-runtime.js +1819 -0
- package/cli/openai-bridge.js +137 -2048
- package/cli.js +69 -4
- package/package.json +1 -1
- package/web-ui/app.js +12 -2
- package/web-ui/modules/app.methods.agents.mjs +189 -1
- package/web-ui/modules/app.methods.providers.mjs +40 -10
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +28 -1
- package/web-ui/modules/i18n/locales/en.mjs +35 -4
- package/web-ui/modules/i18n/locales/ja.mjs +35 -4
- package/web-ui/modules/i18n/locales/vi.mjs +35 -4
- package/web-ui/modules/i18n/locales/zh-tw.mjs +35 -4
- package/web-ui/modules/i18n/locales/zh.mjs +35 -4
- package/web-ui/partials/index/layout-header.html +8 -25
- package/web-ui/partials/index/modals-basic.html +26 -0
- package/web-ui/partials/index/panel-prompts.html +66 -3
- package/web-ui/res/web-ui-render.precompiled.js +177 -39
- package/web-ui/styles/modals-core.css +173 -0
- package/web-ui/styles/responsive.css +32 -0
package/cli.js
CHANGED
|
@@ -2561,6 +2561,28 @@ function buildClaudeSettingsDiff(params = {}) {
|
|
|
2561
2561
|
};
|
|
2562
2562
|
}
|
|
2563
2563
|
|
|
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
|
+
|
|
2564
2586
|
function addProviderToConfig(params = {}) {
|
|
2565
2587
|
const name = typeof params.name === 'string' ? params.name.trim() : '';
|
|
2566
2588
|
const url = typeof params.url === 'string' ? params.url.trim() : '';
|
|
@@ -2575,6 +2597,8 @@ function addProviderToConfig(params = {}) {
|
|
|
2575
2597
|
? params.model.trim()
|
|
2576
2598
|
: fallbackModel;
|
|
2577
2599
|
const useTransform = !!params.useTransform;
|
|
2600
|
+
const hasOpenaiBridgeMaxRetries = params.openaiBridgeMaxRetries !== undefined || params.maxRetries !== undefined;
|
|
2601
|
+
const openaiBridgeMaxRetries = normalizeOpenaiBridgeMaxRetries(params.openaiBridgeMaxRetries ?? params.maxRetries);
|
|
2578
2602
|
const allowManaged = !!params.allowManaged;
|
|
2579
2603
|
const normalizedUrl = normalizeBaseUrl(url);
|
|
2580
2604
|
|
|
@@ -2634,7 +2658,7 @@ function addProviderToConfig(params = {}) {
|
|
|
2634
2658
|
const requiresOpenaiAuth = useTransform;
|
|
2635
2659
|
|
|
2636
2660
|
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 });
|
|
2638
2662
|
if (saveRes && saveRes.error) {
|
|
2639
2663
|
return { error: String(saveRes.error) };
|
|
2640
2664
|
}
|
|
@@ -2646,6 +2670,7 @@ function addProviderToConfig(params = {}) {
|
|
|
2646
2670
|
).toString().replace(/\/+$/g, '');
|
|
2647
2671
|
authKeyForConfig = 'codexmate';
|
|
2648
2672
|
extraLines.push(`codexmate_bridge = "openai"`);
|
|
2673
|
+
extraLines.push(`codexmate_bridge_max_retries = ${openaiBridgeMaxRetries}`);
|
|
2649
2674
|
}
|
|
2650
2675
|
|
|
2651
2676
|
const safeUrl = escapeTomlBasicString(baseUrlForConfig);
|
|
@@ -2689,6 +2714,8 @@ function updateProviderInConfig(params = {}) {
|
|
|
2689
2714
|
? String(params.key).trim()
|
|
2690
2715
|
: undefined;
|
|
2691
2716
|
const useTransform = !!params.useTransform;
|
|
2717
|
+
const hasOpenaiBridgeMaxRetries = params.openaiBridgeMaxRetries !== undefined || params.maxRetries !== undefined;
|
|
2718
|
+
const openaiBridgeMaxRetries = normalizeOpenaiBridgeMaxRetries(params.openaiBridgeMaxRetries ?? params.maxRetries);
|
|
2692
2719
|
const allowManaged = !!params.allowManaged;
|
|
2693
2720
|
|
|
2694
2721
|
if (!name) return { error: '名称不能为空' };
|
|
@@ -2703,7 +2730,7 @@ function updateProviderInConfig(params = {}) {
|
|
|
2703
2730
|
}
|
|
2704
2731
|
|
|
2705
2732
|
try {
|
|
2706
|
-
cmdUpdate(name, url || undefined, key, true, { allowManaged, useTransform });
|
|
2733
|
+
cmdUpdate(name, url || undefined, key, true, { allowManaged, useTransform, ...(hasOpenaiBridgeMaxRetries ? { openaiBridgeMaxRetries } : {}) });
|
|
2707
2734
|
return { success: true };
|
|
2708
2735
|
} catch (e) {
|
|
2709
2736
|
return { error: e.message || '更新失败' };
|
|
@@ -9846,6 +9873,8 @@ function cmdDelete(name, silent = false) {
|
|
|
9846
9873
|
function cmdUpdate(name, baseUrl, apiKey, silent = false, options = {}) {
|
|
9847
9874
|
const allowManaged = !!(options && options.allowManaged);
|
|
9848
9875
|
const forceUseTransform = !!(options && options.useTransform);
|
|
9876
|
+
const hasOpenaiBridgeMaxRetries = options && Object.prototype.hasOwnProperty.call(options, 'openaiBridgeMaxRetries');
|
|
9877
|
+
const openaiBridgeMaxRetries = normalizeOpenaiBridgeMaxRetries(options && options.openaiBridgeMaxRetries);
|
|
9849
9878
|
const normalizedBaseUrl = baseUrl === undefined ? undefined : normalizeBaseUrl(baseUrl);
|
|
9850
9879
|
if (!name) {
|
|
9851
9880
|
if (!silent) console.error('错误: 提供商名称必填');
|
|
@@ -10005,6 +10034,32 @@ function cmdUpdate(name, baseUrl, apiKey, silent = false, options = {}) {
|
|
|
10005
10034
|
return next;
|
|
10006
10035
|
};
|
|
10007
10036
|
|
|
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
|
+
|
|
10008
10063
|
const replaceTomlBooleanField = (block, fieldName, rawValue) => {
|
|
10009
10064
|
const boolValue = rawValue ? 'true' : 'false';
|
|
10010
10065
|
const escapedFieldName = escapeRegex(fieldName);
|
|
@@ -10061,7 +10116,9 @@ function cmdUpdate(name, baseUrl, apiKey, silent = false, options = {}) {
|
|
|
10061
10116
|
: existingApiKey;
|
|
10062
10117
|
|
|
10063
10118
|
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
|
+
});
|
|
10065
10122
|
if (saveRes && saveRes.error) {
|
|
10066
10123
|
throw new Error(String(saveRes.error));
|
|
10067
10124
|
}
|
|
@@ -10077,6 +10134,9 @@ function cmdUpdate(name, baseUrl, apiKey, silent = false, options = {}) {
|
|
|
10077
10134
|
updatedBlock = replaceTomlBooleanField(updatedBlock, 'requires_openai_auth', true);
|
|
10078
10135
|
updatedBlock = replaceTomlStringField(updatedBlock, 'preferred_auth_method', 'codexmate');
|
|
10079
10136
|
updatedBlock = replaceTomlStringField(updatedBlock, 'codexmate_bridge', 'openai');
|
|
10137
|
+
if (hasOpenaiBridgeMaxRetries) {
|
|
10138
|
+
updatedBlock = replaceTomlNumberField(updatedBlock, 'codexmate_bridge_max_retries', openaiBridgeMaxRetries);
|
|
10139
|
+
}
|
|
10080
10140
|
} else {
|
|
10081
10141
|
if (normalizedBaseUrl) {
|
|
10082
10142
|
updatedBlock = replaceTomlStringField(updatedBlock, 'base_url', normalizedBaseUrl);
|
|
@@ -12819,7 +12879,10 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
12819
12879
|
break;
|
|
12820
12880
|
}
|
|
12821
12881
|
// 不返回 apiKey(敏感信息),仅返回用户填过的上游 URL
|
|
12822
|
-
|
|
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 };
|
|
12823
12886
|
break;
|
|
12824
12887
|
}
|
|
12825
12888
|
case 'list-sessions':
|
|
@@ -15059,11 +15122,13 @@ function buildMcpProviderListPayload() {
|
|
|
15059
15122
|
upstreamUrl = upstream.baseUrl.trim();
|
|
15060
15123
|
}
|
|
15061
15124
|
}
|
|
15125
|
+
const openaiBridgeMaxRetries = resolveProviderOpenaiBridgeMaxRetries(p);
|
|
15062
15126
|
return {
|
|
15063
15127
|
name,
|
|
15064
15128
|
url: p.base_url || '',
|
|
15065
15129
|
upstreamUrl,
|
|
15066
15130
|
codexmate_bridge: bridge,
|
|
15131
|
+
openaiBridgeMaxRetries,
|
|
15067
15132
|
key: maskKey(p.preferred_auth_method || ''),
|
|
15068
15133
|
hasKey: !!(p.preferred_auth_method && p.preferred_auth_method.trim()),
|
|
15069
15134
|
models: Array.isArray(p.models)
|
package/package.json
CHANGED
package/web-ui/app.js
CHANGED
|
@@ -74,6 +74,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
74
74
|
showAgentsModal: false,
|
|
75
75
|
promptsSubTab: 'codex',
|
|
76
76
|
projectClaudeMdPath: '',
|
|
77
|
+
promptPresets: [],
|
|
78
|
+
selectedPromptPresetId: '',
|
|
79
|
+
promptPresetNameDraft: '',
|
|
80
|
+
promptPresetRenameDraft: {},
|
|
81
|
+
promptPresetSaving: false,
|
|
82
|
+
__skipNextPromptsSubTabLoad: false,
|
|
77
83
|
projectPathOptions: [],
|
|
78
84
|
projectPathOptionsLoading: false,
|
|
79
85
|
showSkillsModal: false,
|
|
@@ -284,9 +290,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
284
290
|
appVersionStatusChecked: false,
|
|
285
291
|
appVersionStatusCheckedAt: '',
|
|
286
292
|
appVersionStatusSource: '',
|
|
287
|
-
newProvider: { name: '', url: '', key: '', model: '', useTransform: false },
|
|
293
|
+
newProvider: { name: '', url: '', key: '', model: '', useTransform: false, openaiBridgeMaxRetries: 2 },
|
|
288
294
|
resetConfigLoading: false,
|
|
289
|
-
editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false },
|
|
295
|
+
editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false, useTransform: false, openaiBridgeMaxRetries: 2 },
|
|
290
296
|
newModelName: '',
|
|
291
297
|
currentClaudeConfig: '',
|
|
292
298
|
currentClaudeModel: '',
|
|
@@ -737,6 +743,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
737
743
|
if (typeof this.persistWebUiPreferences === 'function') {
|
|
738
744
|
this.persistWebUiPreferences({ promptsSubTab: newVal });
|
|
739
745
|
}
|
|
746
|
+
if (this.__skipNextPromptsSubTabLoad) {
|
|
747
|
+
this.__skipNextPromptsSubTabLoad = false;
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
740
750
|
if (this.mainTab === 'prompts' && typeof this.loadPromptsContent === 'function') {
|
|
741
751
|
this.loadPromptsContent();
|
|
742
752
|
}
|
|
@@ -708,8 +708,193 @@ export function createAgentsMethods(options = {}) {
|
|
|
708
708
|
}
|
|
709
709
|
},
|
|
710
710
|
|
|
711
|
+
|
|
712
|
+
normalizePromptPresetName(name) {
|
|
713
|
+
return typeof name === 'string' ? name.trim() : '';
|
|
714
|
+
},
|
|
715
|
+
buildPromptPresetId() {
|
|
716
|
+
const randomPart = Math.random().toString(36).slice(2, 8);
|
|
717
|
+
return `prompt-preset-${Date.now()}-${randomPart}`;
|
|
718
|
+
},
|
|
719
|
+
getPromptPresetRenameDraft(preset) {
|
|
720
|
+
if (!preset || !preset.id) return '';
|
|
721
|
+
if (Object.prototype.hasOwnProperty.call(this.promptPresetRenameDraft || {}, preset.id)) {
|
|
722
|
+
return this.promptPresetRenameDraft[preset.id];
|
|
723
|
+
}
|
|
724
|
+
return preset.name || '';
|
|
725
|
+
},
|
|
726
|
+
setPromptPresetRenameDraft(id, value) {
|
|
727
|
+
if (!id) return;
|
|
728
|
+
this.promptPresetRenameDraft = {
|
|
729
|
+
...(this.promptPresetRenameDraft || {}),
|
|
730
|
+
[id]: value
|
|
731
|
+
};
|
|
732
|
+
},
|
|
733
|
+
formatPromptPresetTime(value) {
|
|
734
|
+
if (typeof value !== 'string' || !value) {
|
|
735
|
+
return this.t('common.none');
|
|
736
|
+
}
|
|
737
|
+
const date = new Date(value);
|
|
738
|
+
if (Number.isNaN(date.getTime())) {
|
|
739
|
+
return value;
|
|
740
|
+
}
|
|
741
|
+
return date.toLocaleString();
|
|
742
|
+
},
|
|
743
|
+
findPromptPresetByName(name, excludeId = '') {
|
|
744
|
+
const normalizedName = this.normalizePromptPresetName(name).toLowerCase();
|
|
745
|
+
return (Array.isArray(this.promptPresets) ? this.promptPresets : []).find((preset) => {
|
|
746
|
+
if (!preset || preset.id === excludeId) return false;
|
|
747
|
+
return this.normalizePromptPresetName(preset.name).toLowerCase() === normalizedName;
|
|
748
|
+
}) || null;
|
|
749
|
+
},
|
|
750
|
+
async persistPromptPresets() {
|
|
751
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
752
|
+
await this.persistWebUiPreferences({ promptPresets: this.promptPresets });
|
|
753
|
+
}
|
|
754
|
+
},
|
|
755
|
+
getCurrentPromptPresetDefaultName() {
|
|
756
|
+
if (this.promptsSubTab === 'claude-project') {
|
|
757
|
+
const projectPath = typeof this.projectClaudeMdPath === 'string' ? this.projectClaudeMdPath.trim() : '';
|
|
758
|
+
return projectPath
|
|
759
|
+
? this.t('prompts.presets.defaultName.project', { path: projectPath })
|
|
760
|
+
: this.t('prompts.subTab.project');
|
|
761
|
+
}
|
|
762
|
+
const fileName = typeof this.agentsPath === 'string' && this.agentsPath.trim()
|
|
763
|
+
? (this.agentsPath.trim().split(/[\\/]/).filter(Boolean).pop() || '')
|
|
764
|
+
: '';
|
|
765
|
+
return fileName || this.t('prompts.subTab.codex');
|
|
766
|
+
},
|
|
767
|
+
async saveEditorPromptAsPreset() {
|
|
768
|
+
if (this.promptPresetSaving || this.agentsLoading || this.agentsDiffVisible) return;
|
|
769
|
+
const name = this.getCurrentPromptPresetDefaultName();
|
|
770
|
+
const content = typeof this.agentsContent === 'string' ? this.agentsContent : '';
|
|
771
|
+
if (!content.trim()) {
|
|
772
|
+
this.showMessage(this.t('prompts.presets.error.emptyContent'), 'error');
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
const confirmed = await this.requestConfirmDialog({
|
|
776
|
+
title: this.t('prompts.presets.confirm.addCurrentTitle'),
|
|
777
|
+
message: this.t('prompts.presets.confirm.addCurrentMessage', { name }),
|
|
778
|
+
confirmText: this.t('prompts.presets.addCurrent'),
|
|
779
|
+
cancelText: this.t('common.cancel'),
|
|
780
|
+
danger: false
|
|
781
|
+
});
|
|
782
|
+
if (!confirmed) return;
|
|
783
|
+
this.promptPresetNameDraft = name;
|
|
784
|
+
await this.saveCurrentPromptAsPreset();
|
|
785
|
+
},
|
|
786
|
+
async saveCurrentPromptAsPreset() {
|
|
787
|
+
if (this.promptPresetSaving || this.agentsLoading) return;
|
|
788
|
+
const draftName = this.normalizePromptPresetName(this.promptPresetNameDraft);
|
|
789
|
+
const name = draftName || this.normalizePromptPresetName(this.getCurrentPromptPresetDefaultName());
|
|
790
|
+
const content = typeof this.agentsContent === 'string' ? this.agentsContent : '';
|
|
791
|
+
if (!name) {
|
|
792
|
+
this.showMessage(this.t('prompts.presets.error.emptyName'), 'error');
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
if (!content.trim()) {
|
|
796
|
+
this.showMessage(this.t('prompts.presets.error.emptyContent'), 'error');
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
const existing = this.findPromptPresetByName(name);
|
|
800
|
+
if (existing) {
|
|
801
|
+
const confirmed = await this.requestConfirmDialog({
|
|
802
|
+
title: this.t('prompts.presets.confirm.overwriteTitle'),
|
|
803
|
+
message: this.t('prompts.presets.confirm.overwriteMessage', { name }),
|
|
804
|
+
confirmText: this.t('prompts.presets.confirm.overwriteConfirm'),
|
|
805
|
+
cancelText: this.t('common.cancel'),
|
|
806
|
+
danger: false
|
|
807
|
+
});
|
|
808
|
+
if (!confirmed) return;
|
|
809
|
+
}
|
|
810
|
+
this.promptPresetSaving = true;
|
|
811
|
+
try {
|
|
812
|
+
const now = new Date().toISOString();
|
|
813
|
+
if (existing) {
|
|
814
|
+
this.promptPresets = this.promptPresets.map((preset) => preset.id === existing.id
|
|
815
|
+
? { ...preset, name, content, updatedAt: now }
|
|
816
|
+
: preset);
|
|
817
|
+
this.selectedPromptPresetId = existing.id;
|
|
818
|
+
} else {
|
|
819
|
+
const preset = {
|
|
820
|
+
id: this.buildPromptPresetId(),
|
|
821
|
+
name,
|
|
822
|
+
content,
|
|
823
|
+
updatedAt: now
|
|
824
|
+
};
|
|
825
|
+
this.promptPresets = [preset, ...this.promptPresets];
|
|
826
|
+
this.selectedPromptPresetId = preset.id;
|
|
827
|
+
}
|
|
828
|
+
this.promptPresetNameDraft = '';
|
|
829
|
+
await this.persistPromptPresets();
|
|
830
|
+
this.showMessage(this.t('prompts.presets.toast.saved'), 'success');
|
|
831
|
+
} finally {
|
|
832
|
+
this.promptPresetSaving = false;
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
async applyPromptPresetToEditor(preset) {
|
|
836
|
+
if (!preset || typeof preset.content !== 'string' || !preset.content) return;
|
|
837
|
+
this.agentsContent = preset.content;
|
|
838
|
+
this.onAgentsContentInput();
|
|
839
|
+
this.selectedPromptPresetId = preset.id;
|
|
840
|
+
this.showMessage(this.t('prompts.presets.toast.pasted'), 'success');
|
|
841
|
+
},
|
|
842
|
+
async applyPromptPresetSelection(event) {
|
|
843
|
+
const select = event && event.target;
|
|
844
|
+
const presetId = select && typeof select.value === 'string' ? select.value : '';
|
|
845
|
+
if (!presetId) return;
|
|
846
|
+
const preset = (Array.isArray(this.promptPresets) ? this.promptPresets : []).find((item) => item && item.id === presetId);
|
|
847
|
+
await this.applyPromptPresetToEditor(preset);
|
|
848
|
+
if (select) select.value = '';
|
|
849
|
+
},
|
|
850
|
+
async renamePromptPreset(preset) {
|
|
851
|
+
if (!preset || !preset.id) return;
|
|
852
|
+
const name = this.normalizePromptPresetName(this.getPromptPresetRenameDraft(preset));
|
|
853
|
+
if (!name) {
|
|
854
|
+
this.showMessage(this.t('prompts.presets.error.emptyName'), 'error');
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
const existing = this.findPromptPresetByName(name, preset.id);
|
|
858
|
+
if (existing) {
|
|
859
|
+
this.showMessage(this.t('prompts.presets.error.duplicateName'), 'error');
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
if (name === preset.name) {
|
|
863
|
+
this.showMessage(this.t('toast.noChanges'), 'info');
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
const now = new Date().toISOString();
|
|
867
|
+
this.promptPresets = this.promptPresets.map((item) => item.id === preset.id
|
|
868
|
+
? { ...item, name, updatedAt: now }
|
|
869
|
+
: item);
|
|
870
|
+
const drafts = { ...(this.promptPresetRenameDraft || {}) };
|
|
871
|
+
delete drafts[preset.id];
|
|
872
|
+
this.promptPresetRenameDraft = drafts;
|
|
873
|
+
await this.persistPromptPresets();
|
|
874
|
+
this.showMessage(this.t('prompts.presets.toast.renamed'), 'success');
|
|
875
|
+
},
|
|
876
|
+
async deletePromptPreset(preset) {
|
|
877
|
+
if (!preset || !preset.id) return;
|
|
878
|
+
const confirmed = await this.requestConfirmDialog({
|
|
879
|
+
title: this.t('prompts.presets.confirm.deleteTitle'),
|
|
880
|
+
message: this.t('prompts.presets.confirm.deleteMessage', { name: preset.name || '' }),
|
|
881
|
+
confirmText: this.t('common.delete'),
|
|
882
|
+
cancelText: this.t('common.cancel'),
|
|
883
|
+
danger: true
|
|
884
|
+
});
|
|
885
|
+
if (!confirmed) return;
|
|
886
|
+
this.promptPresets = this.promptPresets.filter((item) => item.id !== preset.id);
|
|
887
|
+
if (this.selectedPromptPresetId === preset.id) {
|
|
888
|
+
this.selectedPromptPresetId = '';
|
|
889
|
+
}
|
|
890
|
+
const drafts = { ...(this.promptPresetRenameDraft || {}) };
|
|
891
|
+
delete drafts[preset.id];
|
|
892
|
+
this.promptPresetRenameDraft = drafts;
|
|
893
|
+
await this.persistPromptPresets();
|
|
894
|
+
this.showMessage(this.t('prompts.presets.toast.deleted'), 'success');
|
|
895
|
+
},
|
|
711
896
|
switchPromptsSubTab(subTab) {
|
|
712
|
-
const normalized = subTab === 'claude-project' ?
|
|
897
|
+
const normalized = subTab === 'claude-project' ? subTab : 'codex';
|
|
713
898
|
if (normalized === 'claude-project' && !this.projectPathOptions.length && !this.projectPathOptionsLoading) {
|
|
714
899
|
this.loadProjectPathOptions();
|
|
715
900
|
}
|
|
@@ -718,6 +903,9 @@ export function createAgentsMethods(options = {}) {
|
|
|
718
903
|
return;
|
|
719
904
|
}
|
|
720
905
|
this.promptsSubTab = normalized;
|
|
906
|
+
this.$nextTick(() => {
|
|
907
|
+
document.querySelector('.main-panel')?.scrollTo({ top: 0, left: 0, behavior: 'auto' });
|
|
908
|
+
});
|
|
721
909
|
},
|
|
722
910
|
|
|
723
911
|
async loadPromptsContent() {
|
|
@@ -41,6 +41,13 @@ function findProviderByName(list, name) {
|
|
|
41
41
|
return (Array.isArray(list) ? list : []).find((item) => item && normalizeText(item.name) === target) || null;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
function normalizeBridgeMaxRetries(value, fallback = 2) {
|
|
45
|
+
const raw = Number(value);
|
|
46
|
+
const fallbackRaw = Number(fallback);
|
|
47
|
+
const base = Number.isFinite(raw) ? raw : (Number.isFinite(fallbackRaw) ? fallbackRaw : 2);
|
|
48
|
+
return Math.min(10, Math.max(2, Math.floor(base)));
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
function normalizeProviderDraftState(target) {
|
|
45
52
|
if (!target || typeof target !== 'object') return;
|
|
46
53
|
if (typeof target.name === 'string') {
|
|
@@ -55,6 +62,9 @@ function normalizeProviderDraftState(target) {
|
|
|
55
62
|
if (typeof target.key === 'string') {
|
|
56
63
|
target.key = target.key.trim();
|
|
57
64
|
}
|
|
65
|
+
if (target.useTransform || target.openaiBridgeMaxRetries !== undefined) {
|
|
66
|
+
target.openaiBridgeMaxRetries = normalizeBridgeMaxRetries(target.openaiBridgeMaxRetries);
|
|
67
|
+
}
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
function maskKeyLocal(key) {
|
|
@@ -76,11 +86,14 @@ function getProviderValidationForContext(vm, mode = 'add') {
|
|
|
76
86
|
const url = normalizeProviderUrl(draft && draft.url);
|
|
77
87
|
const model = normalizeText(draft && draft.model);
|
|
78
88
|
const key = normalizeText(draft && draft.key);
|
|
89
|
+
const useTransform = !!(draft && draft.useTransform);
|
|
90
|
+
const openaiBridgeMaxRetries = normalizeBridgeMaxRetries(draft && draft.openaiBridgeMaxRetries);
|
|
79
91
|
const errors = {
|
|
80
92
|
name: '',
|
|
81
93
|
url: '',
|
|
82
94
|
key: '',
|
|
83
|
-
model: ''
|
|
95
|
+
model: '',
|
|
96
|
+
openaiBridgeMaxRetries: ''
|
|
84
97
|
};
|
|
85
98
|
|
|
86
99
|
if (mode === 'add') {
|
|
@@ -111,14 +124,20 @@ function getProviderValidationForContext(vm, mode = 'add') {
|
|
|
111
124
|
errors.model = '模型名称必填';
|
|
112
125
|
}
|
|
113
126
|
|
|
127
|
+
if (useTransform && openaiBridgeMaxRetries < 2) {
|
|
128
|
+
errors.openaiBridgeMaxRetries = '重试次数最小为 2';
|
|
129
|
+
}
|
|
130
|
+
|
|
114
131
|
return {
|
|
115
132
|
mode,
|
|
116
133
|
name,
|
|
117
134
|
url,
|
|
118
135
|
key,
|
|
119
136
|
model,
|
|
137
|
+
useTransform,
|
|
138
|
+
openaiBridgeMaxRetries,
|
|
120
139
|
errors,
|
|
121
|
-
ok: !errors.name && !errors.url && !errors.key && !errors.model
|
|
140
|
+
ok: !errors.name && !errors.url && !errors.key && !errors.model && !errors.openaiBridgeMaxRetries
|
|
122
141
|
};
|
|
123
142
|
}
|
|
124
143
|
|
|
@@ -172,7 +191,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
172
191
|
normalizeProviderDraftState(this.newProvider);
|
|
173
192
|
const validation = getProviderValidationForContext(this, 'add');
|
|
174
193
|
if (!validation.ok) {
|
|
175
|
-
return this.showMessage(validation.errors.name || validation.errors.url || validation.errors.key || validation.errors.model || this.t('toast.provider.fieldsRequired'), 'error');
|
|
194
|
+
return this.showMessage(validation.errors.name || validation.errors.url || validation.errors.key || validation.errors.model || validation.errors.openaiBridgeMaxRetries || this.t('toast.provider.fieldsRequired'), 'error');
|
|
176
195
|
}
|
|
177
196
|
|
|
178
197
|
try {
|
|
@@ -184,6 +203,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
184
203
|
};
|
|
185
204
|
if (this.newProvider && this.newProvider.useTransform) {
|
|
186
205
|
payload.useTransform = true;
|
|
206
|
+
payload.openaiBridgeMaxRetries = validation.openaiBridgeMaxRetries;
|
|
187
207
|
}
|
|
188
208
|
const suggestedModel = validation.model;
|
|
189
209
|
const res = await api('add-provider', payload);
|
|
@@ -198,6 +218,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
198
218
|
url: validation.url,
|
|
199
219
|
upstreamUrl: '',
|
|
200
220
|
codexmate_bridge: payload.useTransform ? 'openai' : '',
|
|
221
|
+
openaiBridgeMaxRetries: payload.useTransform ? validation.openaiBridgeMaxRetries : undefined,
|
|
201
222
|
key: maskKeyLocal(payload.key),
|
|
202
223
|
hasKey: !!payload.key,
|
|
203
224
|
models: suggestedModel ? [{ id: suggestedModel, name: suggestedModel, cost: null, contextWindow: undefined, maxTokens: undefined }] : [],
|
|
@@ -323,7 +344,8 @@ export function createProvidersMethods(options = {}) {
|
|
|
323
344
|
url: cloneUrl,
|
|
324
345
|
key: '',
|
|
325
346
|
model: '',
|
|
326
|
-
useTransform: isTransform
|
|
347
|
+
useTransform: isTransform,
|
|
348
|
+
openaiBridgeMaxRetries: normalizeBridgeMaxRetries(provider.openaiBridgeMaxRetries)
|
|
327
349
|
};
|
|
328
350
|
this.showAddProviderKey = false;
|
|
329
351
|
this.showAddModal = true;
|
|
@@ -335,7 +357,8 @@ export function createProvidersMethods(options = {}) {
|
|
|
335
357
|
url: '',
|
|
336
358
|
key: '',
|
|
337
359
|
model: '',
|
|
338
|
-
useTransform: false
|
|
360
|
+
useTransform: false,
|
|
361
|
+
openaiBridgeMaxRetries: 2
|
|
339
362
|
};
|
|
340
363
|
this.showAddProviderKey = false;
|
|
341
364
|
this.showAddModal = true;
|
|
@@ -363,7 +386,8 @@ export function createProvidersMethods(options = {}) {
|
|
|
363
386
|
nonEditable: typeof provider.nonEditable === 'boolean'
|
|
364
387
|
? provider.nonEditable
|
|
365
388
|
: this.isNonDeletableProvider(provider),
|
|
366
|
-
useTransform: isTransformProvider
|
|
389
|
+
useTransform: isTransformProvider,
|
|
390
|
+
openaiBridgeMaxRetries: normalizeBridgeMaxRetries(provider.openaiBridgeMaxRetries)
|
|
367
391
|
};
|
|
368
392
|
this._editProviderOriginalKey = '';
|
|
369
393
|
this._editProviderRealKeyLoaded = false;
|
|
@@ -404,6 +428,9 @@ export function createProvidersMethods(options = {}) {
|
|
|
404
428
|
&& res.baseUrl.trim()
|
|
405
429
|
) {
|
|
406
430
|
this.editingProvider.url = normalizeProviderUrl(res.baseUrl);
|
|
431
|
+
if (res.maxRetries !== undefined) {
|
|
432
|
+
this.editingProvider.openaiBridgeMaxRetries = normalizeBridgeMaxRetries(res.maxRetries);
|
|
433
|
+
}
|
|
407
434
|
}
|
|
408
435
|
} catch (_) {
|
|
409
436
|
// ignore
|
|
@@ -420,12 +447,13 @@ export function createProvidersMethods(options = {}) {
|
|
|
420
447
|
normalizeProviderDraftState(this.editingProvider);
|
|
421
448
|
const validation = getProviderValidationForContext(this, 'edit');
|
|
422
449
|
if (!validation.ok) {
|
|
423
|
-
return this.showMessage(validation.errors.name || validation.errors.url || this.t('toast.provider.urlRequired'), 'error');
|
|
450
|
+
return this.showMessage(validation.errors.name || validation.errors.url || validation.errors.openaiBridgeMaxRetries || this.t('toast.provider.urlRequired'), 'error');
|
|
424
451
|
}
|
|
425
452
|
|
|
426
453
|
const params = { name: validation.name, url: validation.url };
|
|
427
454
|
if (this.editingProvider && this.editingProvider.useTransform) {
|
|
428
455
|
params.useTransform = true;
|
|
456
|
+
params.openaiBridgeMaxRetries = validation.openaiBridgeMaxRetries;
|
|
429
457
|
}
|
|
430
458
|
if (this._editProviderRealKeyLoaded) {
|
|
431
459
|
const currentKey = typeof this.editingProvider.key === 'string' ? this.editingProvider.key : '';
|
|
@@ -449,7 +477,9 @@ export function createProvidersMethods(options = {}) {
|
|
|
449
477
|
...p,
|
|
450
478
|
url: validation.url,
|
|
451
479
|
key: keyUpdated ? maskKeyLocal(params.key) : p.key,
|
|
452
|
-
hasKey: keyUpdated ? !!params.key : p.hasKey
|
|
480
|
+
hasKey: keyUpdated ? !!params.key : p.hasKey,
|
|
481
|
+
codexmate_bridge: params.useTransform ? 'openai' : p.codexmate_bridge,
|
|
482
|
+
openaiBridgeMaxRetries: params.useTransform ? validation.openaiBridgeMaxRetries : p.openaiBridgeMaxRetries
|
|
453
483
|
};
|
|
454
484
|
}
|
|
455
485
|
return p;
|
|
@@ -467,7 +497,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
467
497
|
this.showEditProviderKey = false;
|
|
468
498
|
this._editProviderOriginalKey = '';
|
|
469
499
|
this._editProviderRealKeyLoaded = false;
|
|
470
|
-
this.editingProvider = { name: '', url: '', key: '', readOnly: false, nonEditable: false, useTransform: false };
|
|
500
|
+
this.editingProvider = { name: '', url: '', key: '', readOnly: false, nonEditable: false, useTransform: false, openaiBridgeMaxRetries: 2 };
|
|
471
501
|
},
|
|
472
502
|
|
|
473
503
|
toggleEditProviderKey() {
|
|
@@ -550,7 +580,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
550
580
|
closeAddModal() {
|
|
551
581
|
this.showAddModal = false;
|
|
552
582
|
this.showAddProviderKey = false;
|
|
553
|
-
this.newProvider = { name: nextCodexProviderName(this.providersList), url: '', key: '', model: '', useTransform: false };
|
|
583
|
+
this.newProvider = { name: nextCodexProviderName(this.providersList), url: '', key: '', model: '', useTransform: false, openaiBridgeMaxRetries: 2 };
|
|
554
584
|
},
|
|
555
585
|
|
|
556
586
|
toggleAddProviderKey() {
|
|
@@ -62,7 +62,27 @@ function normalizeUsageTimeRange(value) {
|
|
|
62
62
|
|
|
63
63
|
function normalizePromptsSubTab(value) {
|
|
64
64
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
65
|
-
|
|
65
|
+
if (normalized === 'claude-project') return normalized;
|
|
66
|
+
return 'codex';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function normalizePromptPresets(value) {
|
|
70
|
+
if (!Array.isArray(value)) return [];
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
return value
|
|
73
|
+
.filter(item => item && typeof item === 'object')
|
|
74
|
+
.map((item, index) => {
|
|
75
|
+
const id = typeof item.id === 'string' && item.id.trim()
|
|
76
|
+
? item.id.trim()
|
|
77
|
+
: `preset-${Date.now()}-${index}`;
|
|
78
|
+
const name = typeof item.name === 'string' ? item.name.trim() : '';
|
|
79
|
+
const content = typeof item.content === 'string' ? item.content : '';
|
|
80
|
+
const updatedAt = typeof item.updatedAt === 'string' && item.updatedAt.trim()
|
|
81
|
+
? item.updatedAt.trim()
|
|
82
|
+
: new Date(0).toISOString();
|
|
83
|
+
return { id, name, content, updatedAt };
|
|
84
|
+
})
|
|
85
|
+
.filter(item => item.id && item.name && !seen.has(item.id) && (seen.add(item.id), true));
|
|
66
86
|
}
|
|
67
87
|
|
|
68
88
|
function normalizeSessionSortMode(value) {
|
|
@@ -313,6 +333,7 @@ export function createWebUiPreferencesMethods(options = {}) {
|
|
|
313
333
|
: this.configTemplateDiffConfirmEnabled !== false,
|
|
314
334
|
sessionsUsageTimeRange: normalizeUsageTimeRange(hasOwn(source, 'sessionsUsageTimeRange') ? source.sessionsUsageTimeRange : this.sessionsUsageTimeRange),
|
|
315
335
|
promptsSubTab: normalizePromptsSubTab(hasOwn(source, 'promptsSubTab') ? source.promptsSubTab : this.promptsSubTab),
|
|
336
|
+
promptPresets: normalizePromptPresets(hasOwn(source, 'promptPresets') ? source.promptPresets : this.promptPresets),
|
|
316
337
|
projectClaudeMdPath: typeof source.projectClaudeMdPath === 'string'
|
|
317
338
|
? source.projectClaudeMdPath
|
|
318
339
|
: (typeof this.projectClaudeMdPath === 'string' ? this.projectClaudeMdPath : ''),
|
|
@@ -365,6 +386,12 @@ export function createWebUiPreferencesMethods(options = {}) {
|
|
|
365
386
|
if (typeof source.promptsSubTab === 'string') {
|
|
366
387
|
this.promptsSubTab = normalizePromptsSubTab(source.promptsSubTab);
|
|
367
388
|
}
|
|
389
|
+
if (Array.isArray(source.promptPresets)) {
|
|
390
|
+
this.promptPresets = normalizePromptPresets(source.promptPresets);
|
|
391
|
+
if (this.selectedPromptPresetId && !this.promptPresets.some(preset => preset.id === this.selectedPromptPresetId)) {
|
|
392
|
+
this.selectedPromptPresetId = '';
|
|
393
|
+
}
|
|
394
|
+
}
|
|
368
395
|
if (typeof source.projectClaudeMdPath === 'string') {
|
|
369
396
|
this.projectClaudeMdPath = source.projectClaudeMdPath;
|
|
370
397
|
}
|