codexmate 0.0.23 → 0.0.24

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 (55) hide show
  1. package/README.md +4 -3
  2. package/README.zh.md +4 -2
  3. package/cli/auth-profiles.js +23 -7
  4. package/cli/doctor-core.js +903 -0
  5. package/cli/import-skills-url.js +334 -0
  6. package/cli.js +304 -208
  7. package/lib/cli-models-utils.js +0 -40
  8. package/lib/cli-network-utils.js +28 -2
  9. package/package.json +5 -2
  10. package/plugins/README.md +20 -0
  11. package/plugins/README.zh-CN.md +20 -0
  12. package/plugins/prompt-templates/comment-polish/index.mjs +25 -0
  13. package/plugins/prompt-templates/computed.mjs +253 -0
  14. package/plugins/prompt-templates/index.mjs +8 -0
  15. package/plugins/prompt-templates/manifest.mjs +15 -0
  16. package/plugins/prompt-templates/methods.mjs +619 -0
  17. package/plugins/prompt-templates/overview.mjs +90 -0
  18. package/plugins/prompt-templates/ownership.mjs +19 -0
  19. package/plugins/prompt-templates/rule-ack/index.mjs +21 -0
  20. package/plugins/prompt-templates/storage.mjs +64 -0
  21. package/plugins/registry.mjs +16 -0
  22. package/res/logo-pack.webp +0 -0
  23. package/web-ui/app.js +15 -32
  24. package/web-ui/index.html +4 -3
  25. package/web-ui/modules/app.computed.dashboard.mjs +22 -22
  26. package/web-ui/modules/app.computed.main-tabs.mjs +3 -0
  27. package/web-ui/modules/app.methods.agents.mjs +91 -3
  28. package/web-ui/modules/app.methods.codex-config.mjs +153 -164
  29. package/web-ui/modules/app.methods.navigation.mjs +34 -1
  30. package/web-ui/modules/app.methods.runtime.mjs +24 -2
  31. package/web-ui/modules/app.methods.session-browser.mjs +9 -2
  32. package/web-ui/modules/config-mode.computed.mjs +1 -3
  33. package/web-ui/modules/i18n.dict.mjs +2039 -0
  34. package/web-ui/modules/i18n.mjs +2 -1769
  35. package/web-ui/partials/index/layout-header.html +36 -32
  36. package/web-ui/partials/index/modal-config-template-agents.html +3 -4
  37. package/web-ui/partials/index/modal-health-check.html +33 -60
  38. package/web-ui/partials/index/panel-config-claude.html +35 -15
  39. package/web-ui/partials/index/panel-config-codex.html +47 -19
  40. package/web-ui/partials/index/panel-config-openclaw.html +8 -3
  41. package/web-ui/partials/index/panel-dashboard.html +186 -0
  42. package/web-ui/partials/index/panel-docs.html +1 -1
  43. package/web-ui/partials/index/panel-market.html +3 -0
  44. package/web-ui/partials/index/panel-orchestration.html +3 -0
  45. package/web-ui/partials/index/panel-plugins.html +16 -10
  46. package/web-ui/partials/index/panel-sessions.html +4 -1
  47. package/web-ui/partials/index/panel-settings.html +1 -1
  48. package/web-ui/partials/index/panel-usage.html +2 -1
  49. package/web-ui/styles/controls-forms.css +9 -2
  50. package/web-ui/styles/dashboard.css +274 -0
  51. package/web-ui/styles/layout-shell.css +2 -2
  52. package/web-ui/styles/sessions-list.css +3 -3
  53. package/web-ui/styles/sessions-usage.css +9 -0
  54. package/web-ui/styles.css +1 -0
  55. package/res/logo.png +0 -0
@@ -271,53 +271,164 @@ export function createCodexConfigMethods(options = {}) {
271
271
  });
272
272
  },
273
273
 
274
- async runHealthCheck() {
274
+ async runHealthCheck(options = {}) {
275
275
  this.healthCheckLoading = true;
276
276
  this.healthCheckResult = null;
277
- let shouldRunClaudeSpeedTests = false;
277
+ this.healthCheckBatchTotal = 0;
278
+ this.healthCheckBatchDone = 0;
279
+ this.healthCheckBatchFailed = 0;
278
280
  try {
279
- const res = await api('config-health-check', {
280
- remote: this.configMode === 'codex'
281
- });
281
+ const silent = !!(options && options.silent);
282
+ const forceRefresh = !!(options && options.forceRefresh);
283
+ const useDoctor = !!(options && options.doctor);
284
+ if (useDoctor) {
285
+ const res = await api('doctor', {
286
+ lang: this.lang,
287
+ remote: true,
288
+ range: this.sessionsUsageTimeRange,
289
+ targetApp: this.skillsTargetApp,
290
+ includeUsage: true,
291
+ includeTasks: true,
292
+ includeSkills: true,
293
+ includeInstall: true,
294
+ forceRefresh
295
+ });
296
+ if (hasResponseError(res)) {
297
+ this.healthCheckResult = null;
298
+ if (!silent) {
299
+ this.showMessage(getResponseMessage(res, '检查失败'), 'error');
300
+ }
301
+ return;
302
+ }
303
+ if (res && typeof res === 'object') {
304
+ this.healthCheckResult = res;
305
+ const report = res.report && typeof res.report === 'object' ? res.report : null;
306
+ const summary = report && report.summary && typeof report.summary === 'object' ? report.summary : null;
307
+ const total = summary && Number.isFinite(Number(summary.total))
308
+ ? Math.max(0, Math.floor(Number(summary.total)))
309
+ : (Array.isArray(res.issues) ? res.issues.length : 0);
310
+ const errors = summary && Number.isFinite(Number(summary.error)) ? Math.max(0, Math.floor(Number(summary.error))) : 0;
311
+ const warns = summary && Number.isFinite(Number(summary.warn)) ? Math.max(0, Math.floor(Number(summary.warn))) : 0;
312
+ this.healthCheckBatchTotal = total;
313
+ this.healthCheckBatchDone = total;
314
+ this.healthCheckBatchFailed = errors + warns;
315
+ if (!silent && res.ok) {
316
+ this.showMessage('检查通过', 'success');
317
+ }
318
+ return;
319
+ }
320
+ this.healthCheckResult = null;
321
+ if (!silent) {
322
+ this.showMessage('检查失败', 'error');
323
+ }
324
+ return;
325
+ }
326
+
327
+ if (this.configMode === 'claude') {
328
+ const entries = Object.entries(this.claudeConfigs || {});
329
+ this.healthCheckBatchTotal = entries.length;
330
+
331
+ const speedTasks = entries.map(([name, config]) => this.runClaudeSpeedTest(name, config)
332
+ .then((result) => {
333
+ if (!result || result.ok !== true) {
334
+ this.healthCheckBatchFailed += 1;
335
+ }
336
+ return { name, result };
337
+ })
338
+ .catch((err) => {
339
+ this.healthCheckBatchFailed += 1;
340
+ return {
341
+ name,
342
+ result: { ok: false, error: err && err.message ? err.message : 'Speed test failed' }
343
+ };
344
+ })
345
+ .finally(() => {
346
+ this.healthCheckBatchDone += 1;
347
+ })
348
+ );
349
+
350
+ const pairs = await Promise.all(speedTasks);
351
+ const results = {};
352
+ const issues = [];
353
+ for (const pair of pairs) {
354
+ results[pair.name] = pair.result || null;
355
+ if (typeof this.buildSpeedTestIssue === 'function') {
356
+ const issue = this.buildSpeedTestIssue(pair.name, pair.result);
357
+ if (issue) issues.push(issue);
358
+ }
359
+ }
360
+ const ok = issues.length === 0 && this.healthCheckBatchFailed === 0;
361
+ this.healthCheckResult = {
362
+ ok,
363
+ issues,
364
+ remote: {
365
+ type: 'speed-test',
366
+ speedTests: results
367
+ }
368
+ };
369
+ if (ok && !silent) {
370
+ this.showMessage('检查通过', 'success');
371
+ }
372
+ return;
373
+ }
374
+
375
+ const shouldRunSpeedTests = this.configMode === 'codex';
376
+ const speedTimeoutMs = shouldRunSpeedTests ? 3500 : 0;
377
+ const providers = shouldRunSpeedTests
378
+ ? (this.providersList || [])
379
+ .map((provider) => typeof provider === 'string'
380
+ ? provider.trim()
381
+ : String((provider && provider.name) || '').trim())
382
+ .filter(Boolean)
383
+ : [];
384
+ const currentProvider = String(this.currentProvider || '').trim();
385
+ const orderedProviders = currentProvider && providers.includes(currentProvider)
386
+ ? [currentProvider, ...providers.filter((name) => name !== currentProvider)]
387
+ : providers;
388
+ this.healthCheckBatchTotal = orderedProviders.length;
389
+
390
+ const speedTasks = orderedProviders.map((provider) => this.runSpeedTest(provider, { silent: true, timeoutMs: speedTimeoutMs })
391
+ .then((result) => {
392
+ if (!result || result.ok !== true) {
393
+ this.healthCheckBatchFailed += 1;
394
+ }
395
+ return { name: provider, result };
396
+ })
397
+ .catch((err) => {
398
+ this.healthCheckBatchFailed += 1;
399
+ return {
400
+ name: provider,
401
+ result: { ok: false, error: err && err.message ? err.message : 'Speed test failed' }
402
+ };
403
+ })
404
+ .finally(() => {
405
+ this.healthCheckBatchDone += 1;
406
+ })
407
+ );
408
+
409
+ const configTask = api('config-health-check', { remote: this.configMode === 'codex' });
410
+ const [res, pairs] = await Promise.all([
411
+ configTask,
412
+ Promise.all(speedTasks)
413
+ ]);
282
414
  if (hasResponseError(res)) {
283
415
  this.healthCheckResult = null;
284
- this.showMessage(getResponseMessage(res, '检查失败'), 'error');
416
+ if (!silent) {
417
+ this.showMessage(getResponseMessage(res, '检查失败'), 'error');
418
+ }
285
419
  } else if (res && typeof res === 'object') {
286
- shouldRunClaudeSpeedTests = true;
287
420
  const issues = Array.isArray(res.issues) ? [...res.issues] : [];
288
421
  let remote = res.remote || null;
289
- {
290
- const providers = (this.providersList || [])
291
- .map((provider) => typeof provider === 'string'
292
- ? provider.trim()
293
- : String((provider && provider.name) || '').trim())
294
- .filter(Boolean);
295
- const tasks = providers.map(provider =>
296
- this.runSpeedTest(provider, { silent: true })
297
- .then(result => ({ name: provider, result }))
298
- .catch(err => ({
299
- name: provider,
300
- result: { ok: false, error: err && err.message ? err.message : 'Speed test failed' }
301
- }))
302
- );
303
- const pairs = await Promise.all(tasks);
422
+ if (shouldRunSpeedTests) {
304
423
  const results = {};
305
424
  for (const pair of pairs) {
306
425
  results[pair.name] = pair.result || null;
307
426
  const issue = this.buildSpeedTestIssue(pair.name, pair.result);
308
427
  if (issue) issues.push(issue);
309
428
  }
310
- if (remote && typeof remote === 'object') {
311
- remote = {
312
- ...remote,
313
- speedTests: results
314
- };
315
- } else {
316
- remote = {
317
- type: 'speed-test',
318
- speedTests: results
319
- };
320
- }
429
+ remote = remote && typeof remote === 'object'
430
+ ? { ...remote, speedTests: results }
431
+ : { type: 'speed-test', speedTests: results };
321
432
  }
322
433
 
323
434
  const ok = issues.length === 0;
@@ -327,146 +438,24 @@ export function createCodexConfigMethods(options = {}) {
327
438
  issues,
328
439
  remote
329
440
  };
330
- if (ok) {
441
+ if (ok && !silent) {
331
442
  this.showMessage('检查通过', 'success');
332
443
  }
333
444
  } else {
334
445
  this.healthCheckResult = null;
335
- this.showMessage('检查失败', 'error');
446
+ if (!silent) {
447
+ this.showMessage('检查失败', 'error');
448
+ }
336
449
  }
337
450
  } catch (e) {
338
451
  this.healthCheckResult = null;
339
- this.showMessage('检查失败', 'error');
340
- } finally {
341
- if (shouldRunClaudeSpeedTests && this.configMode === 'claude') {
342
- try {
343
- const entries = Object.entries(this.claudeConfigs || {});
344
- await Promise.all(entries.map(([name, config]) => this.runClaudeSpeedTest(name, config)));
345
- } catch (e) {}
346
- }
347
- this.healthCheckLoading = false;
348
- }
349
- },
350
-
351
- buildDefaultHealthCheckPrompt() {
352
- return '请简短回复:连接正常。';
353
- },
354
-
355
- openHealthCheckDialog(options = {}) {
356
- const providerName = typeof options.providerName === 'string'
357
- ? options.providerName.trim()
358
- : '';
359
- const locked = !!options.locked && !!providerName;
360
- if (locked && providerName && providerName !== String(this.currentProvider || '').trim()) {
361
- if (typeof this.showMessage === 'function') {
362
- this.showMessage('请先切换到该提供商再进行健康聊天测试', 'info');
363
- }
364
- return;
365
- }
366
- const nextProvider = providerName
367
- || String(this.healthCheckDialogSelectedProvider || '').trim()
368
- || String(this.currentProvider || '').trim()
369
- || String(((this.displayProvidersList || [])[0] || {}).name || '').trim();
370
-
371
- this.showHealthCheckDialog = true;
372
- this.healthCheckDialogLockedProvider = locked ? nextProvider : '';
373
- this.healthCheckDialogSelectedProvider = nextProvider;
374
- this.healthCheckDialogPrompt = this.buildDefaultHealthCheckPrompt();
375
- this.healthCheckDialogMessages = [];
376
- this.healthCheckDialogLastResult = null;
377
- },
378
-
379
- closeHealthCheckDialog(options = {}) {
380
- if (this.healthCheckDialogSending && !options.force) {
381
- return;
382
- }
383
- this.showHealthCheckDialog = false;
384
- this.healthCheckDialogLockedProvider = '';
385
- this.healthCheckDialogSelectedProvider = '';
386
- this.healthCheckDialogPrompt = this.buildDefaultHealthCheckPrompt();
387
- this.healthCheckDialogMessages = [];
388
- this.healthCheckDialogLastResult = null;
389
- },
390
-
391
- async sendHealthCheckDialogMessage() {
392
- if (this.healthCheckDialogSending) {
393
- return;
394
- }
395
-
396
- const provider = String(
397
- this.healthCheckDialogLockedProvider || this.healthCheckDialogSelectedProvider || ''
398
- ).trim();
399
- const prompt = String(this.healthCheckDialogPrompt || '').trim();
400
- if (!provider) {
401
- this.showMessage('请先选择提供商', 'error');
402
- return;
403
- }
404
- if (!prompt) {
405
- this.showMessage('请输入消息内容', 'error');
406
- return;
407
- }
408
-
409
- this.healthCheckDialogMessages.push({
410
- id: `user-${Date.now()}`,
411
- role: 'user',
412
- text: prompt
413
- });
414
- this.healthCheckDialogSending = true;
415
- this.healthCheckDialogLastResult = null;
416
-
417
- try {
418
- const res = await api('provider-chat-check', {
419
- name: provider,
420
- prompt
421
- });
422
- this.healthCheckDialogLastResult = res;
423
-
424
- if (hasResponseError(res) || res.ok === false) {
425
- const message = getResponseMessage(res, '健康聊天测试失败');
426
- this.healthCheckDialogMessages.push({
427
- id: `assistant-${Date.now()}`,
428
- role: 'assistant',
429
- text: message,
430
- ok: false,
431
- status: Number.isFinite(res && res.status) ? res.status : 0,
432
- durationMs: Number.isFinite(res && res.durationMs) ? res.durationMs : 0,
433
- model: typeof (res && res.model) === 'string' ? res.model : '',
434
- rawPreview: typeof (res && res.rawPreview) === 'string' ? res.rawPreview : ''
435
- });
436
- this.showMessage(message, 'error');
437
- return;
452
+ if (!(options && options.silent)) {
453
+ this.showMessage('检查失败', 'error');
438
454
  }
439
-
440
- const reply = typeof res.reply === 'string' && res.reply.trim()
441
- ? res.reply.trim()
442
- : '已收到回复,但未解析到可展示文本。';
443
- this.healthCheckDialogMessages.push({
444
- id: `assistant-${Date.now()}`,
445
- role: 'assistant',
446
- text: reply,
447
- ok: true,
448
- status: Number.isFinite(res.status) ? res.status : 0,
449
- durationMs: Number.isFinite(res.durationMs) ? res.durationMs : 0,
450
- model: typeof res.model === 'string' ? res.model : '',
451
- rawPreview: typeof res.rawPreview === 'string' ? res.rawPreview : ''
452
- });
453
- this.healthCheckDialogPrompt = '';
454
- } catch (e) {
455
- const message = e && e.message ? e.message : '健康聊天测试失败';
456
- this.healthCheckDialogMessages.push({
457
- id: `assistant-${Date.now()}`,
458
- role: 'assistant',
459
- text: message,
460
- ok: false,
461
- status: 0,
462
- durationMs: 0,
463
- model: '',
464
- rawPreview: ''
465
- });
466
- this.healthCheckDialogLastResult = { ok: false, error: message };
467
- this.showMessage(message, 'error');
468
455
  } finally {
469
- this.healthCheckDialogSending = false;
456
+ this.healthCheckBatchTotal = this.healthCheckBatchTotal || 0;
457
+ this.healthCheckBatchDone = Math.min(this.healthCheckBatchDone || 0, this.healthCheckBatchTotal || 0);
458
+ this.healthCheckLoading = false;
470
459
  }
471
460
  },
472
461
 
@@ -6,6 +6,7 @@ export function createNavigationMethods(options = {}) {
6
6
  } = options;
7
7
  const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
8
8
  const MAIN_TAB_SET = new Set([
9
+ 'dashboard',
9
10
  'config',
10
11
  'sessions',
11
12
  'usage',
@@ -15,6 +16,29 @@ export function createNavigationMethods(options = {}) {
15
16
  'docs',
16
17
  'settings'
17
18
  ]);
19
+ const loadDoctorOverview = async (vm, options = {}) => {
20
+ if (!vm || typeof vm !== 'object') return false;
21
+ if (vm.__doctorLoading) return false;
22
+ const forceRefresh = !!(options && options.forceRefresh);
23
+ vm.__doctorLoading = true;
24
+ let ok = true;
25
+ try {
26
+ if (typeof vm.runHealthCheck === 'function') {
27
+ await vm.runHealthCheck({ doctor: true, silent: true, forceRefresh });
28
+ }
29
+ vm.__doctorLoadedOnce = true;
30
+ return true;
31
+ } catch (_) {
32
+ ok = false;
33
+ vm.__doctorLoadedOnce = true;
34
+ return false;
35
+ } finally {
36
+ vm.__doctorLoading = false;
37
+ if (!ok) {
38
+ vm.__doctorLoadedOnce = true;
39
+ }
40
+ }
41
+ };
18
42
  const readNavState = () => {
19
43
  if (typeof localStorage === 'undefined') return null;
20
44
  let raw = '';
@@ -37,7 +61,7 @@ export function createNavigationMethods(options = {}) {
37
61
  const mainTab = typeof vm.mainTab === 'string' ? vm.mainTab.trim().toLowerCase() : '';
38
62
  const configMode = typeof vm.configMode === 'string' ? vm.configMode.trim().toLowerCase() : '';
39
63
  const snapshot = {
40
- mainTab: MAIN_TAB_SET.has(mainTab) ? mainTab : 'docs',
64
+ mainTab: MAIN_TAB_SET.has(mainTab) ? mainTab : 'dashboard',
41
65
  configMode: configModeSet && configModeSet.has(configMode) ? configMode : 'codex'
42
66
  };
43
67
  try {
@@ -341,6 +365,9 @@ export function createNavigationMethods(options = {}) {
341
365
  if (targetTab === previousTab) {
342
366
  switchState.ticket += 1;
343
367
  switchState.pendingTarget = '';
368
+ if (targetTab === 'dashboard' && !this.__doctorLoadedOnce) {
369
+ void loadDoctorOverview(this);
370
+ }
344
371
  if (
345
372
  targetTab === 'sessions'
346
373
  && typeof this.prepareSessionTabRender === 'function'
@@ -366,6 +393,9 @@ export function createNavigationMethods(options = {}) {
366
393
  switchState.pendingTarget = '';
367
394
  const result = switchMainTabHelper.call(this, targetTab);
368
395
  persistNavState(this);
396
+ if (targetTab === 'dashboard') {
397
+ void loadDoctorOverview(this);
398
+ }
369
399
  this.scheduleAfterFrame(() => {
370
400
  this.clearMainTabSwitchIntent(normalizedTab);
371
401
  });
@@ -381,6 +411,9 @@ export function createNavigationMethods(options = {}) {
381
411
  liveState.pendingTarget = '';
382
412
  switchMainTabHelper.call(this, pendingTarget);
383
413
  persistNavState(this);
414
+ if (pendingTarget === 'dashboard') {
415
+ void loadDoctorOverview(this);
416
+ }
384
417
  this.clearMainTabSwitchIntent(normalizedTab);
385
418
  });
386
419
  },
@@ -37,7 +37,12 @@ export function createRuntimeMethods(options = {}) {
37
37
  const silent = !!options.silent;
38
38
  this.speedLoading[name] = true;
39
39
  try {
40
- const res = await api('speed-test', { name });
40
+ const timeoutMs = Number.isFinite(options.timeoutMs) ? Math.max(1000, Number(options.timeoutMs)) : 0;
41
+ const payload = { name };
42
+ if (timeoutMs) {
43
+ payload.timeoutMs = timeoutMs;
44
+ }
45
+ const res = await api('speed-test', payload);
41
46
  if (res.error) {
42
47
  this.speedResults[name] = { ok: false, error: res.error };
43
48
  if (!silent) {
@@ -66,6 +71,8 @@ export function createRuntimeMethods(options = {}) {
66
71
  async runClaudeSpeedTest(name, config) {
67
72
  if (!name || this.claudeSpeedLoading[name]) return null;
68
73
  const baseUrl = config && typeof config.baseUrl === 'string' ? config.baseUrl.trim() : '';
74
+ const apiKey = config && typeof config.apiKey === 'string' ? config.apiKey.trim() : '';
75
+ const model = config && typeof config.model === 'string' ? config.model.trim() : '';
69
76
  this.claudeSpeedLoading[name] = true;
70
77
  try {
71
78
  if (!baseUrl) {
@@ -73,7 +80,22 @@ export function createRuntimeMethods(options = {}) {
73
80
  this.claudeSpeedResults[name] = res;
74
81
  return res;
75
82
  }
76
- const res = await api('speed-test', { url: baseUrl });
83
+ if (!apiKey) {
84
+ const res = { ok: false, error: 'Missing API key' };
85
+ this.claudeSpeedResults[name] = res;
86
+ return res;
87
+ }
88
+ if (!model) {
89
+ const res = { ok: false, error: 'Missing model' };
90
+ this.claudeSpeedResults[name] = res;
91
+ return res;
92
+ }
93
+ const res = await api('speed-test', {
94
+ kind: 'claude',
95
+ url: baseUrl,
96
+ apiKey,
97
+ model
98
+ });
77
99
  if (res.error) {
78
100
  this.claudeSpeedResults[name] = { ok: false, error: res.error };
79
101
  return { ok: false, error: res.error };
@@ -442,12 +442,19 @@ export function createSessionBrowserMethods(options = {}) {
442
442
  await this.onSessionSourceChange();
443
443
  },
444
444
 
445
- copySessionsFilterShareUrl() {
445
+ async copySessionsFilterShareUrl() {
446
446
  const url = buildSessionsFilterShareUrl(this);
447
447
  if (!url) {
448
- this.showMessage('无法生成链接', 'error');
448
+ this.showMessage(typeof this.t === 'function' ? this.t('sessions.filters.urlBuildFail') : 'Failed to build link', 'error');
449
449
  return;
450
450
  }
451
+ try {
452
+ if (navigator.clipboard && window.isSecureContext) {
453
+ await navigator.clipboard.writeText(url);
454
+ this.showMessage(typeof this.t === 'function' ? this.t('toast.copy.ok') : 'Copied', 'success');
455
+ return;
456
+ }
457
+ } catch (_) {}
451
458
  const ok = typeof this.fallbackCopyText === 'function' ? this.fallbackCopyText(url) : false;
452
459
  if (ok) {
453
460
  this.showMessage(typeof this.t === 'function' ? this.t('toast.copy.ok') : 'Copied', 'success');
@@ -47,6 +47,7 @@ export function createConfigModeComputed() {
47
47
  return `${this.activeProviderModeLabel} 当前复用 Codex Provider / Model 管理链路。`;
48
48
  },
49
49
  inspectorMainTabLabel() {
50
+ if (this.mainTab === 'dashboard') return '概览';
50
51
  if (this.mainTab === 'config') return '配置中心';
51
52
  if (this.mainTab === 'sessions') return '会话浏览';
52
53
  if (this.mainTab === 'usage') return 'Usage';
@@ -56,7 +57,6 @@ export function createConfigModeComputed() {
56
57
  return '未知';
57
58
  },
58
59
  inspectorConfigModeLabel() {
59
- if (this.mainTab !== 'config') return '--';
60
60
  const providerMeta = getProviderConfigModeMeta(this.configMode);
61
61
  if (providerMeta) return providerMeta.label;
62
62
  if (this.configMode === 'claude') return 'Claude Code';
@@ -64,7 +64,6 @@ export function createConfigModeComputed() {
64
64
  return '未选择';
65
65
  },
66
66
  inspectorCurrentConfigLabel() {
67
- if (this.mainTab !== 'config') return '--';
68
67
  if (getProviderConfigModeMeta(this.configMode)) {
69
68
  const provider = typeof this.currentProvider === 'string' ? this.currentProvider.trim() : '';
70
69
  return provider || '未选择';
@@ -80,7 +79,6 @@ export function createConfigModeComputed() {
80
79
  return '未选择';
81
80
  },
82
81
  inspectorCurrentModelLabel() {
83
- if (this.mainTab !== 'config') return '--';
84
82
  if (getProviderConfigModeMeta(this.configMode)) {
85
83
  const model = typeof this.currentModel === 'string' ? this.currentModel.trim() : '';
86
84
  return model || '未选择';