codexmate 0.0.57 → 0.1.2
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 +70 -0
- package/cli/openai-bridge-runtime.js +1832 -0
- package/cli/openai-bridge.js +143 -2044
- package/cli.js +69 -4
- package/package.json +1 -1
- package/web-ui/app.js +11 -1
- package/web-ui/modules/app.methods.agents.mjs +191 -3
- package/web-ui/modules/app.methods.openclaw-core.mjs +87 -0
- package/web-ui/modules/app.methods.providers.mjs +8 -5
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +28 -1
- package/web-ui/modules/i18n/locales/en.mjs +33 -4
- package/web-ui/modules/i18n/locales/ja.mjs +33 -4
- package/web-ui/modules/i18n/locales/vi.mjs +33 -4
- package/web-ui/modules/i18n/locales/zh-tw.mjs +33 -4
- package/web-ui/modules/i18n/locales/zh.mjs +37 -8
- package/web-ui/partials/index/layout-header.html +8 -25
- package/web-ui/partials/index/modal-config-template-agents.html +8 -4
- package/web-ui/partials/index/panel-config-openclaw.html +42 -14
- package/web-ui/partials/index/panel-prompts.html +66 -3
- package/web-ui/res/web-ui-render.precompiled.js +187 -67
- package/web-ui/styles/layout-shell.css +1 -0
- package/web-ui/styles/modals-core.css +252 -0
- package/web-ui/styles/openclaw-structured.css +117 -71
- 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,
|
|
@@ -286,7 +292,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
286
292
|
appVersionStatusSource: '',
|
|
287
293
|
newProvider: { name: '', url: '', key: '', model: '', useTransform: false },
|
|
288
294
|
resetConfigLoading: false,
|
|
289
|
-
editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false },
|
|
295
|
+
editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false, useTransform: false },
|
|
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
|
}
|
|
@@ -206,13 +206,13 @@ export function createAgentsMethods(options = {}) {
|
|
|
206
206
|
this.agentsContext = 'openclaw-workspace';
|
|
207
207
|
this.agentsWorkspaceFileName = fileName;
|
|
208
208
|
this.agentsModalTitle = tr('modal.agents.title.openclawWorkspaceFile', `OpenClaw 工作区文件: ${fileName}`, { fileName });
|
|
209
|
-
this.agentsModalHint = tr('modal.agents.hint.openclawWorkspaceFile',
|
|
209
|
+
this.agentsModalHint = tr('modal.agents.hint.openclawWorkspaceFile', `Workspace / ${fileName}`, { fileName });
|
|
210
210
|
return;
|
|
211
211
|
}
|
|
212
212
|
this.agentsContext = context === 'openclaw' ? 'openclaw' : 'codex';
|
|
213
213
|
if (this.agentsContext === 'openclaw') {
|
|
214
214
|
this.agentsModalTitle = tr('modal.agents.title.openclaw', 'OpenClaw AGENTS.md 编辑器');
|
|
215
|
-
this.agentsModalHint = tr('modal.agents.hint.openclaw', '
|
|
215
|
+
this.agentsModalHint = tr('modal.agents.hint.openclaw', 'Workspace / AGENTS.md');
|
|
216
216
|
} else {
|
|
217
217
|
this.agentsModalTitle = tr('modal.agents.title.default', 'AGENTS.md 编辑器');
|
|
218
218
|
this.agentsModalHint = tr('modal.agents.hint.default', '保存后会写入目标 AGENTS.md(与 config.toml 同级)。');
|
|
@@ -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() {
|
|
@@ -224,6 +224,31 @@ function readPreferredProviderModels(records) {
|
|
|
224
224
|
return [];
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
function readNestedValue(record, path) {
|
|
228
|
+
if (!isPlainRecord(record)) return undefined;
|
|
229
|
+
let cursor = record;
|
|
230
|
+
for (const key of path) {
|
|
231
|
+
if (!isPlainRecord(cursor) || !Object.prototype.hasOwnProperty.call(cursor, key)) {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
cursor = cursor[key];
|
|
235
|
+
}
|
|
236
|
+
return cursor;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function formatOpenclawSummaryValue(value, fallback = '未配置') {
|
|
240
|
+
if (Array.isArray(value)) {
|
|
241
|
+
const list = value
|
|
242
|
+
.map(item => (typeof item === 'string' ? item.trim() : String(item || '').trim()))
|
|
243
|
+
.filter(Boolean);
|
|
244
|
+
return list.length ? list.join(' / ') : fallback;
|
|
245
|
+
}
|
|
246
|
+
if (value === true) return '启用';
|
|
247
|
+
if (value === false) return '关闭';
|
|
248
|
+
const text = typeof value === 'string' ? value.trim() : String(value ?? '').trim();
|
|
249
|
+
return text || fallback;
|
|
250
|
+
}
|
|
251
|
+
|
|
227
252
|
export function createOpenclawCoreMethods() {
|
|
228
253
|
return {
|
|
229
254
|
getOpenclawParser() {
|
|
@@ -472,6 +497,68 @@ export function createOpenclawCoreMethods() {
|
|
|
472
497
|
};
|
|
473
498
|
},
|
|
474
499
|
|
|
500
|
+
getOpenclawConfigSummary(config) {
|
|
501
|
+
const content = config && typeof config.content === 'string' ? config.content : '';
|
|
502
|
+
if (!content.trim()) {
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
const parsed = this.parseOpenclawContent(content, { allowEmpty: true });
|
|
506
|
+
if (!parsed.ok || !isPlainRecord(parsed.data)) {
|
|
507
|
+
return [
|
|
508
|
+
{ key: 'parse-error', label: '配置状态', value: '解析失败', tone: 'warning' }
|
|
509
|
+
];
|
|
510
|
+
}
|
|
511
|
+
const data = parsed.data;
|
|
512
|
+
const agentDefaults = readNestedValue(data, ['agents', 'defaults']);
|
|
513
|
+
const modelConfig = isPlainRecord(agentDefaults) ? agentDefaults.model : undefined;
|
|
514
|
+
const primaryModel = isPlainRecord(modelConfig)
|
|
515
|
+
? modelConfig.primary
|
|
516
|
+
: (typeof modelConfig === 'string' ? modelConfig : readNestedValue(data, ['agent', 'model']));
|
|
517
|
+
const fallbacks = isPlainRecord(modelConfig) && Array.isArray(modelConfig.fallbacks)
|
|
518
|
+
? modelConfig.fallbacks
|
|
519
|
+
: [];
|
|
520
|
+
const workspace = isPlainRecord(agentDefaults) ? agentDefaults.workspace : '';
|
|
521
|
+
const browser = isPlainRecord(data.browser) ? data.browser : {};
|
|
522
|
+
const browserMode = browser.enabled === false
|
|
523
|
+
? '关闭'
|
|
524
|
+
: (browser.headless === true ? 'Headless' : (browser.headless === false ? '可见窗口' : '未声明'));
|
|
525
|
+
const executablePath = typeof browser.executablePath === 'string' ? browser.executablePath.trim() : '';
|
|
526
|
+
const providerNames = collectDistinctProviderKeys(
|
|
527
|
+
readNestedValue(data, ['models', 'providers']),
|
|
528
|
+
data.providers
|
|
529
|
+
);
|
|
530
|
+
return [
|
|
531
|
+
{ key: 'primary', label: '默认模型', value: formatOpenclawSummaryValue(primaryModel) },
|
|
532
|
+
{ key: 'fallbacks', label: 'Fallback', value: formatOpenclawSummaryValue(fallbacks) },
|
|
533
|
+
{ key: 'workspace', label: 'Workspace', value: formatOpenclawSummaryValue(workspace) },
|
|
534
|
+
{ key: 'browser', label: 'Browser', value: browserMode },
|
|
535
|
+
{ key: 'browser-path', label: 'Chrome', value: formatOpenclawSummaryValue(executablePath) },
|
|
536
|
+
{ key: 'providers', label: 'Providers', value: providerNames.length ? providerNames.join(' / ') : '未配置' }
|
|
537
|
+
];
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
getOpenclawStatusSummaryItems() {
|
|
541
|
+
const current = this.openclawConfigs && this.currentOpenclawConfig
|
|
542
|
+
? this.openclawConfigs[this.currentOpenclawConfig]
|
|
543
|
+
: null;
|
|
544
|
+
const configPath = this.openclawConfigPath || '~/.openclaw/openclaw.json';
|
|
545
|
+
return [
|
|
546
|
+
{ key: 'config-path', label: 'Config', value: configPath, tone: this.openclawConfigExists ? 'ok' : 'warning' },
|
|
547
|
+
...this.getOpenclawConfigSummary(current || {}).slice(0, 4)
|
|
548
|
+
];
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
getOpenclawQuickWorkspaceFiles() {
|
|
552
|
+
return ['SOUL.md', 'USER.md', 'TOOLS.md', 'HEARTBEAT.md'];
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
openOpenclawQuickWorkspaceFile(fileName) {
|
|
556
|
+
const normalized = typeof fileName === 'string' ? fileName.trim() : '';
|
|
557
|
+
if (!normalized) return;
|
|
558
|
+
this.openclawWorkspaceFileName = normalized;
|
|
559
|
+
this.openOpenclawWorkspaceEditor();
|
|
560
|
+
},
|
|
561
|
+
|
|
475
562
|
syncOpenclawQuickFromText(options = {}) {
|
|
476
563
|
const silent = !!options.silent;
|
|
477
564
|
const parsed = this.parseOpenclawContent(this.openclawEditing.content, { allowEmpty: true });
|
|
@@ -76,11 +76,12 @@ function getProviderValidationForContext(vm, mode = 'add') {
|
|
|
76
76
|
const url = normalizeProviderUrl(draft && draft.url);
|
|
77
77
|
const model = normalizeText(draft && draft.model);
|
|
78
78
|
const key = normalizeText(draft && draft.key);
|
|
79
|
+
const useTransform = !!(draft && draft.useTransform);
|
|
79
80
|
const errors = {
|
|
80
81
|
name: '',
|
|
81
82
|
url: '',
|
|
82
83
|
key: '',
|
|
83
|
-
model: ''
|
|
84
|
+
model: '',
|
|
84
85
|
};
|
|
85
86
|
|
|
86
87
|
if (mode === 'add') {
|
|
@@ -117,6 +118,7 @@ function getProviderValidationForContext(vm, mode = 'add') {
|
|
|
117
118
|
url,
|
|
118
119
|
key,
|
|
119
120
|
model,
|
|
121
|
+
useTransform,
|
|
120
122
|
errors,
|
|
121
123
|
ok: !errors.name && !errors.url && !errors.key && !errors.model
|
|
122
124
|
};
|
|
@@ -323,7 +325,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
323
325
|
url: cloneUrl,
|
|
324
326
|
key: '',
|
|
325
327
|
model: '',
|
|
326
|
-
useTransform: isTransform
|
|
328
|
+
useTransform: isTransform,
|
|
327
329
|
};
|
|
328
330
|
this.showAddProviderKey = false;
|
|
329
331
|
this.showAddModal = true;
|
|
@@ -335,7 +337,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
335
337
|
url: '',
|
|
336
338
|
key: '',
|
|
337
339
|
model: '',
|
|
338
|
-
useTransform: false
|
|
340
|
+
useTransform: false,
|
|
339
341
|
};
|
|
340
342
|
this.showAddProviderKey = false;
|
|
341
343
|
this.showAddModal = true;
|
|
@@ -363,7 +365,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
363
365
|
nonEditable: typeof provider.nonEditable === 'boolean'
|
|
364
366
|
? provider.nonEditable
|
|
365
367
|
: this.isNonDeletableProvider(provider),
|
|
366
|
-
useTransform: isTransformProvider
|
|
368
|
+
useTransform: isTransformProvider,
|
|
367
369
|
};
|
|
368
370
|
this._editProviderOriginalKey = '';
|
|
369
371
|
this._editProviderRealKeyLoaded = false;
|
|
@@ -449,7 +451,8 @@ export function createProvidersMethods(options = {}) {
|
|
|
449
451
|
...p,
|
|
450
452
|
url: validation.url,
|
|
451
453
|
key: keyUpdated ? maskKeyLocal(params.key) : p.key,
|
|
452
|
-
hasKey: keyUpdated ? !!params.key : p.hasKey
|
|
454
|
+
hasKey: keyUpdated ? !!params.key : p.hasKey,
|
|
455
|
+
codexmate_bridge: params.useTransform ? 'openai' : p.codexmate_bridge,
|
|
453
456
|
};
|
|
454
457
|
}
|
|
455
458
|
return p;
|
|
@@ -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
|
}
|