codexmate 0.0.53 → 0.0.55
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 +1 -0
- package/README.vi.md +170 -0
- package/cli/builtin-proxy.js +222 -11
- package/cli/openai-bridge.js +238 -14
- package/cli.js +553 -5
- package/package.json +1 -1
- package/web-ui/app.js +38 -0
- package/web-ui/logic.claude.mjs +8 -0
- package/web-ui/modules/app.methods.claude-config.mjs +72 -5
- package/web-ui/modules/app.methods.index.mjs +4 -0
- package/web-ui/modules/app.methods.navigation.mjs +3 -0
- package/web-ui/modules/app.methods.provider-cache.mjs +223 -0
- package/web-ui/modules/app.methods.session-actions.mjs +12 -0
- package/web-ui/modules/app.methods.session-browser.mjs +3 -0
- package/web-ui/modules/app.methods.session-trash.mjs +3 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +13 -3
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +161 -0
- package/web-ui/modules/i18n/locales/en.mjs +65 -1
- package/web-ui/modules/i18n/locales/ja.mjs +62 -1
- package/web-ui/modules/i18n/locales/vi.mjs +917 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +62 -1
- package/web-ui/modules/i18n/locales/zh.mjs +62 -1
- package/web-ui/partials/index/layout-header.html +18 -5
- package/web-ui/partials/index/modals-basic.html +182 -0
- package/web-ui/partials/index/panel-sessions.html +2 -2
- package/web-ui/partials/index/panel-settings.html +20 -0
- package/web-ui/res/web-ui-render.precompiled.js +397 -20
- package/web-ui/styles/layout-shell.css +51 -1
- package/web-ui/styles/settings-panel.css +398 -0
package/web-ui/app.js
CHANGED
|
@@ -79,6 +79,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
79
79
|
projectPathOptionsLoading: false,
|
|
80
80
|
showSkillsModal: false,
|
|
81
81
|
showHealthCheckModal: false,
|
|
82
|
+
showProviderCacheModal: false,
|
|
83
|
+
showProviderCacheAnnouncementModal: false,
|
|
82
84
|
showCodexBridgePoolModal: false,
|
|
83
85
|
showClaudeBridgePoolModal: false,
|
|
84
86
|
showWebhookModal: false,
|
|
@@ -378,6 +380,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
378
380
|
codexDownloadLoading: false,
|
|
379
381
|
codexDownloadProgress: 0,
|
|
380
382
|
codexDownloadTimer: null,
|
|
383
|
+
providerCacheRecords: { root: '', generatedAt: '', groups: [] },
|
|
384
|
+
providerCacheLoadedOnce: false,
|
|
385
|
+
providerCacheLoadedAt: '',
|
|
386
|
+
providerCacheLoading: false,
|
|
387
|
+
providerCacheSyncing: false,
|
|
388
|
+
providerCacheSyncMessage: '',
|
|
389
|
+
providerCacheError: '',
|
|
390
|
+
providerCacheRequestSeq: 0,
|
|
381
391
|
settingsTab: 'general',
|
|
382
392
|
toolConfigPermissions: (function() {
|
|
383
393
|
try {
|
|
@@ -506,6 +516,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
506
516
|
if (typeof this.loadWebhookSettings === 'function') {
|
|
507
517
|
this.loadWebhookSettings();
|
|
508
518
|
}
|
|
519
|
+
if (typeof this.loadWebUiPreferences === 'function') {
|
|
520
|
+
const applyPreferenceNavigation = (() => {
|
|
521
|
+
try {
|
|
522
|
+
const url = new URL(window.location.href);
|
|
523
|
+
if (url.pathname === '/session') return false;
|
|
524
|
+
return !String(url.searchParams.get('tab') || '').trim();
|
|
525
|
+
} catch (_) {
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
})();
|
|
529
|
+
void this.loadWebUiPreferences({ applyNavigation: applyPreferenceNavigation });
|
|
530
|
+
}
|
|
509
531
|
if (typeof this.t === 'function') {
|
|
510
532
|
this.confirmDialogConfirmText = this.t('confirm.ok');
|
|
511
533
|
this.confirmDialogCancelText = this.t('confirm.cancel');
|
|
@@ -693,8 +715,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
693
715
|
if (typeof this.loadAppVersionStatus === 'function') {
|
|
694
716
|
void this.loadAppVersionStatus({ silent: true });
|
|
695
717
|
}
|
|
718
|
+
if (typeof this.hydrateClaudeConfigsFromProviderCache === 'function') {
|
|
719
|
+
await this.hydrateClaudeConfigsFromProviderCache({ silent: true });
|
|
720
|
+
}
|
|
696
721
|
void this.refreshClaudeSelectionFromSettings({ silent: true });
|
|
697
722
|
void this.syncDefaultOpenclawConfigEntry({ silent: true });
|
|
723
|
+
if (typeof this.loadProviderCacheRecords === 'function') {
|
|
724
|
+
void this.loadProviderCacheRecords({ background: true });
|
|
725
|
+
}
|
|
698
726
|
};
|
|
699
727
|
if (typeof requestAnimationFrame === 'function') {
|
|
700
728
|
this._initialLoadRafId = requestAnimationFrame(() => {
|
|
@@ -743,6 +771,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
743
771
|
clearTimeout(this._initialLoadTimer);
|
|
744
772
|
this._initialLoadTimer = 0;
|
|
745
773
|
}
|
|
774
|
+
if (this.__webUiPreferencesPersistTimer) {
|
|
775
|
+
clearTimeout(this.__webUiPreferencesPersistTimer);
|
|
776
|
+
this.__webUiPreferencesPersistTimer = 0;
|
|
777
|
+
}
|
|
746
778
|
window.removeEventListener('resize', this.onWindowResize);
|
|
747
779
|
window.removeEventListener('keydown', this.handleGlobalKeydown);
|
|
748
780
|
window.removeEventListener('beforeunload', this.handleBeforeUnload);
|
|
@@ -767,6 +799,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
767
799
|
try {
|
|
768
800
|
localStorage.setItem('codexmate_prompts_sub_tab', newVal);
|
|
769
801
|
} catch (_) {}
|
|
802
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
803
|
+
this.persistWebUiPreferences({ promptsSubTab: newVal });
|
|
804
|
+
}
|
|
770
805
|
if (this.mainTab === 'prompts' && typeof this.loadPromptsContent === 'function') {
|
|
771
806
|
this.loadPromptsContent();
|
|
772
807
|
}
|
|
@@ -779,6 +814,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
779
814
|
localStorage.removeItem('codexmate_project_claude_md_path');
|
|
780
815
|
}
|
|
781
816
|
} catch (_) {}
|
|
817
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
818
|
+
this.persistWebUiPreferences({ projectClaudeMdPath: newPath || '' });
|
|
819
|
+
}
|
|
782
820
|
}
|
|
783
821
|
},
|
|
784
822
|
|
package/web-ui/logic.claude.mjs
CHANGED
|
@@ -191,6 +191,14 @@ export function matchClaudeConfigFromSettings(claudeConfigs = {}, env = {}) {
|
|
|
191
191
|
if (normalizedSettings.apiKey && normalizedConfig.apiKey === normalizedSettings.apiKey) {
|
|
192
192
|
return name;
|
|
193
193
|
}
|
|
194
|
+
if (normalizedSettings.apiKey
|
|
195
|
+
&& normalizedConfig.apiKey === ''
|
|
196
|
+
&& config
|
|
197
|
+
&& typeof config.providerCacheRef === 'string'
|
|
198
|
+
&& config.providerCacheRef.trim()
|
|
199
|
+
&& config.hasKey === true) {
|
|
200
|
+
return name;
|
|
201
|
+
}
|
|
194
202
|
if (!normalizedSettings.apiKey
|
|
195
203
|
&& normalizedConfig.apiKey === ''
|
|
196
204
|
&& normalizedConfig.externalCredentialType
|
|
@@ -20,6 +20,7 @@ function getClaudeConfigValidationForContext(vm, mode = 'add') {
|
|
|
20
20
|
const draft = mode === 'edit' ? vm.editingConfig : vm.newClaudeConfig;
|
|
21
21
|
const name = normalizeClaudeText(draft && draft.name);
|
|
22
22
|
const apiKey = normalizeClaudeText(draft && draft.apiKey);
|
|
23
|
+
const providerCacheRef = normalizeClaudeText(draft && draft.providerCacheRef);
|
|
23
24
|
const externalCredentialType = normalizeClaudeText(draft && draft.externalCredentialType);
|
|
24
25
|
const baseUrl = normalizeClaudeBaseUrl(draft && draft.baseUrl);
|
|
25
26
|
const model = normalizeClaudeText(draft && draft.model);
|
|
@@ -40,7 +41,7 @@ function getClaudeConfigValidationForContext(vm, mode = 'add') {
|
|
|
40
41
|
errors.name = vm.t('validation.claude.nameExists');
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
if (!apiKey && !externalCredentialType && targetApi !== 'ollama') {
|
|
44
|
+
if (!apiKey && !externalCredentialType && !providerCacheRef && targetApi !== 'ollama') {
|
|
44
45
|
errors.apiKey = vm.t('validation.claude.apiKeyRequired');
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -58,6 +59,7 @@ function getClaudeConfigValidationForContext(vm, mode = 'add') {
|
|
|
58
59
|
mode,
|
|
59
60
|
name,
|
|
60
61
|
apiKey,
|
|
62
|
+
providerCacheRef,
|
|
61
63
|
externalCredentialType,
|
|
62
64
|
baseUrl,
|
|
63
65
|
model,
|
|
@@ -112,6 +114,64 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
112
114
|
this.syncClaudeBridgeProviders();
|
|
113
115
|
},
|
|
114
116
|
|
|
117
|
+
async hydrateClaudeConfigsFromProviderCache(options = {}) {
|
|
118
|
+
const silent = options && options.silent === true;
|
|
119
|
+
try {
|
|
120
|
+
const res = await api('get-claude-provider-cache-configs');
|
|
121
|
+
if (!res || res.error) {
|
|
122
|
+
if (!silent) this.showMessage((res && res.error) || this.t('toast.claude.loadSettingsFail'), 'error');
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const providers = Array.isArray(res.providers) ? res.providers : [];
|
|
126
|
+
if (providers.length === 0) return true;
|
|
127
|
+
const configs = this.claudeConfigs && typeof this.claudeConfigs === 'object' ? this.claudeConfigs : {};
|
|
128
|
+
let changed = false;
|
|
129
|
+
let firstCachedName = '';
|
|
130
|
+
for (const provider of providers) {
|
|
131
|
+
if (!provider || typeof provider !== 'object') continue;
|
|
132
|
+
const name = normalizeClaudeText(provider.name);
|
|
133
|
+
const baseUrl = normalizeClaudeBaseUrl(provider.baseUrl);
|
|
134
|
+
const model = normalizeClaudeText(provider.model);
|
|
135
|
+
if (!name || !baseUrl || !model) continue;
|
|
136
|
+
if (!firstCachedName) firstCachedName = name;
|
|
137
|
+
const cachedConfig = {
|
|
138
|
+
apiKey: '',
|
|
139
|
+
baseUrl,
|
|
140
|
+
model,
|
|
141
|
+
hasKey: provider.hasKey === true,
|
|
142
|
+
providerCacheRef: normalizeClaudeText(provider.providerCacheRef) || name,
|
|
143
|
+
source: 'provider-cache',
|
|
144
|
+
targetApi: normalizeClaudeText(provider.targetApi) || 'responses'
|
|
145
|
+
};
|
|
146
|
+
const existing = configs[name];
|
|
147
|
+
if (existing && existing.source !== 'provider-cache' && existing.providerCacheRef !== cachedConfig.providerCacheRef) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (JSON.stringify(existing || {}) !== JSON.stringify(cachedConfig)) {
|
|
151
|
+
configs[name] = cachedConfig;
|
|
152
|
+
changed = true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
this.claudeConfigs = configs;
|
|
156
|
+
if (firstCachedName) {
|
|
157
|
+
let savedCurrent = '';
|
|
158
|
+
try { savedCurrent = localStorage.getItem('currentClaudeConfig') || ''; } catch (_) {}
|
|
159
|
+
const current = normalizeClaudeText(this.currentClaudeConfig);
|
|
160
|
+
const currentConfig = current && configs[current] ? configs[current] : null;
|
|
161
|
+
if (!savedCurrent && (!current || (currentConfig && currentConfig.hasKey === false && !currentConfig.providerCacheRef))) {
|
|
162
|
+
this.currentClaudeConfig = firstCachedName;
|
|
163
|
+
try { localStorage.setItem('currentClaudeConfig', firstCachedName); } catch (_) {}
|
|
164
|
+
changed = true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (changed) this.saveClaudeConfigs();
|
|
168
|
+
return true;
|
|
169
|
+
} catch (e) {
|
|
170
|
+
if (!silent) this.showMessage(e && e.message ? e.message : this.t('toast.claude.loadSettingsFail'), 'error');
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
|
|
115
175
|
async syncClaudeBridgeProviders() {
|
|
116
176
|
try { await api('claude-local-bridge-sync-providers', { providers: this.claudeConfigs || {} }); } catch (_) {}
|
|
117
177
|
},
|
|
@@ -154,6 +214,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
154
214
|
model: config.model || '',
|
|
155
215
|
targetApi: config.targetApi || 'responses'
|
|
156
216
|
};
|
|
217
|
+
if (config.providerCacheRef) this.editingConfig.providerCacheRef = config.providerCacheRef;
|
|
157
218
|
this.showEditClaudeConfigKey = false;
|
|
158
219
|
this.showEditConfigModal = true;
|
|
159
220
|
},
|
|
@@ -165,6 +226,8 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
165
226
|
}
|
|
166
227
|
const name = validation.name;
|
|
167
228
|
this.editingConfig.apiKey = validation.apiKey;
|
|
229
|
+
if (validation.providerCacheRef) this.editingConfig.providerCacheRef = validation.providerCacheRef;
|
|
230
|
+
else delete this.editingConfig.providerCacheRef;
|
|
168
231
|
this.editingConfig.externalCredentialType = validation.externalCredentialType;
|
|
169
232
|
this.editingConfig.baseUrl = validation.baseUrl;
|
|
170
233
|
this.editingConfig.model = validation.model;
|
|
@@ -195,6 +258,8 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
195
258
|
}
|
|
196
259
|
const name = validation.name;
|
|
197
260
|
this.editingConfig.apiKey = validation.apiKey;
|
|
261
|
+
if (validation.providerCacheRef) this.editingConfig.providerCacheRef = validation.providerCacheRef;
|
|
262
|
+
else delete this.editingConfig.providerCacheRef;
|
|
198
263
|
this.editingConfig.externalCredentialType = validation.externalCredentialType;
|
|
199
264
|
this.editingConfig.baseUrl = validation.baseUrl;
|
|
200
265
|
this.editingConfig.model = validation.model;
|
|
@@ -203,7 +268,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
203
268
|
this.saveClaudeConfigs();
|
|
204
269
|
|
|
205
270
|
const config = this.claudeConfigs[name];
|
|
206
|
-
if (!config.apiKey && config.targetApi !== 'ollama') {
|
|
271
|
+
if (!config.apiKey && !config.providerCacheRef && config.targetApi !== 'ollama') {
|
|
207
272
|
this.showMessage(this.t('toast.claude.savedWithoutKey'), 'info');
|
|
208
273
|
this.closeEditConfigModal();
|
|
209
274
|
if (name === this.currentClaudeConfig) {
|
|
@@ -212,7 +277,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
212
277
|
return;
|
|
213
278
|
}
|
|
214
279
|
|
|
215
|
-
const _claudeKey = `${name}|${config.apiKey || ""}|${config.baseUrl || ""}|${config.model || ""}|${config.targetApi || "responses"}`;
|
|
280
|
+
const _claudeKey = `${name}|${config.apiKey || ""}|${config.providerCacheRef || ""}|${config.baseUrl || ""}|${config.model || ""}|${config.targetApi || "responses"}`;
|
|
216
281
|
try {
|
|
217
282
|
const res = await api('apply-claude-config', { config: { ...config, name } });
|
|
218
283
|
if (res.error || res.success === false) {
|
|
@@ -238,6 +303,8 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
238
303
|
}
|
|
239
304
|
this.newClaudeConfig.name = validation.name;
|
|
240
305
|
this.newClaudeConfig.apiKey = validation.apiKey;
|
|
306
|
+
if (validation.providerCacheRef) this.newClaudeConfig.providerCacheRef = validation.providerCacheRef;
|
|
307
|
+
else delete this.newClaudeConfig.providerCacheRef;
|
|
241
308
|
this.newClaudeConfig.externalCredentialType = validation.externalCredentialType;
|
|
242
309
|
this.newClaudeConfig.baseUrl = validation.baseUrl;
|
|
243
310
|
this.newClaudeConfig.model = validation.model;
|
|
@@ -284,14 +351,14 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
284
351
|
this.refreshClaudeModelContext();
|
|
285
352
|
const config = this.claudeConfigs[name];
|
|
286
353
|
|
|
287
|
-
if (!config.apiKey && config.targetApi !== 'ollama') {
|
|
354
|
+
if (!config.apiKey && !config.providerCacheRef && config.targetApi !== 'ollama') {
|
|
288
355
|
if (config.externalCredentialType) {
|
|
289
356
|
return this.showMessage(this.t('toast.claude.externalAuth'), 'info');
|
|
290
357
|
}
|
|
291
358
|
return this.showMessage(this.t('toast.claude.apiKeyRequired'), 'error');
|
|
292
359
|
}
|
|
293
360
|
|
|
294
|
-
const _claudeKey2 = `${name}|${config.apiKey || ""}|${config.baseUrl || ""}|${config.model || ""}|${config.targetApi || "responses"}`;
|
|
361
|
+
const _claudeKey2 = `${name}|${config.apiKey || ""}|${config.providerCacheRef || ""}|${config.baseUrl || ""}|${config.model || ""}|${config.targetApi || "responses"}`;
|
|
295
362
|
try {
|
|
296
363
|
const res = await api('apply-claude-config', { config: { ...config, name } });
|
|
297
364
|
if (res.error || res.success === false) {
|
|
@@ -21,6 +21,7 @@ import { createOpenclawEditingMethods } from './app.methods.openclaw-editing.mjs
|
|
|
21
21
|
import { createOpenclawPersistMethods } from './app.methods.openclaw-persist.mjs';
|
|
22
22
|
import { createOpencodeConfigMethods } from './app.methods.opencode-config.mjs';
|
|
23
23
|
import { createProvidersMethods } from './app.methods.providers.mjs';
|
|
24
|
+
import { createProviderCacheMethods } from './app.methods.provider-cache.mjs';
|
|
24
25
|
import { createRuntimeMethods } from './app.methods.runtime.mjs';
|
|
25
26
|
import { createToolConfigPermissionMethods } from './app.methods.tool-config-permissions.mjs';
|
|
26
27
|
import { createTaskOrchestrationMethods } from './app.methods.task-orchestration.mjs';
|
|
@@ -33,6 +34,7 @@ import { createSkillsMethods } from './skills.methods.mjs';
|
|
|
33
34
|
import { createPluginsMethods } from './plugins.methods.mjs';
|
|
34
35
|
import { createI18nMethods } from './i18n.mjs';
|
|
35
36
|
import { createWebhookMethods } from './app.methods.webhook.mjs';
|
|
37
|
+
import { createWebUiPreferencesMethods } from './app.methods.web-ui-preferences.mjs';
|
|
36
38
|
import {
|
|
37
39
|
CONFIG_MODE_SET,
|
|
38
40
|
getProviderConfigModeMeta
|
|
@@ -83,6 +85,8 @@ export function createAppMethods() {
|
|
|
83
85
|
...createPluginsMethods(),
|
|
84
86
|
...createAgentsMethods({ api, apiWithMeta }),
|
|
85
87
|
...createProvidersMethods({ api }),
|
|
88
|
+
...createProviderCacheMethods({ api }),
|
|
89
|
+
...createWebUiPreferencesMethods({ api }),
|
|
86
90
|
...createClaudeConfigMethods({ api }),
|
|
87
91
|
...createToolConfigPermissionMethods({ api }),
|
|
88
92
|
...createOpenclawCoreMethods(),
|
|
@@ -108,6 +108,9 @@
|
|
|
108
108
|
try {
|
|
109
109
|
localStorage.setItem(NAV_STATE_STORAGE_KEY, JSON.stringify(snapshot));
|
|
110
110
|
} catch (_) {}
|
|
111
|
+
if (typeof vm.persistWebUiPreferences === 'function') {
|
|
112
|
+
vm.persistWebUiPreferences({ navigation: snapshot });
|
|
113
|
+
}
|
|
111
114
|
};
|
|
112
115
|
|
|
113
116
|
return {
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
export function createProviderCacheMethods(options = {}) {
|
|
2
|
+
const { api } = options;
|
|
3
|
+
|
|
4
|
+
return {
|
|
5
|
+
async openProviderCacheModal(options = {}) {
|
|
6
|
+
this.showProviderCacheModal = true;
|
|
7
|
+
if (options.forceRefresh === true || !this.providerCacheLoadedOnce) {
|
|
8
|
+
await this.loadProviderCacheRecords({ forceRefresh: options.forceRefresh === true });
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
closeProviderCacheModal() {
|
|
13
|
+
this.showProviderCacheModal = false;
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
async openProviderCacheAnnouncementModal() {
|
|
17
|
+
this.showProviderCacheAnnouncementModal = true;
|
|
18
|
+
if (!this.providerCacheLoadedOnce) {
|
|
19
|
+
await this.loadProviderCacheRecords();
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
closeProviderCacheAnnouncementModal() {
|
|
24
|
+
this.showProviderCacheAnnouncementModal = false;
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
async openProviderCacheDetailsFromAnnouncement() {
|
|
28
|
+
this.showProviderCacheAnnouncementModal = false;
|
|
29
|
+
await this.openProviderCacheModal({ forceRefresh: false });
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async loadProviderCacheRecords(options = {}) {
|
|
33
|
+
const forceRefresh = options && options.forceRefresh === true;
|
|
34
|
+
const background = options && options.background === true;
|
|
35
|
+
if (this.providerCacheLoading && !forceRefresh) return;
|
|
36
|
+
const requestSeq = (Number(this.providerCacheRequestSeq) || 0) + 1;
|
|
37
|
+
this.providerCacheRequestSeq = requestSeq;
|
|
38
|
+
const isLatestRequest = () => requestSeq === Number(this.providerCacheRequestSeq || 0);
|
|
39
|
+
if (!background) {
|
|
40
|
+
this.providerCacheLoading = true;
|
|
41
|
+
}
|
|
42
|
+
this.providerCacheError = '';
|
|
43
|
+
try {
|
|
44
|
+
const res = await api('get-provider-cache-records');
|
|
45
|
+
if (!isLatestRequest()) return;
|
|
46
|
+
if (res && res.error) {
|
|
47
|
+
this.providerCacheError = res.error;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this.providerCacheRecords = res && typeof res === 'object' ? res : { groups: [] };
|
|
51
|
+
this.providerCacheLoadedOnce = true;
|
|
52
|
+
this.providerCacheLoadedAt = this.providerCacheRecords.generatedAt || new Date().toISOString();
|
|
53
|
+
} catch (e) {
|
|
54
|
+
if (!isLatestRequest()) return;
|
|
55
|
+
this.providerCacheError = e && e.message ? e.message : this.t('modal.providerCache.loadFailed');
|
|
56
|
+
} finally {
|
|
57
|
+
if (isLatestRequest() && !background) {
|
|
58
|
+
this.providerCacheLoading = false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async syncProviderCacheRecords() {
|
|
64
|
+
if (this.providerCacheSyncing) return;
|
|
65
|
+
this.providerCacheSyncing = true;
|
|
66
|
+
this.providerCacheError = '';
|
|
67
|
+
this.providerCacheSyncMessage = '';
|
|
68
|
+
try {
|
|
69
|
+
const res = await api('sync-provider-cache-records');
|
|
70
|
+
if (res && res.error) {
|
|
71
|
+
this.providerCacheError = res.errorKey ? this.t(res.errorKey) : res.error;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const summary = res && res.summary && typeof res.summary === 'object' ? res.summary : {};
|
|
75
|
+
const providerCount = Number(summary.providerCount || 0);
|
|
76
|
+
const fileCount = Number(summary.fileCount || 0);
|
|
77
|
+
this.providerCacheSyncMessage = this.t('modal.providerCache.syncSucceeded', { count: providerCount, fileCount });
|
|
78
|
+
if (res && res.records && typeof res.records === 'object') {
|
|
79
|
+
this.providerCacheRecords = res.records;
|
|
80
|
+
this.providerCacheLoadedOnce = true;
|
|
81
|
+
this.providerCacheLoadedAt = this.providerCacheRecords.generatedAt || new Date().toISOString();
|
|
82
|
+
}
|
|
83
|
+
await this.loadProviderCacheRecords({ forceRefresh: true });
|
|
84
|
+
} catch (e) {
|
|
85
|
+
this.providerCacheError = e && e.message ? e.message : this.t('modal.providerCache.syncFailed');
|
|
86
|
+
} finally {
|
|
87
|
+
this.providerCacheSyncing = false;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
getProviderCacheGroups() {
|
|
92
|
+
const records = this.providerCacheRecords && typeof this.providerCacheRecords === 'object'
|
|
93
|
+
? this.providerCacheRecords
|
|
94
|
+
: {};
|
|
95
|
+
return Array.isArray(records.groups) ? records.groups : [];
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
getProviderCacheAnnouncementSummary() {
|
|
99
|
+
const groups = this.getProviderCacheGroups();
|
|
100
|
+
let fileCount = 0;
|
|
101
|
+
let providerCount = 0;
|
|
102
|
+
for (const group of groups) {
|
|
103
|
+
const files = this.getProviderCacheExistingFiles(group);
|
|
104
|
+
fileCount += files.length;
|
|
105
|
+
for (const file of files) {
|
|
106
|
+
const count = Number(file && file.providerCount);
|
|
107
|
+
if (Number.isFinite(count) && count > 0) {
|
|
108
|
+
providerCount += count;
|
|
109
|
+
} else {
|
|
110
|
+
providerCount += this.getProviderCacheFileProviders(file).length;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
groupCount: groups.length,
|
|
116
|
+
fileCount,
|
|
117
|
+
providerCount,
|
|
118
|
+
loadedAt: this.providerCacheLoadedAt || (this.providerCacheRecords && this.providerCacheRecords.generatedAt) || ''
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
getProviderCacheAnnouncementGroups() {
|
|
123
|
+
return this.getProviderCacheGroups().map((group) => {
|
|
124
|
+
const existingFiles = this.getProviderCacheExistingFiles(group);
|
|
125
|
+
return {
|
|
126
|
+
key: group && group.key ? group.key : '',
|
|
127
|
+
label: group && group.label ? group.label : '',
|
|
128
|
+
existingCount: existingFiles.length,
|
|
129
|
+
providerCount: existingFiles.reduce((sum, file) => {
|
|
130
|
+
const count = Number(file && file.providerCount);
|
|
131
|
+
if (Number.isFinite(count) && count > 0) return sum + count;
|
|
132
|
+
return sum + this.getProviderCacheFileProviders(file).length;
|
|
133
|
+
}, 0)
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
getProviderCacheExistingFiles(group) {
|
|
139
|
+
const files = group && Array.isArray(group.files) ? group.files : [];
|
|
140
|
+
return files.filter((file) => file && file.exists);
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
hasProviderCacheExistingFiles(group) {
|
|
144
|
+
return this.getProviderCacheExistingFiles(group).length > 0;
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
getProviderCacheFileKey(file) {
|
|
148
|
+
if (!file) return '';
|
|
149
|
+
return file.displayPath || file.path || file.name || '';
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
getProviderCacheFilePath(file) {
|
|
153
|
+
if (!file) return '';
|
|
154
|
+
return file.displayPath || file.path || file.name || '';
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
getProviderCacheFileSummary(file) {
|
|
158
|
+
if (!file || !file.exists) return '';
|
|
159
|
+
const count = Number(file.providerCount || 0);
|
|
160
|
+
if (count > 0) return this.t('modal.providerCache.providerCount', { count });
|
|
161
|
+
if (file.tooLarge) return this.t('modal.providerCache.tooLarge');
|
|
162
|
+
if (file.ok === false) return this.t('modal.providerCache.parseFailed');
|
|
163
|
+
return this.t('modal.providerCache.rawJsonOnly');
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
getProviderCacheFileProviders(file) {
|
|
167
|
+
const providers = file && Array.isArray(file.providers) ? file.providers : [];
|
|
168
|
+
return providers.filter((provider) => provider && typeof provider === 'object');
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
hasProviderCacheProviders(file) {
|
|
172
|
+
return this.getProviderCacheFileProviders(file).length > 0;
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
getProviderCacheProviderMeta(provider) {
|
|
176
|
+
if (!provider || typeof provider !== 'object') return [];
|
|
177
|
+
const fields = [
|
|
178
|
+
['baseUrl', 'base_url'],
|
|
179
|
+
['wireApi', 'wire_api'],
|
|
180
|
+
['authMethod', 'auth'],
|
|
181
|
+
['model', 'model']
|
|
182
|
+
];
|
|
183
|
+
return fields
|
|
184
|
+
.map(([key, label]) => {
|
|
185
|
+
const value = provider[key];
|
|
186
|
+
if (value === undefined || value === null || value === '') return null;
|
|
187
|
+
return { label, value: String(value) };
|
|
188
|
+
})
|
|
189
|
+
.filter(Boolean);
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
getProviderCacheProviderText(provider) {
|
|
193
|
+
if (!provider || typeof provider !== 'object') return '';
|
|
194
|
+
try {
|
|
195
|
+
return JSON.stringify(provider.data === undefined ? provider : provider.data, null, 2);
|
|
196
|
+
} catch (_) {
|
|
197
|
+
return String(provider.name || '');
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
getProviderCacheRecordText(record) {
|
|
202
|
+
if (!record || !record.exists) return '';
|
|
203
|
+
if (record.ok === false) {
|
|
204
|
+
return record.error || '';
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
return JSON.stringify(record.data === undefined ? null : record.data, null, 2);
|
|
208
|
+
} catch (_) {
|
|
209
|
+
return String(record.data || '');
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
formatProviderCacheFileSize(size) {
|
|
214
|
+
const bytes = Number(size);
|
|
215
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';
|
|
216
|
+
if (bytes < 1024) return `${Math.floor(bytes)} B`;
|
|
217
|
+
const kib = bytes / 1024;
|
|
218
|
+
if (kib < 1024) return `${kib.toFixed(kib >= 10 ? 0 : 1)} KiB`;
|
|
219
|
+
const mib = kib / 1024;
|
|
220
|
+
return `${mib.toFixed(mib >= 10 ? 1 : 2)} MiB`;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
@@ -281,6 +281,9 @@ export function createSessionActionMethods(options = {}) {
|
|
|
281
281
|
try {
|
|
282
282
|
localStorage.setItem('codexmateSessionTrashEnabled', enabled ? 'true' : 'false');
|
|
283
283
|
} catch (_) {}
|
|
284
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
285
|
+
this.persistWebUiPreferences({ sessionTrashEnabled: enabled });
|
|
286
|
+
}
|
|
284
287
|
},
|
|
285
288
|
|
|
286
289
|
setSessionTimelineStyle(style) {
|
|
@@ -289,12 +292,18 @@ export function createSessionActionMethods(options = {}) {
|
|
|
289
292
|
try {
|
|
290
293
|
localStorage.setItem('codexmateSessionTimelineStyle', normalized);
|
|
291
294
|
} catch (_) {}
|
|
295
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
296
|
+
this.persistWebUiPreferences({ sessionTimelineStyle: normalized });
|
|
297
|
+
}
|
|
292
298
|
},
|
|
293
299
|
|
|
294
300
|
setConfigTemplateDiffConfirmEnabled(value) {
|
|
295
301
|
const enabled = this.normalizeConfigTemplateDiffConfirmEnabled(value);
|
|
296
302
|
this.configTemplateDiffConfirmEnabled = enabled;
|
|
297
303
|
persistConfigTemplateDiffConfirmEnabledToStorage(enabled);
|
|
304
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
305
|
+
this.persistWebUiPreferences({ configTemplateDiffConfirmEnabled: enabled });
|
|
306
|
+
}
|
|
298
307
|
},
|
|
299
308
|
|
|
300
309
|
getShareCommandPrefixInvocation() {
|
|
@@ -308,6 +317,9 @@ export function createSessionActionMethods(options = {}) {
|
|
|
308
317
|
try {
|
|
309
318
|
localStorage.setItem('codexmateShareCommandPrefix', normalized);
|
|
310
319
|
} catch (_) {}
|
|
320
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
321
|
+
this.persistWebUiPreferences({ shareCommandPrefix: normalized });
|
|
322
|
+
}
|
|
311
323
|
},
|
|
312
324
|
|
|
313
325
|
fallbackCopyText(text) {
|
|
@@ -803,6 +803,9 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
803
803
|
const range = normalized === 'all' ? 'all' : (normalized === '30d' ? '30d' : '7d');
|
|
804
804
|
this.sessionsUsageTimeRange = range;
|
|
805
805
|
try { localStorage.setItem('sessionsUsageTimeRange', range); } catch (_) {}
|
|
806
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
807
|
+
this.persistWebUiPreferences({ sessionsUsageTimeRange: range });
|
|
808
|
+
}
|
|
806
809
|
if (range === 'all') {
|
|
807
810
|
this.sessionsUsageCompareEnabled = false;
|
|
808
811
|
}
|
|
@@ -311,6 +311,9 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
311
311
|
const normalized = this.normalizeSessionTrashRetentionDays(days);
|
|
312
312
|
this.sessionTrashRetentionDays = normalized;
|
|
313
313
|
try { localStorage.setItem('codexmateSessionTrashRetentionDays', String(normalized)); } catch (_) {}
|
|
314
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
315
|
+
this.persistWebUiPreferences({ sessionTrashRetentionDays: normalized });
|
|
316
|
+
}
|
|
314
317
|
},
|
|
315
318
|
|
|
316
319
|
getSessionTrashActionKey(item) {
|
|
@@ -260,17 +260,23 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
260
260
|
mergeClaudeConfig(existing = {}, updates = {}) {
|
|
261
261
|
const previous = this.normalizeClaudeConfig(existing);
|
|
262
262
|
const next = this.normalizeClaudeConfig({ ...existing, ...updates });
|
|
263
|
+
const raw = { ...existing, ...updates };
|
|
264
|
+
const providerCacheRef = typeof raw.providerCacheRef === 'string' ? raw.providerCacheRef.trim() : '';
|
|
265
|
+
const source = raw.source === 'provider-cache' ? 'provider-cache' : (existing.source === 'provider-cache' && providerCacheRef ? 'provider-cache' : '');
|
|
263
266
|
const externalCredentialType = next.apiKey
|
|
264
267
|
? ''
|
|
265
268
|
: (next.externalCredentialType || previous.externalCredentialType || '');
|
|
266
|
-
|
|
269
|
+
const merged = {
|
|
267
270
|
apiKey: next.apiKey,
|
|
268
271
|
baseUrl: next.baseUrl,
|
|
269
272
|
model: next.model || previous.model || 'glm-4.7',
|
|
270
|
-
hasKey: !!(next.apiKey || externalCredentialType),
|
|
273
|
+
hasKey: !!(next.apiKey || externalCredentialType || providerCacheRef || raw.hasKey === true),
|
|
271
274
|
externalCredentialType,
|
|
272
275
|
targetApi: next.targetApi || previous.targetApi || 'responses'
|
|
273
276
|
};
|
|
277
|
+
if (providerCacheRef) merged.providerCacheRef = providerCacheRef;
|
|
278
|
+
if (source) merged.source = source;
|
|
279
|
+
return merged;
|
|
274
280
|
},
|
|
275
281
|
|
|
276
282
|
buildClaudeImportedConfigName(baseUrl) {
|
|
@@ -459,6 +465,9 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
459
465
|
const currentConfigName = typeof this.currentClaudeConfig === 'string' ? this.currentClaudeConfig.trim() : '';
|
|
460
466
|
const baseUrl = (config.baseUrl || '').trim();
|
|
461
467
|
const apiKey = (config.apiKey || '').trim();
|
|
468
|
+
const providerCacheRef = typeof config.providerCacheRef === 'string'
|
|
469
|
+
? config.providerCacheRef.trim()
|
|
470
|
+
: '';
|
|
462
471
|
const externalCredentialType = typeof config.externalCredentialType === 'string'
|
|
463
472
|
? config.externalCredentialType.trim()
|
|
464
473
|
: '';
|
|
@@ -468,7 +477,7 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
468
477
|
return;
|
|
469
478
|
}
|
|
470
479
|
const localCatalog = getClaudeModelCatalogForBaseUrl(baseUrl);
|
|
471
|
-
if (!apiKey && externalCredentialType) {
|
|
480
|
+
if (!apiKey && (externalCredentialType || providerCacheRef)) {
|
|
472
481
|
this.claudeModels = localCatalog;
|
|
473
482
|
this.claudeModelsSource = localCatalog.length ? 'catalog' : 'unlimited';
|
|
474
483
|
if (localCatalog.length) {
|
|
@@ -520,6 +529,7 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
520
529
|
}
|
|
521
530
|
return (latestConfig.baseUrl || '').trim() === baseUrl
|
|
522
531
|
&& (latestConfig.apiKey || '').trim() === apiKey
|
|
532
|
+
&& (typeof latestConfig.providerCacheRef === 'string' ? latestConfig.providerCacheRef.trim() : '') === providerCacheRef
|
|
523
533
|
&& (typeof latestConfig.externalCredentialType === 'string' ? latestConfig.externalCredentialType.trim() : '') === externalCredentialType;
|
|
524
534
|
};
|
|
525
535
|
if (cachedOk) {
|