codexmate 0.0.54 → 0.0.56
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 +3 -0
- package/README.vi.md +172 -0
- package/README.zh.md +2 -0
- package/cli/local-bridge.js +221 -22
- package/cli.js +666 -6
- package/package.json +1 -1
- package/web-ui/app.js +40 -0
- package/web-ui/logic.claude.mjs +8 -0
- package/web-ui/modules/app.computed.session.mjs +210 -0
- package/web-ui/modules/app.methods.claude-config.mjs +196 -13
- package/web-ui/modules/app.methods.codex-config.mjs +294 -65
- package/web-ui/modules/app.methods.index.mjs +4 -0
- package/web-ui/modules/app.methods.navigation.mjs +7 -1
- package/web-ui/modules/app.methods.provider-cache.mjs +223 -0
- package/web-ui/modules/app.methods.providers.mjs +15 -1
- package/web-ui/modules/app.methods.runtime.mjs +7 -2
- package/web-ui/modules/app.methods.session-actions.mjs +36 -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 +47 -4
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +161 -0
- package/web-ui/modules/i18n/locales/en.mjs +106 -3
- package/web-ui/modules/i18n/locales/ja.mjs +103 -3
- package/web-ui/modules/i18n/locales/vi.mjs +950 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +103 -3
- package/web-ui/modules/i18n/locales/zh.mjs +103 -3
- package/web-ui/modules/provider-default-names.mjs +25 -0
- package/web-ui/partials/index/layout-header.html +18 -5
- package/web-ui/partials/index/modal-health-check.html +69 -5
- package/web-ui/partials/index/modals-basic.html +182 -0
- package/web-ui/partials/index/panel-config-codex.html +2 -2
- package/web-ui/partials/index/panel-sessions.html +99 -19
- package/web-ui/partials/index/panel-settings.html +20 -0
- package/web-ui/res/web-ui-render.precompiled.js +742 -113
- package/web-ui/session-helpers.mjs +4 -1
- package/web-ui/styles/layout-shell.css +51 -1
- package/web-ui/styles/responsive.css +98 -0
- package/web-ui/styles/sessions-preview.css +212 -2
- package/web-ui/styles/sessions-toolbar-trash.css +61 -18
- package/web-ui/styles/settings-panel.css +398 -0
- package/web-ui/styles/skills-list.css +122 -0
- package/web-ui/styles/titles-cards.css +52 -0
|
@@ -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
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { nextCodexProviderName } from './provider-default-names.mjs';
|
|
2
|
+
|
|
1
3
|
const PROVIDER_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
2
4
|
const RESERVED_PROXY_PROVIDER_NAME = 'codexmate-proxy';
|
|
3
5
|
const RESERVED_LOCAL_PROVIDER_NAME = 'local';
|
|
@@ -327,6 +329,18 @@ export function createProvidersMethods(options = {}) {
|
|
|
327
329
|
this.showAddModal = true;
|
|
328
330
|
},
|
|
329
331
|
|
|
332
|
+
openAddProviderModal() {
|
|
333
|
+
this.newProvider = {
|
|
334
|
+
name: nextCodexProviderName(this.providersList),
|
|
335
|
+
url: '',
|
|
336
|
+
key: '',
|
|
337
|
+
model: '',
|
|
338
|
+
useTransform: false
|
|
339
|
+
};
|
|
340
|
+
this.showAddProviderKey = false;
|
|
341
|
+
this.showAddModal = true;
|
|
342
|
+
},
|
|
343
|
+
|
|
330
344
|
async openEditModal(provider) {
|
|
331
345
|
const requestId = Symbol('openEditModal');
|
|
332
346
|
this._openEditModalRequestId = requestId;
|
|
@@ -536,7 +550,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
536
550
|
closeAddModal() {
|
|
537
551
|
this.showAddModal = false;
|
|
538
552
|
this.showAddProviderKey = false;
|
|
539
|
-
this.newProvider = { name:
|
|
553
|
+
this.newProvider = { name: nextCodexProviderName(this.providersList), url: '', key: '', model: '', useTransform: false };
|
|
540
554
|
},
|
|
541
555
|
|
|
542
556
|
toggleAddProviderKey() {
|
|
@@ -154,6 +154,10 @@ export function createRuntimeMethods(options = {}) {
|
|
|
154
154
|
const baseUrl = config && typeof config.baseUrl === 'string' ? config.baseUrl.trim() : '';
|
|
155
155
|
const apiKey = config && typeof config.apiKey === 'string' ? config.apiKey.trim() : '';
|
|
156
156
|
const model = config && typeof config.model === 'string' ? config.model.trim() : '';
|
|
157
|
+
const targetApiRaw = config && typeof config.targetApi === 'string' ? config.targetApi.trim().toLowerCase() : '';
|
|
158
|
+
const targetApi = targetApiRaw === 'ollama'
|
|
159
|
+
? 'ollama'
|
|
160
|
+
: (targetApiRaw === 'chat_completions' || targetApiRaw === 'chat-completions' || targetApiRaw === 'chat/completions' ? 'chat_completions' : 'responses');
|
|
157
161
|
this.claudeSpeedLoading[name] = true;
|
|
158
162
|
try {
|
|
159
163
|
if (!baseUrl) {
|
|
@@ -161,7 +165,7 @@ export function createRuntimeMethods(options = {}) {
|
|
|
161
165
|
this.claudeSpeedResults[name] = res;
|
|
162
166
|
return res;
|
|
163
167
|
}
|
|
164
|
-
if (!apiKey) {
|
|
168
|
+
if (!apiKey && targetApi !== 'ollama') {
|
|
165
169
|
const res = { ok: false, error: 'Missing API key' };
|
|
166
170
|
this.claudeSpeedResults[name] = res;
|
|
167
171
|
return res;
|
|
@@ -175,7 +179,8 @@ export function createRuntimeMethods(options = {}) {
|
|
|
175
179
|
kind: 'claude',
|
|
176
180
|
url: baseUrl,
|
|
177
181
|
apiKey,
|
|
178
|
-
model
|
|
182
|
+
model,
|
|
183
|
+
targetApi
|
|
179
184
|
});
|
|
180
185
|
if (res.error) {
|
|
181
186
|
this.claudeSpeedResults[name] = { ok: false, error: res.error };
|
|
@@ -165,6 +165,30 @@ export function createSessionActionMethods(options = {}) {
|
|
|
165
165
|
this.showMessage(this.t('toast.copy.fail'), 'error');
|
|
166
166
|
},
|
|
167
167
|
|
|
168
|
+
async copySessionWorkspaceBrief() {
|
|
169
|
+
const summary = this.activeSessionWorkspaceSummary;
|
|
170
|
+
const text = summary && typeof summary.briefText === 'string'
|
|
171
|
+
? summary.briefText.trim()
|
|
172
|
+
: '';
|
|
173
|
+
if (!text) {
|
|
174
|
+
this.showMessage(this.t('sessions.workspace.copy.empty'), 'error');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const ok = this.fallbackCopyText(text);
|
|
178
|
+
if (ok) {
|
|
179
|
+
this.showMessage(this.t('sessions.workspace.copy.success'), 'success');
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
184
|
+
await navigator.clipboard.writeText(text);
|
|
185
|
+
this.showMessage(this.t('sessions.workspace.copy.success'), 'success');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
} catch (_) {}
|
|
189
|
+
this.showMessage(this.t('toast.copy.fail'), 'error');
|
|
190
|
+
},
|
|
191
|
+
|
|
168
192
|
getSessionExportKey(session) {
|
|
169
193
|
return `${session.source || 'unknown'}:${session.sessionId || ''}:${session.filePath || ''}`;
|
|
170
194
|
},
|
|
@@ -281,6 +305,9 @@ export function createSessionActionMethods(options = {}) {
|
|
|
281
305
|
try {
|
|
282
306
|
localStorage.setItem('codexmateSessionTrashEnabled', enabled ? 'true' : 'false');
|
|
283
307
|
} catch (_) {}
|
|
308
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
309
|
+
this.persistWebUiPreferences({ sessionTrashEnabled: enabled });
|
|
310
|
+
}
|
|
284
311
|
},
|
|
285
312
|
|
|
286
313
|
setSessionTimelineStyle(style) {
|
|
@@ -289,12 +316,18 @@ export function createSessionActionMethods(options = {}) {
|
|
|
289
316
|
try {
|
|
290
317
|
localStorage.setItem('codexmateSessionTimelineStyle', normalized);
|
|
291
318
|
} catch (_) {}
|
|
319
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
320
|
+
this.persistWebUiPreferences({ sessionTimelineStyle: normalized });
|
|
321
|
+
}
|
|
292
322
|
},
|
|
293
323
|
|
|
294
324
|
setConfigTemplateDiffConfirmEnabled(value) {
|
|
295
325
|
const enabled = this.normalizeConfigTemplateDiffConfirmEnabled(value);
|
|
296
326
|
this.configTemplateDiffConfirmEnabled = enabled;
|
|
297
327
|
persistConfigTemplateDiffConfirmEnabledToStorage(enabled);
|
|
328
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
329
|
+
this.persistWebUiPreferences({ configTemplateDiffConfirmEnabled: enabled });
|
|
330
|
+
}
|
|
298
331
|
},
|
|
299
332
|
|
|
300
333
|
getShareCommandPrefixInvocation() {
|
|
@@ -308,6 +341,9 @@ export function createSessionActionMethods(options = {}) {
|
|
|
308
341
|
try {
|
|
309
342
|
localStorage.setItem('codexmateShareCommandPrefix', normalized);
|
|
310
343
|
} catch (_) {}
|
|
344
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
345
|
+
this.persistWebUiPreferences({ shareCommandPrefix: normalized });
|
|
346
|
+
}
|
|
311
347
|
},
|
|
312
348
|
|
|
313
349
|
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) {
|
|
@@ -8,6 +8,31 @@ import {
|
|
|
8
8
|
normalizeClaudeSettingsEnv,
|
|
9
9
|
normalizeClaudeValue
|
|
10
10
|
} from '../logic.mjs';
|
|
11
|
+
import { nextClaudeConfigName } from './provider-default-names.mjs';
|
|
12
|
+
|
|
13
|
+
const DELETED_CLAUDE_SETTINGS_IMPORTS_STORAGE_KEY = 'deletedClaudeSettingsImports';
|
|
14
|
+
|
|
15
|
+
function normalizeDeletedClaudeImportUrl(value) {
|
|
16
|
+
return typeof value === 'string' ? value.trim().replace(/\/+$/g, '') : '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function shouldSuppressDeletedClaudeSettingsImport(env = {}) {
|
|
20
|
+
const normalized = normalizeClaudeSettingsEnv(env);
|
|
21
|
+
const baseUrl = normalizeDeletedClaudeImportUrl(normalized.baseUrl);
|
|
22
|
+
const model = normalizeClaudeValue(normalized.model);
|
|
23
|
+
if (!baseUrl || !model) return false;
|
|
24
|
+
try {
|
|
25
|
+
const raw = localStorage.getItem(DELETED_CLAUDE_SETTINGS_IMPORTS_STORAGE_KEY);
|
|
26
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
27
|
+
const entries = Array.isArray(parsed) ? parsed : [];
|
|
28
|
+
return entries.some((entry) => entry
|
|
29
|
+
&& typeof entry === 'object'
|
|
30
|
+
&& normalizeDeletedClaudeImportUrl(entry.baseUrl) === baseUrl
|
|
31
|
+
&& normalizeClaudeValue(entry.model) === model);
|
|
32
|
+
} catch (_) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
11
36
|
|
|
12
37
|
export function createStartupClaudeMethods(options = {}) {
|
|
13
38
|
const {
|
|
@@ -250,7 +275,7 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
250
275
|
},
|
|
251
276
|
|
|
252
277
|
shouldSuppressClaudeSettingsImport(env) {
|
|
253
|
-
return isLikelyBuiltinClaudeProxySettingsEnv(env);
|
|
278
|
+
return isLikelyBuiltinClaudeProxySettingsEnv(env) || shouldSuppressDeletedClaudeSettingsImport(env);
|
|
254
279
|
},
|
|
255
280
|
|
|
256
281
|
findDuplicateClaudeConfigName(config) {
|
|
@@ -260,17 +285,23 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
260
285
|
mergeClaudeConfig(existing = {}, updates = {}) {
|
|
261
286
|
const previous = this.normalizeClaudeConfig(existing);
|
|
262
287
|
const next = this.normalizeClaudeConfig({ ...existing, ...updates });
|
|
288
|
+
const raw = { ...existing, ...updates };
|
|
289
|
+
const providerCacheRef = typeof raw.providerCacheRef === 'string' ? raw.providerCacheRef.trim() : '';
|
|
290
|
+
const source = raw.source === 'provider-cache' ? 'provider-cache' : (existing.source === 'provider-cache' && providerCacheRef ? 'provider-cache' : '');
|
|
263
291
|
const externalCredentialType = next.apiKey
|
|
264
292
|
? ''
|
|
265
293
|
: (next.externalCredentialType || previous.externalCredentialType || '');
|
|
266
|
-
|
|
294
|
+
const merged = {
|
|
267
295
|
apiKey: next.apiKey,
|
|
268
296
|
baseUrl: next.baseUrl,
|
|
269
297
|
model: next.model || previous.model || 'glm-4.7',
|
|
270
|
-
hasKey: !!(next.apiKey || externalCredentialType),
|
|
298
|
+
hasKey: !!(next.apiKey || externalCredentialType || providerCacheRef || raw.hasKey === true),
|
|
271
299
|
externalCredentialType,
|
|
272
300
|
targetApi: next.targetApi || previous.targetApi || 'responses'
|
|
273
301
|
};
|
|
302
|
+
if (providerCacheRef) merged.providerCacheRef = providerCacheRef;
|
|
303
|
+
if (source) merged.source = source;
|
|
304
|
+
return merged;
|
|
274
305
|
},
|
|
275
306
|
|
|
276
307
|
buildClaudeImportedConfigName(baseUrl) {
|
|
@@ -459,6 +490,9 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
459
490
|
const currentConfigName = typeof this.currentClaudeConfig === 'string' ? this.currentClaudeConfig.trim() : '';
|
|
460
491
|
const baseUrl = (config.baseUrl || '').trim();
|
|
461
492
|
const apiKey = (config.apiKey || '').trim();
|
|
493
|
+
const providerCacheRef = typeof config.providerCacheRef === 'string'
|
|
494
|
+
? config.providerCacheRef.trim()
|
|
495
|
+
: '';
|
|
462
496
|
const externalCredentialType = typeof config.externalCredentialType === 'string'
|
|
463
497
|
? config.externalCredentialType.trim()
|
|
464
498
|
: '';
|
|
@@ -468,7 +502,7 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
468
502
|
return;
|
|
469
503
|
}
|
|
470
504
|
const localCatalog = getClaudeModelCatalogForBaseUrl(baseUrl);
|
|
471
|
-
if (!apiKey && externalCredentialType) {
|
|
505
|
+
if (!apiKey && (externalCredentialType || providerCacheRef)) {
|
|
472
506
|
this.claudeModels = localCatalog;
|
|
473
507
|
this.claudeModelsSource = localCatalog.length ? 'catalog' : 'unlimited';
|
|
474
508
|
if (localCatalog.length) {
|
|
@@ -520,6 +554,7 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
520
554
|
}
|
|
521
555
|
return (latestConfig.baseUrl || '').trim() === baseUrl
|
|
522
556
|
&& (latestConfig.apiKey || '').trim() === apiKey
|
|
557
|
+
&& (typeof latestConfig.providerCacheRef === 'string' ? latestConfig.providerCacheRef.trim() : '') === providerCacheRef
|
|
523
558
|
&& (typeof latestConfig.externalCredentialType === 'string' ? latestConfig.externalCredentialType.trim() : '') === externalCredentialType;
|
|
524
559
|
};
|
|
525
560
|
if (cachedOk) {
|
|
@@ -563,6 +598,14 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
563
598
|
},
|
|
564
599
|
|
|
565
600
|
openClaudeConfigModal() {
|
|
601
|
+
this.newClaudeConfig = {
|
|
602
|
+
name: nextClaudeConfigName(this.claudeConfigs),
|
|
603
|
+
apiKey: '',
|
|
604
|
+
externalCredentialType: '',
|
|
605
|
+
baseUrl: '',
|
|
606
|
+
model: '',
|
|
607
|
+
targetApi: 'responses'
|
|
608
|
+
};
|
|
566
609
|
this.showAddClaudeConfigKey = false;
|
|
567
610
|
this.showClaudeConfigModal = true;
|
|
568
611
|
},
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export function createWebUiPreferencesMethods(options = {}) {
|
|
2
|
+
const api = typeof options.api === 'function' ? options.api : async () => ({});
|
|
3
|
+
const storageOverride = options.storage && typeof options.storage === 'object' ? options.storage : null;
|
|
4
|
+
|
|
5
|
+
const getLocalStorage = () => {
|
|
6
|
+
const storage = storageOverride || (typeof globalThis !== 'undefined' ? globalThis.localStorage : null);
|
|
7
|
+
return storage && typeof storage.setItem === 'function' && typeof storage.removeItem === 'function'
|
|
8
|
+
? storage
|
|
9
|
+
: null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const setLocalStorageValue = (key, value) => {
|
|
13
|
+
const storage = getLocalStorage();
|
|
14
|
+
if (!storage) return;
|
|
15
|
+
try {
|
|
16
|
+
if (value === null || value === undefined || value === '') {
|
|
17
|
+
storage.removeItem(key);
|
|
18
|
+
} else {
|
|
19
|
+
storage.setItem(key, String(value));
|
|
20
|
+
}
|
|
21
|
+
} catch (_) {}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const normalizeUsageTimeRange = (value) => {
|
|
25
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
26
|
+
if (normalized === 'all' || normalized === '30d') return normalized;
|
|
27
|
+
return '7d';
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const normalizePromptsSubTab = (value) => {
|
|
31
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
32
|
+
return normalized === 'claude-project' ? 'claude-project' : 'codex';
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const normalizeNavigationSnapshot = (vm, source = {}) => {
|
|
36
|
+
const currentSkillsTargetApp = vm.skillsTargetApp === 'claude' ? 'claude' : 'codex';
|
|
37
|
+
const currentPromptTemplatesMode = vm.promptTemplatesMode === 'manage' ? 'manage' : 'compose';
|
|
38
|
+
return {
|
|
39
|
+
mainTab: typeof source.mainTab === 'string' ? source.mainTab : vm.mainTab,
|
|
40
|
+
configMode: typeof source.configMode === 'string' ? source.configMode : vm.configMode,
|
|
41
|
+
settingsTab: typeof source.settingsTab === 'string' ? source.settingsTab : vm.settingsTab,
|
|
42
|
+
skillsTargetApp: source.skillsTargetApp === 'claude' || source.skillsTargetApp === 'codex'
|
|
43
|
+
? source.skillsTargetApp
|
|
44
|
+
: currentSkillsTargetApp,
|
|
45
|
+
promptTemplatesMode: source.promptTemplatesMode === 'manage' || source.promptTemplatesMode === 'compose'
|
|
46
|
+
? source.promptTemplatesMode
|
|
47
|
+
: currentPromptTemplatesMode
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
buildWebUiPreferencesSnapshot(overrides = {}) {
|
|
53
|
+
const navigationOverride = overrides && typeof overrides.navigation === 'object' && overrides.navigation
|
|
54
|
+
? overrides.navigation
|
|
55
|
+
: null;
|
|
56
|
+
return {
|
|
57
|
+
shareCommandPrefix: typeof this.normalizeShareCommandPrefix === 'function'
|
|
58
|
+
? this.normalizeShareCommandPrefix(overrides.shareCommandPrefix || this.shareCommandPrefix)
|
|
59
|
+
: (this.shareCommandPrefix || 'npm start'),
|
|
60
|
+
sessionTrashEnabled: typeof this.normalizeSessionTrashEnabled === 'function'
|
|
61
|
+
? this.normalizeSessionTrashEnabled(Object.prototype.hasOwnProperty.call(overrides, 'sessionTrashEnabled') ? overrides.sessionTrashEnabled : this.sessionTrashEnabled)
|
|
62
|
+
: this.sessionTrashEnabled !== false,
|
|
63
|
+
sessionTrashRetentionDays: typeof this.normalizeSessionTrashRetentionDays === 'function'
|
|
64
|
+
? this.normalizeSessionTrashRetentionDays(Object.prototype.hasOwnProperty.call(overrides, 'sessionTrashRetentionDays') ? overrides.sessionTrashRetentionDays : this.sessionTrashRetentionDays)
|
|
65
|
+
: 30,
|
|
66
|
+
sessionTimelineStyle: typeof this.normalizeSessionTimelineStyle === 'function'
|
|
67
|
+
? this.normalizeSessionTimelineStyle(overrides.sessionTimelineStyle || this.sessionTimelineStyle)
|
|
68
|
+
: (this.sessionTimelineStyle === 'bar' ? 'bar' : 'dots'),
|
|
69
|
+
configTemplateDiffConfirmEnabled: typeof this.normalizeConfigTemplateDiffConfirmEnabled === 'function'
|
|
70
|
+
? this.normalizeConfigTemplateDiffConfirmEnabled(Object.prototype.hasOwnProperty.call(overrides, 'configTemplateDiffConfirmEnabled') ? overrides.configTemplateDiffConfirmEnabled : this.configTemplateDiffConfirmEnabled)
|
|
71
|
+
: this.configTemplateDiffConfirmEnabled !== false,
|
|
72
|
+
sessionsUsageTimeRange: normalizeUsageTimeRange(overrides.sessionsUsageTimeRange || this.sessionsUsageTimeRange),
|
|
73
|
+
promptsSubTab: normalizePromptsSubTab(overrides.promptsSubTab || this.promptsSubTab),
|
|
74
|
+
projectClaudeMdPath: typeof overrides.projectClaudeMdPath === 'string'
|
|
75
|
+
? overrides.projectClaudeMdPath
|
|
76
|
+
: (typeof this.projectClaudeMdPath === 'string' ? this.projectClaudeMdPath : ''),
|
|
77
|
+
navigation: normalizeNavigationSnapshot(this, navigationOverride || {})
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
applyWebUiPreferences(preferences = {}, options = {}) {
|
|
82
|
+
const source = preferences && typeof preferences === 'object' ? preferences : {};
|
|
83
|
+
const shouldApplyNavigation = !(options && options.applyNavigation === false);
|
|
84
|
+
this.__webUiPreferencesApplying = true;
|
|
85
|
+
try {
|
|
86
|
+
if (typeof source.shareCommandPrefix === 'string' && typeof this.normalizeShareCommandPrefix === 'function') {
|
|
87
|
+
this.shareCommandPrefix = this.normalizeShareCommandPrefix(source.shareCommandPrefix);
|
|
88
|
+
setLocalStorageValue('codexmateShareCommandPrefix', this.shareCommandPrefix);
|
|
89
|
+
}
|
|
90
|
+
if (Object.prototype.hasOwnProperty.call(source, 'sessionTrashEnabled') && typeof this.normalizeSessionTrashEnabled === 'function') {
|
|
91
|
+
this.sessionTrashEnabled = this.normalizeSessionTrashEnabled(source.sessionTrashEnabled);
|
|
92
|
+
setLocalStorageValue('codexmateSessionTrashEnabled', this.sessionTrashEnabled ? 'true' : 'false');
|
|
93
|
+
}
|
|
94
|
+
if (Object.prototype.hasOwnProperty.call(source, 'sessionTrashRetentionDays') && typeof this.normalizeSessionTrashRetentionDays === 'function') {
|
|
95
|
+
this.sessionTrashRetentionDays = this.normalizeSessionTrashRetentionDays(source.sessionTrashRetentionDays);
|
|
96
|
+
setLocalStorageValue('codexmateSessionTrashRetentionDays', this.sessionTrashRetentionDays);
|
|
97
|
+
}
|
|
98
|
+
if (typeof source.sessionTimelineStyle === 'string' && typeof this.normalizeSessionTimelineStyle === 'function') {
|
|
99
|
+
this.sessionTimelineStyle = this.normalizeSessionTimelineStyle(source.sessionTimelineStyle);
|
|
100
|
+
setLocalStorageValue('codexmateSessionTimelineStyle', this.sessionTimelineStyle);
|
|
101
|
+
}
|
|
102
|
+
if (Object.prototype.hasOwnProperty.call(source, 'configTemplateDiffConfirmEnabled') && typeof this.normalizeConfigTemplateDiffConfirmEnabled === 'function') {
|
|
103
|
+
this.configTemplateDiffConfirmEnabled = this.normalizeConfigTemplateDiffConfirmEnabled(source.configTemplateDiffConfirmEnabled);
|
|
104
|
+
setLocalStorageValue('codexmateConfigTemplateDiffConfirmEnabled', this.configTemplateDiffConfirmEnabled ? 'true' : 'false');
|
|
105
|
+
}
|
|
106
|
+
if (typeof source.sessionsUsageTimeRange === 'string') {
|
|
107
|
+
this.sessionsUsageTimeRange = normalizeUsageTimeRange(source.sessionsUsageTimeRange);
|
|
108
|
+
setLocalStorageValue('sessionsUsageTimeRange', this.sessionsUsageTimeRange);
|
|
109
|
+
}
|
|
110
|
+
if (typeof source.promptsSubTab === 'string') {
|
|
111
|
+
this.promptsSubTab = normalizePromptsSubTab(source.promptsSubTab);
|
|
112
|
+
setLocalStorageValue('codexmate_prompts_sub_tab', this.promptsSubTab);
|
|
113
|
+
}
|
|
114
|
+
if (typeof source.projectClaudeMdPath === 'string') {
|
|
115
|
+
this.projectClaudeMdPath = source.projectClaudeMdPath;
|
|
116
|
+
setLocalStorageValue('codexmate_project_claude_md_path', this.projectClaudeMdPath);
|
|
117
|
+
}
|
|
118
|
+
if (shouldApplyNavigation && source.navigation && typeof source.navigation === 'object') {
|
|
119
|
+
const nav = source.navigation;
|
|
120
|
+
if (typeof nav.settingsTab === 'string' && typeof this.normalizeSettingsTab === 'function') {
|
|
121
|
+
this.settingsTab = this.normalizeSettingsTab(nav.settingsTab);
|
|
122
|
+
}
|
|
123
|
+
if (typeof nav.configMode === 'string') this.configMode = nav.configMode;
|
|
124
|
+
if (typeof nav.mainTab === 'string') {
|
|
125
|
+
if (typeof this.switchMainTab === 'function') {
|
|
126
|
+
this.switchMainTab(nav.mainTab);
|
|
127
|
+
} else {
|
|
128
|
+
this.mainTab = nav.mainTab;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (nav.skillsTargetApp === 'codex' || nav.skillsTargetApp === 'claude') this.skillsTargetApp = nav.skillsTargetApp;
|
|
132
|
+
if (nav.promptTemplatesMode === 'compose' || nav.promptTemplatesMode === 'manage') this.promptTemplatesMode = nav.promptTemplatesMode;
|
|
133
|
+
if (typeof this.saveNavState === 'function') this.saveNavState();
|
|
134
|
+
}
|
|
135
|
+
} finally {
|
|
136
|
+
this.__webUiPreferencesApplying = false;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
async loadWebUiPreferences(options = {}) {
|
|
141
|
+
try {
|
|
142
|
+
const res = await api('get-web-ui-preferences');
|
|
143
|
+
if (res && res.preferences && typeof res.preferences === 'object') {
|
|
144
|
+
this.applyWebUiPreferences(res.preferences, options && typeof options === 'object' ? options : {});
|
|
145
|
+
}
|
|
146
|
+
} catch (_) {}
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
persistWebUiPreferences(overrides = {}) {
|
|
150
|
+
if (this.__webUiPreferencesApplying) return;
|
|
151
|
+
const snapshot = this.buildWebUiPreferencesSnapshot(overrides && typeof overrides === 'object' ? overrides : {});
|
|
152
|
+
if (this.__webUiPreferencesPersistTimer) {
|
|
153
|
+
clearTimeout(this.__webUiPreferencesPersistTimer);
|
|
154
|
+
}
|
|
155
|
+
this.__webUiPreferencesPersistTimer = setTimeout(() => {
|
|
156
|
+
this.__webUiPreferencesPersistTimer = 0;
|
|
157
|
+
api('set-web-ui-preferences', { preferences: snapshot }).catch(() => {});
|
|
158
|
+
}, 120);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|