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
|
@@ -24,6 +24,28 @@ function getResponseMessage(response, fallback) {
|
|
|
24
24
|
return fallback;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
|
|
28
|
+
function normalizeProviderName(value) {
|
|
29
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function providerIssueDetail(issues) {
|
|
33
|
+
if (!Array.isArray(issues) || !issues.length) return '';
|
|
34
|
+
return issues
|
|
35
|
+
.map((issue) => issue && (issue.message || issue.code || ''))
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.join(';');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function remoteProviderDetail(remote) {
|
|
41
|
+
if (!remote || typeof remote !== 'object') return '';
|
|
42
|
+
const parts = [];
|
|
43
|
+
if (remote.endpoint) parts.push(remote.endpoint);
|
|
44
|
+
if (remote.statusCode) parts.push(String(remote.statusCode));
|
|
45
|
+
if (remote.message) parts.push(remote.message);
|
|
46
|
+
return parts.join(' · ');
|
|
47
|
+
}
|
|
48
|
+
|
|
27
49
|
export function createCodexConfigMethods(options = {}) {
|
|
28
50
|
const {
|
|
29
51
|
api,
|
|
@@ -303,6 +325,7 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
303
325
|
async runHealthCheck(options = {}) {
|
|
304
326
|
this.healthCheckLoading = true;
|
|
305
327
|
this.healthCheckResult = null;
|
|
328
|
+
this.healthCheckFailedProviderSelections = {};
|
|
306
329
|
this.healthCheckBatchTotal = 0;
|
|
307
330
|
this.healthCheckBatchDone = 0;
|
|
308
331
|
this.healthCheckBatchFailed = 0;
|
|
@@ -354,7 +377,13 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
354
377
|
}
|
|
355
378
|
|
|
356
379
|
if (this.configMode === 'claude') {
|
|
357
|
-
const
|
|
380
|
+
const configs = this.claudeConfigs || {};
|
|
381
|
+
const currentName = typeof this.currentClaudeConfig === 'string' && configs[this.currentClaudeConfig]
|
|
382
|
+
? this.currentClaudeConfig
|
|
383
|
+
: Object.keys(configs)[0];
|
|
384
|
+
const entries = currentName && configs[currentName]
|
|
385
|
+
? [[currentName, configs[currentName]]]
|
|
386
|
+
: [];
|
|
358
387
|
this.healthCheckBatchTotal = entries.length;
|
|
359
388
|
|
|
360
389
|
const speedTasks = entries.map(([name, config]) => this.runClaudeSpeedTest(name, config)
|
|
@@ -395,80 +424,44 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
395
424
|
speedTests: results
|
|
396
425
|
}
|
|
397
426
|
};
|
|
427
|
+
if (!silent) {
|
|
428
|
+
this.showHealthCheckModal = true;
|
|
429
|
+
}
|
|
398
430
|
if (ok && !silent) {
|
|
399
|
-
this.showMessage('
|
|
431
|
+
this.showMessage(this.t('toast.check.success'), 'success');
|
|
400
432
|
}
|
|
401
433
|
return;
|
|
402
434
|
}
|
|
403
435
|
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
? (this.providersList || [])
|
|
408
|
-
.map((provider) => typeof provider === 'string'
|
|
409
|
-
? provider.trim()
|
|
410
|
-
: String((provider && provider.name) || '').trim())
|
|
411
|
-
.filter(Boolean)
|
|
412
|
-
: [];
|
|
413
|
-
const currentProvider = String(this.currentProvider || '').trim();
|
|
414
|
-
const orderedProviders = currentProvider && providers.includes(currentProvider)
|
|
415
|
-
? [currentProvider, ...providers.filter((name) => name !== currentProvider)]
|
|
416
|
-
: providers;
|
|
417
|
-
this.healthCheckBatchTotal = orderedProviders.length;
|
|
418
|
-
|
|
419
|
-
const speedTasks = orderedProviders.map((provider) => this.runSpeedTest(provider, { silent: true, timeoutMs: speedTimeoutMs })
|
|
420
|
-
.then((result) => {
|
|
421
|
-
if (!result || result.ok !== true) {
|
|
422
|
-
this.healthCheckBatchFailed += 1;
|
|
423
|
-
}
|
|
424
|
-
return { name: provider, result };
|
|
425
|
-
})
|
|
426
|
-
.catch((err) => {
|
|
427
|
-
this.healthCheckBatchFailed += 1;
|
|
428
|
-
return {
|
|
429
|
-
name: provider,
|
|
430
|
-
result: { ok: false, error: err && err.message ? err.message : 'Speed test failed' }
|
|
431
|
-
};
|
|
432
|
-
})
|
|
433
|
-
.finally(() => {
|
|
434
|
-
this.healthCheckBatchDone += 1;
|
|
435
|
-
})
|
|
436
|
-
);
|
|
437
|
-
|
|
438
|
-
const configTask = api('config-health-check', { remote: this.configMode === 'codex' });
|
|
439
|
-
const [res, pairs] = await Promise.all([
|
|
440
|
-
configTask,
|
|
441
|
-
Promise.all(speedTasks)
|
|
436
|
+
const [configRes, providersRes] = await Promise.all([
|
|
437
|
+
api('config-health-check', { remote: true }),
|
|
438
|
+
api('providers-health', { remote: false })
|
|
442
439
|
]);
|
|
443
|
-
if (hasResponseError(
|
|
440
|
+
if (providersRes && typeof providersRes === 'object' && !hasResponseError(providersRes)) {
|
|
441
|
+
this.providersHealthResult = providersRes;
|
|
442
|
+
}
|
|
443
|
+
if (hasResponseError(configRes)) {
|
|
444
444
|
this.healthCheckResult = null;
|
|
445
445
|
if (!silent) {
|
|
446
|
-
this.showMessage(getResponseMessage(
|
|
447
|
-
}
|
|
448
|
-
} else if (res && typeof res === 'object') {
|
|
449
|
-
const issues = Array.isArray(res.issues) ? [...res.issues] : [];
|
|
450
|
-
let remote = res.remote || null;
|
|
451
|
-
if (shouldRunSpeedTests) {
|
|
452
|
-
const results = {};
|
|
453
|
-
for (const pair of pairs) {
|
|
454
|
-
results[pair.name] = pair.result || null;
|
|
455
|
-
const issue = this.buildSpeedTestIssue(pair.name, pair.result);
|
|
456
|
-
if (issue) issues.push(issue);
|
|
457
|
-
}
|
|
458
|
-
remote = remote && typeof remote === 'object'
|
|
459
|
-
? { ...remote, speedTests: results }
|
|
460
|
-
: { type: 'speed-test', speedTests: results };
|
|
446
|
+
this.showMessage(getResponseMessage(configRes, this.t('toast.check.fail')), 'error');
|
|
461
447
|
}
|
|
462
|
-
|
|
463
|
-
const
|
|
448
|
+
} else if (configRes && typeof configRes === 'object') {
|
|
449
|
+
const issues = Array.isArray(configRes.issues) ? [...configRes.issues] : [];
|
|
450
|
+
const ok = typeof configRes.ok === 'boolean' ? configRes.ok : issues.length === 0;
|
|
464
451
|
this.healthCheckResult = {
|
|
465
|
-
...
|
|
452
|
+
...configRes,
|
|
466
453
|
ok,
|
|
467
454
|
issues,
|
|
468
|
-
remote
|
|
455
|
+
remote: configRes.remote || null
|
|
469
456
|
};
|
|
470
|
-
|
|
471
|
-
|
|
457
|
+
this.healthCheckBatchTotal = 1;
|
|
458
|
+
this.healthCheckBatchDone = 1;
|
|
459
|
+
this.healthCheckBatchFailed = ok ? 0 : 1;
|
|
460
|
+
if (!silent) {
|
|
461
|
+
this.showHealthCheckModal = true;
|
|
462
|
+
}
|
|
463
|
+
if (!silent) {
|
|
464
|
+
this.showMessage(this.t(ok ? 'toast.check.success' : 'toast.check.fail'), ok ? 'success' : 'error');
|
|
472
465
|
}
|
|
473
466
|
} else {
|
|
474
467
|
this.healthCheckResult = null;
|
|
@@ -479,16 +472,252 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
479
472
|
} catch (e) {
|
|
480
473
|
this.healthCheckResult = null;
|
|
481
474
|
if (!(options && options.silent)) {
|
|
482
|
-
this.showMessage('
|
|
475
|
+
this.showMessage(this.t('toast.check.fail'), 'error');
|
|
483
476
|
}
|
|
484
477
|
} finally {
|
|
485
478
|
this.healthCheckBatchTotal = this.healthCheckBatchTotal || 0;
|
|
486
479
|
this.healthCheckBatchDone = Math.min(this.healthCheckBatchDone || 0, this.healthCheckBatchTotal || 0);
|
|
487
480
|
this.healthCheckLoading = false;
|
|
488
|
-
|
|
489
|
-
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
getHealthCheckFailedProviderItems() {
|
|
485
|
+
const result = this.healthCheckResult && typeof this.healthCheckResult === 'object'
|
|
486
|
+
? this.healthCheckResult
|
|
487
|
+
: null;
|
|
488
|
+
if (!result) return [];
|
|
489
|
+
const remote = result.remote && typeof result.remote === 'object' ? result.remote : null;
|
|
490
|
+
const items = new Map();
|
|
491
|
+
const currentMode = this.configMode === 'claude' ? 'claude' : 'codex';
|
|
492
|
+
const pushItem = (name, detail, status = 'red', mode = currentMode) => {
|
|
493
|
+
const providerName = normalizeProviderName(name);
|
|
494
|
+
if (!providerName) return;
|
|
495
|
+
const key = `${mode}:${providerName}`;
|
|
496
|
+
const provider = (this.providersList || []).find((item) => item && item.name === providerName) || providerName;
|
|
497
|
+
const baseDeletable = mode === 'claude'
|
|
498
|
+
? !!(this.claudeConfigs && this.claudeConfigs[providerName])
|
|
499
|
+
: (typeof this.shouldShowProviderDelete === 'function' ? this.shouldShowProviderDelete(provider) : true);
|
|
500
|
+
const writeAllowed = typeof this.isToolConfigWriteAllowed === 'function'
|
|
501
|
+
? this.isToolConfigWriteAllowed(mode)
|
|
502
|
+
: true;
|
|
503
|
+
const deletable = baseDeletable && writeAllowed;
|
|
504
|
+
const blockedReason = !baseDeletable ? 'reserved' : (!writeAllowed ? 'readonly' : '');
|
|
505
|
+
const previous = items.get(key) || {};
|
|
506
|
+
items.set(key, {
|
|
507
|
+
key,
|
|
508
|
+
mode,
|
|
509
|
+
name: providerName,
|
|
510
|
+
status: previous.status || status || 'red',
|
|
511
|
+
detail: detail || previous.detail || '',
|
|
512
|
+
deletable,
|
|
513
|
+
blockedReason: previous.blockedReason || blockedReason,
|
|
514
|
+
selected: !!(this.healthCheckFailedProviderSelections && this.healthCheckFailedProviderSelections[key])
|
|
515
|
+
});
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
if (remote && remote.type === 'providers-health' && Array.isArray(remote.providers)) {
|
|
519
|
+
for (const provider of remote.providers) {
|
|
520
|
+
if (!provider || provider.status === 'green') continue;
|
|
521
|
+
pushItem(provider.provider, providerIssueDetail(provider.issues) || remoteProviderDetail(provider.remote), provider.status || 'red', 'codex');
|
|
490
522
|
}
|
|
491
523
|
}
|
|
524
|
+
|
|
525
|
+
if (remote && remote.type === 'remote-health-check' && (result.ok === false || remote.ok === false)) {
|
|
526
|
+
pushItem(remote.provider, remoteProviderDetail(remote), 'red', 'codex');
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (remote && remote.speedTests && typeof remote.speedTests === 'object') {
|
|
530
|
+
for (const [name, speedResult] of Object.entries(remote.speedTests)) {
|
|
531
|
+
if (speedResult && speedResult.ok === true) continue;
|
|
532
|
+
pushItem(name, speedResult && speedResult.error ? speedResult.error : this.t('config.health.fail'), 'red', this.configMode === 'claude' ? 'claude' : 'codex');
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (Array.isArray(result.issues)) {
|
|
537
|
+
for (const issue of result.issues) {
|
|
538
|
+
if (!issue || typeof issue !== 'object') continue;
|
|
539
|
+
const name = normalizeProviderName(issue.provider || issue.providerName || issue.name);
|
|
540
|
+
if (name) pushItem(name, issue.message || issue.code || '', 'red', currentMode);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return Array.from(items.values());
|
|
545
|
+
},
|
|
546
|
+
|
|
547
|
+
getSelectableHealthCheckFailedProviderItems() {
|
|
548
|
+
return this.getHealthCheckFailedProviderItems().filter((item) => item.deletable);
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
hasHealthCheckFailedProviderSelection() {
|
|
552
|
+
return this.getSelectableHealthCheckFailedProviderItems().some((item) => item.selected);
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
areAllHealthCheckFailedProvidersSelected() {
|
|
556
|
+
const selectable = this.getSelectableHealthCheckFailedProviderItems();
|
|
557
|
+
return selectable.length > 0 && selectable.every((item) => item.selected);
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
setAllHealthCheckFailedProviderSelections(checked) {
|
|
561
|
+
const selectable = this.getSelectableHealthCheckFailedProviderItems();
|
|
562
|
+
const next = { ...(this.healthCheckFailedProviderSelections || {}) };
|
|
563
|
+
for (const item of selectable) {
|
|
564
|
+
next[item.key] = !!checked;
|
|
565
|
+
}
|
|
566
|
+
this.healthCheckFailedProviderSelections = next;
|
|
567
|
+
},
|
|
568
|
+
|
|
569
|
+
toggleHealthCheckFailedProviderSelection(item, checked) {
|
|
570
|
+
if (!item || !item.key || !item.deletable) return;
|
|
571
|
+
this.healthCheckFailedProviderSelections = {
|
|
572
|
+
...(this.healthCheckFailedProviderSelections || {}),
|
|
573
|
+
[item.key]: !!checked
|
|
574
|
+
};
|
|
575
|
+
},
|
|
576
|
+
|
|
577
|
+
async deleteSelectedHealthCheckFailedProviders() {
|
|
578
|
+
const selectedItems = this.getHealthCheckFailedProviderItems()
|
|
579
|
+
.filter((item) => item.selected && item.deletable);
|
|
580
|
+
if (!selectedItems.length) {
|
|
581
|
+
this.showMessage(this.t('toast.health.noFailedProviderSelection'), 'info');
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
this.healthCheckFailedProviderDeleting = true;
|
|
585
|
+
const deleted = [];
|
|
586
|
+
try {
|
|
587
|
+
const claudeItems = selectedItems.filter((item) => item.mode === 'claude');
|
|
588
|
+
const codexItems = selectedItems.filter((item) => item.mode !== 'claude');
|
|
589
|
+
|
|
590
|
+
if (claudeItems.length) {
|
|
591
|
+
const configs = this.claudeConfigs && typeof this.claudeConfigs === 'object' ? this.claudeConfigs : {};
|
|
592
|
+
const names = claudeItems.map((item) => item.name).filter((name) => configs[name]);
|
|
593
|
+
if (names.length) {
|
|
594
|
+
const deletedCurrentClaude = names.includes(this.currentClaudeConfig);
|
|
595
|
+
const remainingNames = Object.keys(configs).filter((name) => !names.includes(name));
|
|
596
|
+
if (remainingNames.length === 0) {
|
|
597
|
+
throw new Error(this.t('toast.claude.keepOne'));
|
|
598
|
+
}
|
|
599
|
+
for (const name of names) {
|
|
600
|
+
const config = configs[name];
|
|
601
|
+
if (config && (config.providerCacheRef || config.source === 'provider-cache')) {
|
|
602
|
+
if (typeof this.deleteClaudeProviderCacheRef === 'function') {
|
|
603
|
+
const ok = await this.deleteClaudeProviderCacheRef(config);
|
|
604
|
+
if (!ok) throw new Error(this.t('toast.delete.fail'));
|
|
605
|
+
} else {
|
|
606
|
+
const cacheName = typeof config.providerCacheRef === 'string' && config.providerCacheRef.trim()
|
|
607
|
+
? config.providerCacheRef.trim()
|
|
608
|
+
: name;
|
|
609
|
+
const res = await api('delete-provider-cache-record', { name: cacheName, group: 'claude' });
|
|
610
|
+
if (res && res.error) throw new Error(res.error);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (typeof this.rememberDeletedClaudeSettingsImport === 'function') {
|
|
614
|
+
this.rememberDeletedClaudeSettingsImport(config);
|
|
615
|
+
}
|
|
616
|
+
delete configs[name];
|
|
617
|
+
deleted.push(name);
|
|
618
|
+
}
|
|
619
|
+
if (deletedCurrentClaude) {
|
|
620
|
+
this.currentClaudeConfig = typeof this.selectClaudeFallbackConfigName === 'function'
|
|
621
|
+
? this.selectClaudeFallbackConfigName(names)
|
|
622
|
+
: remainingNames[0];
|
|
623
|
+
}
|
|
624
|
+
if (typeof this.saveClaudeConfigs === 'function') {
|
|
625
|
+
this.saveClaudeConfigs();
|
|
626
|
+
}
|
|
627
|
+
if (deletedCurrentClaude && typeof this.applyCurrentClaudeConfigSilently === 'function') {
|
|
628
|
+
await this.applyCurrentClaudeConfigSilently();
|
|
629
|
+
} else if (typeof this.refreshClaudeModelContext === 'function') {
|
|
630
|
+
this.refreshClaudeModelContext();
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
for (const item of codexItems) {
|
|
636
|
+
const res = await api('delete-provider', { name: item.name });
|
|
637
|
+
if (res && res.error) {
|
|
638
|
+
throw new Error(res.error);
|
|
639
|
+
}
|
|
640
|
+
this.providersList = (this.providersList || []).filter((provider) => provider && provider.name !== item.name);
|
|
641
|
+
if (this.currentModels && this.currentModels[item.name]) {
|
|
642
|
+
delete this.currentModels[item.name];
|
|
643
|
+
}
|
|
644
|
+
if (res && res.switched && res.provider) {
|
|
645
|
+
this.currentProvider = res.provider;
|
|
646
|
+
if (res.model) this.currentModel = res.model;
|
|
647
|
+
this.providersList = (this.providersList || []).map((provider) => ({
|
|
648
|
+
...provider,
|
|
649
|
+
current: provider.name === res.provider
|
|
650
|
+
}));
|
|
651
|
+
}
|
|
652
|
+
deleted.push(item.name);
|
|
653
|
+
}
|
|
654
|
+
const deletedSet = new Set(deleted);
|
|
655
|
+
this.healthCheckFailedProviderSelections = {};
|
|
656
|
+
const result = this.healthCheckResult;
|
|
657
|
+
const remote = result && result.remote;
|
|
658
|
+
const remainingIssues = Array.isArray(result && result.issues)
|
|
659
|
+
? result.issues.filter((issue) => {
|
|
660
|
+
const name = normalizeProviderName(issue && (issue.provider || issue.providerName || issue.name));
|
|
661
|
+
return !name || !deletedSet.has(name);
|
|
662
|
+
})
|
|
663
|
+
: [];
|
|
664
|
+
if (result && typeof result === 'object') {
|
|
665
|
+
if (remote && remote.type === 'providers-health' && Array.isArray(remote.providers)) {
|
|
666
|
+
const providers = remote.providers.filter((provider) => !deletedSet.has(provider && provider.provider));
|
|
667
|
+
const summary = {
|
|
668
|
+
total: providers.length,
|
|
669
|
+
green: providers.filter((p) => p && p.status === 'green').length,
|
|
670
|
+
yellow: providers.filter((p) => p && p.status === 'yellow').length,
|
|
671
|
+
red: providers.filter((p) => p && p.status === 'red').length
|
|
672
|
+
};
|
|
673
|
+
this.healthCheckResult = {
|
|
674
|
+
...result,
|
|
675
|
+
ok: remainingIssues.length === 0 && summary.yellow === 0 && summary.red === 0,
|
|
676
|
+
remote: {
|
|
677
|
+
...remote,
|
|
678
|
+
providers,
|
|
679
|
+
summary
|
|
680
|
+
},
|
|
681
|
+
issues: remainingIssues
|
|
682
|
+
};
|
|
683
|
+
this.healthCheckBatchTotal = summary.total;
|
|
684
|
+
this.healthCheckBatchDone = summary.total;
|
|
685
|
+
this.healthCheckBatchFailed = summary.yellow + summary.red;
|
|
686
|
+
} else if (remote && remote.type === 'speed-test' && remote.speedTests && typeof remote.speedTests === 'object') {
|
|
687
|
+
const speedTests = Object.fromEntries(
|
|
688
|
+
Object.entries(remote.speedTests).filter(([name]) => !deletedSet.has(normalizeProviderName(name)))
|
|
689
|
+
);
|
|
690
|
+
const total = Object.keys(speedTests).length;
|
|
691
|
+
const failed = Object.values(speedTests).filter((entry) => !entry || entry.ok !== true).length;
|
|
692
|
+
this.healthCheckResult = {
|
|
693
|
+
...result,
|
|
694
|
+
ok: remainingIssues.length === 0 && failed === 0,
|
|
695
|
+
remote: {
|
|
696
|
+
...remote,
|
|
697
|
+
speedTests
|
|
698
|
+
},
|
|
699
|
+
issues: remainingIssues
|
|
700
|
+
};
|
|
701
|
+
this.healthCheckBatchTotal = total;
|
|
702
|
+
this.healthCheckBatchDone = total;
|
|
703
|
+
this.healthCheckBatchFailed = failed;
|
|
704
|
+
} else {
|
|
705
|
+
const removedRemote = remote && remote.type === 'remote-health-check' && deletedSet.has(remote.provider);
|
|
706
|
+
this.healthCheckResult = {
|
|
707
|
+
...result,
|
|
708
|
+
ok: remainingIssues.length === 0,
|
|
709
|
+
remote: removedRemote ? null : remote,
|
|
710
|
+
issues: remainingIssues
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
this.showMessage(this.t('toast.health.deleteFailedProvidersDone', { count: deleted.length }), 'success');
|
|
715
|
+
this.showHealthCheckModal = false;
|
|
716
|
+
} catch (e) {
|
|
717
|
+
this.showMessage(e && e.message ? e.message : this.t('toast.delete.fail'), 'error');
|
|
718
|
+
} finally {
|
|
719
|
+
this.healthCheckFailedProviderDeleting = false;
|
|
720
|
+
}
|
|
492
721
|
},
|
|
493
722
|
|
|
494
723
|
async runProvidersHealthCheck(options = {}) {
|
|
@@ -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 {
|
|
@@ -721,7 +724,10 @@
|
|
|
721
724
|
? this.activeSessionMessages.length
|
|
722
725
|
: 0;
|
|
723
726
|
if (total <= 0) return;
|
|
724
|
-
|
|
727
|
+
const initialBatchSize = Number.isFinite(this.sessionPreviewInitialBatchSize)
|
|
728
|
+
? Math.max(1, Math.floor(this.sessionPreviewInitialBatchSize))
|
|
729
|
+
: 12;
|
|
730
|
+
this.sessionPreviewVisibleCount = Math.min(total, initialBatchSize);
|
|
725
731
|
this.invalidateSessionTimelineMeasurementCache();
|
|
726
732
|
},
|
|
727
733
|
|