codexmate 0.0.55 → 0.0.57

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.
Files changed (49) hide show
  1. package/README.md +2 -0
  2. package/README.vi.md +2 -0
  3. package/README.zh.md +2 -0
  4. package/cli/local-bridge.js +221 -22
  5. package/cli.js +797 -134
  6. package/lib/task-orchestrator.js +90 -21
  7. package/lib/task-workspace-chat.js +292 -0
  8. package/package.json +2 -2
  9. package/web-ui/app.js +57 -129
  10. package/web-ui/modules/app.computed.main-tabs.mjs +304 -7
  11. package/web-ui/modules/app.computed.session.mjs +210 -0
  12. package/web-ui/modules/app.methods.agents.mjs +3 -0
  13. package/web-ui/modules/app.methods.claude-config.mjs +178 -22
  14. package/web-ui/modules/app.methods.codex-config.mjs +294 -65
  15. package/web-ui/modules/app.methods.navigation.mjs +71 -84
  16. package/web-ui/modules/app.methods.openclaw-editing.mjs +3 -6
  17. package/web-ui/modules/app.methods.providers.mjs +15 -1
  18. package/web-ui/modules/app.methods.runtime.mjs +7 -2
  19. package/web-ui/modules/app.methods.session-actions.mjs +25 -12
  20. package/web-ui/modules/app.methods.session-browser.mjs +23 -54
  21. package/web-ui/modules/app.methods.session-trash.mjs +0 -1
  22. package/web-ui/modules/app.methods.startup-claude.mjs +35 -17
  23. package/web-ui/modules/app.methods.task-orchestration.mjs +347 -8
  24. package/web-ui/modules/app.methods.tool-config-permissions.mjs +0 -3
  25. package/web-ui/modules/app.methods.web-ui-preferences.mjs +415 -68
  26. package/web-ui/modules/config-template-confirm-pref.mjs +2 -3
  27. package/web-ui/modules/i18n/locales/en.mjs +187 -28
  28. package/web-ui/modules/i18n/locales/ja.mjs +184 -25
  29. package/web-ui/modules/i18n/locales/vi.mjs +186 -33
  30. package/web-ui/modules/i18n/locales/zh-tw.mjs +187 -28
  31. package/web-ui/modules/i18n/locales/zh.mjs +189 -30
  32. package/web-ui/modules/i18n.mjs +5 -12
  33. package/web-ui/modules/provider-default-names.mjs +25 -0
  34. package/web-ui/modules/sessions-filters-url.mjs +1 -2
  35. package/web-ui/partials/index/layout-header.html +37 -14
  36. package/web-ui/partials/index/modal-health-check.html +69 -5
  37. package/web-ui/partials/index/panel-config-codex.html +2 -2
  38. package/web-ui/partials/index/panel-orchestration.html +489 -282
  39. package/web-ui/partials/index/panel-sessions.html +97 -17
  40. package/web-ui/res/web-ui-render.precompiled.js +1423 -732
  41. package/web-ui/session-helpers.mjs +4 -1
  42. package/web-ui/styles/layout-shell.css +157 -1
  43. package/web-ui/styles/navigation-panels.css +11 -0
  44. package/web-ui/styles/responsive.css +98 -0
  45. package/web-ui/styles/sessions-preview.css +212 -2
  46. package/web-ui/styles/sessions-toolbar-trash.css +61 -18
  47. package/web-ui/styles/skills-list.css +122 -0
  48. package/web-ui/styles/task-orchestration.css +2161 -4
  49. package/web-ui/styles/titles-cards.css +52 -0
@@ -1,3 +1,5 @@
1
+ import { nextClaudeConfigName } from './provider-default-names.mjs';
2
+
1
3
  function normalizeClaudeText(value) {
2
4
  return typeof value === 'string' ? value.trim() : '';
3
5
  }
@@ -6,6 +8,19 @@ function normalizeClaudeBaseUrl(value) {
6
8
  return normalizeClaudeText(value).replace(/\/+$/g, '');
7
9
  }
8
10
 
11
+ function buildDeletedClaudeSettingsFingerprint(config = {}) {
12
+ const safe = config && typeof config === 'object' ? config : {};
13
+ const baseUrl = normalizeClaudeBaseUrl(safe.baseUrl);
14
+ const model = normalizeClaudeText(safe.model);
15
+ if (!baseUrl || !model) return null;
16
+ return {
17
+ baseUrl,
18
+ model,
19
+ providerCacheRef: normalizeClaudeText(safe.providerCacheRef),
20
+ deletedAt: Date.now()
21
+ };
22
+ }
23
+
9
24
  function isValidClaudeHttpUrl(value) {
10
25
  if (!value) return false;
11
26
  try {
@@ -75,10 +90,41 @@ export function createClaudeConfigMethods(options = {}) {
75
90
  return {
76
91
  switchClaudeConfig(name) {
77
92
  this.currentClaudeConfig = name;
78
- try { localStorage.setItem('currentClaudeConfig', name || ''); } catch (_) {}
93
+ if (typeof this.persistWebUiPreferences === 'function') {
94
+ this.persistWebUiPreferences({ currentClaudeConfig: name || '' });
95
+ }
79
96
  this.refreshClaudeModelContext();
80
97
  },
81
98
 
99
+ normalizeStoredClaudeConfigs() {
100
+ const configs = this.claudeConfigs && typeof this.claudeConfigs === 'object' && !Array.isArray(this.claudeConfigs)
101
+ ? this.claudeConfigs
102
+ : {};
103
+ let changed = configs !== this.claudeConfigs;
104
+ for (const config of Object.values(configs)) {
105
+ if (!config || typeof config !== 'object') continue;
106
+ if (config.apiKey && config.apiKey.includes('****')) {
107
+ config.apiKey = '';
108
+ config.hasKey = false;
109
+ changed = true;
110
+ }
111
+ const targetApiRaw = typeof config.targetApi === 'string' ? config.targetApi.trim().toLowerCase() : '';
112
+ const previousTargetApi = config.targetApi;
113
+ if (targetApiRaw === 'chat_completions' || targetApiRaw === 'chat-completions' || targetApiRaw === 'chat/completions') {
114
+ config.targetApi = 'chat_completions';
115
+ } else if (targetApiRaw === 'ollama') {
116
+ config.targetApi = 'ollama';
117
+ } else {
118
+ config.targetApi = 'responses';
119
+ }
120
+ if (config.targetApi !== previousTargetApi) {
121
+ changed = true;
122
+ }
123
+ }
124
+ this.claudeConfigs = configs;
125
+ return changed;
126
+ },
127
+
82
128
  onClaudeModelChange() {
83
129
  const name = this.currentClaudeConfig;
84
130
  if (!name || name === 'claude-local') {
@@ -107,13 +153,57 @@ export function createClaudeConfigMethods(options = {}) {
107
153
  },
108
154
 
109
155
  saveClaudeConfigs() {
110
- try { localStorage.setItem('claudeConfigs', JSON.stringify(this.claudeConfigs)); } catch (_) {}
111
- if (this.currentClaudeConfig) {
112
- try { localStorage.setItem('currentClaudeConfig', this.currentClaudeConfig); } catch (_) {}
156
+ if (typeof this.normalizeStoredClaudeConfigs === 'function') {
157
+ this.normalizeStoredClaudeConfigs();
158
+ }
159
+ if (typeof this.persistWebUiPreferences === 'function') {
160
+ this.persistWebUiPreferences({
161
+ claudeConfigs: this.claudeConfigs,
162
+ currentClaudeConfig: this.currentClaudeConfig || ''
163
+ });
113
164
  }
114
165
  this.syncClaudeBridgeProviders();
115
166
  },
116
167
 
168
+ rememberDeletedClaudeSettingsImport(config) {
169
+ const fingerprint = buildDeletedClaudeSettingsFingerprint(config);
170
+ if (!fingerprint) return;
171
+ const entries = Array.isArray(this.deletedClaudeSettingsImports) ? this.deletedClaudeSettingsImports : [];
172
+ const deduped = entries.filter((entry) => {
173
+ if (!entry || typeof entry !== 'object') return false;
174
+ return normalizeClaudeBaseUrl(entry.baseUrl) !== fingerprint.baseUrl
175
+ || normalizeClaudeText(entry.model) !== fingerprint.model;
176
+ });
177
+ deduped.push(fingerprint);
178
+ this.deletedClaudeSettingsImports = deduped.slice(-50);
179
+ if (typeof this.persistWebUiPreferences === 'function') {
180
+ this.persistWebUiPreferences({ deletedClaudeSettingsImports: this.deletedClaudeSettingsImports });
181
+ }
182
+ },
183
+
184
+ async applyCurrentClaudeConfigSilently() {
185
+ const name = normalizeClaudeText(this.currentClaudeConfig);
186
+ if (!name || !this.claudeConfigs || !this.claudeConfigs[name]) return false;
187
+ if (typeof this.applyClaudeConfig !== 'function') return false;
188
+ return await this.applyClaudeConfig(name, { silent: true });
189
+ },
190
+
191
+ selectClaudeFallbackConfigName(excludedNames = []) {
192
+ const configs = this.claudeConfigs && typeof this.claudeConfigs === 'object' ? this.claudeConfigs : {};
193
+ const excluded = new Set((Array.isArray(excludedNames) ? excludedNames : [excludedNames])
194
+ .map((name) => normalizeClaudeText(name))
195
+ .filter(Boolean));
196
+ const names = Object.keys(configs).filter((name) => name && !excluded.has(name));
197
+ const applyable = names.find((name) => {
198
+ const config = configs[name] || {};
199
+ return !!(config.apiKey
200
+ || config.providerCacheRef
201
+ || config.externalCredentialType
202
+ || config.targetApi === 'ollama');
203
+ });
204
+ return applyable || names[0] || '';
205
+ },
206
+
117
207
  async hydrateClaudeConfigsFromProviderCache(options = {}) {
118
208
  const silent = options && options.silent === true;
119
209
  try {
@@ -123,10 +213,10 @@ export function createClaudeConfigMethods(options = {}) {
123
213
  return false;
124
214
  }
125
215
  const providers = Array.isArray(res.providers) ? res.providers : [];
126
- if (providers.length === 0) return true;
127
216
  const configs = this.claudeConfigs && typeof this.claudeConfigs === 'object' ? this.claudeConfigs : {};
128
217
  let changed = false;
129
218
  let firstCachedName = '';
219
+ const liveCacheRefs = new Set();
130
220
  for (const provider of providers) {
131
221
  if (!provider || typeof provider !== 'object') continue;
132
222
  const name = normalizeClaudeText(provider.name);
@@ -134,12 +224,15 @@ export function createClaudeConfigMethods(options = {}) {
134
224
  const model = normalizeClaudeText(provider.model);
135
225
  if (!name || !baseUrl || !model) continue;
136
226
  if (!firstCachedName) firstCachedName = name;
227
+ const providerCacheRef = normalizeClaudeText(provider.providerCacheRef) || name;
228
+ liveCacheRefs.add(name);
229
+ liveCacheRefs.add(providerCacheRef);
137
230
  const cachedConfig = {
138
231
  apiKey: '',
139
232
  baseUrl,
140
233
  model,
141
234
  hasKey: provider.hasKey === true,
142
- providerCacheRef: normalizeClaudeText(provider.providerCacheRef) || name,
235
+ providerCacheRef,
143
236
  source: 'provider-cache',
144
237
  targetApi: normalizeClaudeText(provider.targetApi) || 'responses'
145
238
  };
@@ -152,15 +245,33 @@ export function createClaudeConfigMethods(options = {}) {
152
245
  changed = true;
153
246
  }
154
247
  }
248
+ for (const [name, existing] of Object.entries(configs)) {
249
+ if (!existing || typeof existing !== 'object') continue;
250
+ const providerCacheRef = normalizeClaudeText(existing.providerCacheRef);
251
+ const source = normalizeClaudeText(existing.source);
252
+ const cacheBacked = source === 'provider-cache' || !!providerCacheRef;
253
+ if (!cacheBacked) continue;
254
+ const ref = providerCacheRef || normalizeClaudeText(name);
255
+ if (liveCacheRefs.has(ref) || liveCacheRefs.has(normalizeClaudeText(name))) continue;
256
+ delete configs[name];
257
+ changed = true;
258
+ }
155
259
  this.claudeConfigs = configs;
156
- if (firstCachedName) {
157
- let savedCurrent = '';
158
- try { savedCurrent = localStorage.getItem('currentClaudeConfig') || ''; } catch (_) {}
159
- const current = normalizeClaudeText(this.currentClaudeConfig);
260
+ const current = normalizeClaudeText(this.currentClaudeConfig);
261
+ if (current && !configs[current]) {
262
+ this.currentClaudeConfig = firstCachedName || Object.keys(configs)[0] || '';
263
+ changed = true;
264
+ } else if (firstCachedName) {
160
265
  const currentConfig = current && configs[current] ? configs[current] : null;
161
- if (!savedCurrent && (!current || (currentConfig && currentConfig.hasKey === false && !currentConfig.providerCacheRef))) {
266
+ const currentApplyable = currentConfig && (
267
+ currentConfig.hasKey === true
268
+ || !!normalizeClaudeText(currentConfig.apiKey)
269
+ || !!normalizeClaudeText(currentConfig.providerCacheRef)
270
+ || !!normalizeClaudeText(currentConfig.externalCredentialType)
271
+ || normalizeClaudeText(currentConfig.targetApi) === 'ollama'
272
+ );
273
+ if (!currentApplyable) {
162
274
  this.currentClaudeConfig = firstCachedName;
163
- try { localStorage.setItem('currentClaudeConfig', firstCachedName); } catch (_) {}
164
275
  changed = true;
165
276
  }
166
277
  }
@@ -323,6 +434,28 @@ export function createClaudeConfigMethods(options = {}) {
323
434
  await this.applyClaudeConfig(name);
324
435
  },
325
436
 
437
+ async deleteClaudeProviderCacheRef(configOrName) {
438
+ const config = configOrName && typeof configOrName === 'object'
439
+ ? configOrName
440
+ : (this.claudeConfigs && this.claudeConfigs[configOrName] ? this.claudeConfigs[configOrName] : null);
441
+ const ref = normalizeClaudeText(config && config.providerCacheRef);
442
+ const source = normalizeClaudeText(config && config.source);
443
+ if (!ref && source !== 'provider-cache') return true;
444
+ const name = ref || normalizeClaudeText(config && config.name) || normalizeClaudeText(configOrName);
445
+ if (!name) return true;
446
+ try {
447
+ const res = await api('delete-provider-cache-record', { name, group: 'claude' });
448
+ if (res && res.error) {
449
+ this.showMessage(res.error, 'error');
450
+ return false;
451
+ }
452
+ return true;
453
+ } catch (e) {
454
+ this.showMessage(e && e.message ? e.message : this.t('toast.operation.fail'), 'error');
455
+ return false;
456
+ }
457
+ },
458
+
326
459
  async deleteClaudeConfig(name) {
327
460
  if (Object.keys(this.claudeConfigs).length <= 1) {
328
461
  return this.showMessage(this.t('toast.claude.keepOne'), 'error');
@@ -336,25 +469,43 @@ export function createClaudeConfigMethods(options = {}) {
336
469
  });
337
470
  if (!confirmed) return;
338
471
 
472
+ const config = this.claudeConfigs[name];
473
+ const cacheDeleted = await this.deleteClaudeProviderCacheRef(config || name);
474
+ if (!cacheDeleted) return;
475
+
476
+ if (typeof this.rememberDeletedClaudeSettingsImport === 'function') {
477
+ this.rememberDeletedClaudeSettingsImport(config);
478
+ }
339
479
  delete this.claudeConfigs[name];
340
480
  if (this.currentClaudeConfig === name) {
341
- this.currentClaudeConfig = Object.keys(this.claudeConfigs)[0];
481
+ this.currentClaudeConfig = this.selectClaudeFallbackConfigName();
342
482
  }
343
483
  this.saveClaudeConfigs();
344
484
  this.showMessage(this.t('toast.operation.success'), 'success');
345
- this.refreshClaudeModelContext();
485
+ if (this.currentClaudeConfig) {
486
+ await this.applyCurrentClaudeConfigSilently();
487
+ } else {
488
+ this.refreshClaudeModelContext();
489
+ }
346
490
  },
347
491
 
348
- async applyClaudeConfig(name) {
492
+ async applyClaudeConfig(name, options = {}) {
493
+ const silent = !!(options && options.silent);
494
+ const silentSuccess = silent || !!(options && options.silentSuccess);
495
+ const silentError = silent || !!(options && options.silentError);
349
496
  this.currentClaudeConfig = name;
350
- try { localStorage.setItem('currentClaudeConfig', name || ''); } catch (_) {}
497
+ if (typeof this.persistWebUiPreferences === 'function') {
498
+ this.persistWebUiPreferences({ currentClaudeConfig: name || '' });
499
+ }
351
500
  this.refreshClaudeModelContext();
352
501
  const config = this.claudeConfigs[name];
353
502
 
354
503
  if (!config.apiKey && !config.providerCacheRef && config.targetApi !== 'ollama') {
355
504
  if (config.externalCredentialType) {
505
+ if (silentError) return false;
356
506
  return this.showMessage(this.t('toast.claude.externalAuth'), 'info');
357
507
  }
508
+ if (silentError) return false;
358
509
  return this.showMessage(this.t('toast.claude.apiKeyRequired'), 'error');
359
510
  }
360
511
 
@@ -362,15 +513,18 @@ export function createClaudeConfigMethods(options = {}) {
362
513
  try {
363
514
  const res = await api('apply-claude-config', { config: { ...config, name } });
364
515
  if (res.error || res.success === false) {
365
- this.showMessage(res.error || this.t('toast.apply.fail'), 'error');
516
+ if (!silentError) this.showMessage(res.error || this.t('toast.apply.fail'), 'error');
517
+ return false;
366
518
  } else {
367
- if (this._lastAppliedClaudeKey !== _claudeKey2) {
519
+ if (!silentSuccess && this._lastAppliedClaudeKey !== _claudeKey2) {
368
520
  this.showMessage(this.t('toast.apply.success'), 'success');
369
- this._lastAppliedClaudeKey = _claudeKey2;
370
521
  }
522
+ this._lastAppliedClaudeKey = _claudeKey2;
523
+ return true;
371
524
  }
372
525
  } catch (_) {
373
- this.showMessage(this.t('toast.apply.fail'), 'error');
526
+ if (!silentError) this.showMessage(this.t('toast.apply.fail'), 'error');
527
+ return false;
374
528
  }
375
529
  },
376
530
 
@@ -378,7 +532,7 @@ export function createClaudeConfigMethods(options = {}) {
378
532
  this.showClaudeConfigModal = false;
379
533
  this.showAddClaudeConfigKey = false;
380
534
  this.newClaudeConfig = {
381
- name: '',
535
+ name: nextClaudeConfigName(this.claudeConfigs),
382
536
  apiKey: '',
383
537
  externalCredentialType: '',
384
538
  baseUrl: '',
@@ -456,7 +610,9 @@ export function createClaudeConfigMethods(options = {}) {
456
610
 
457
611
  async applyClaudeLocalBridge() {
458
612
  this.currentClaudeConfig = 'claude-local';
459
- try { localStorage.setItem('currentClaudeConfig', 'claude-local'); } catch (_) {}
613
+ if (typeof this.persistWebUiPreferences === 'function') {
614
+ this.persistWebUiPreferences({ currentClaudeConfig: 'claude-local' });
615
+ }
460
616
  this.refreshClaudeModelContext();
461
617
 
462
618
  const candidates = this.claudeLocalBridgeCandidateProviders();