codexmate 0.0.22 → 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 (66) hide show
  1. package/README.md +5 -3
  2. package/README.zh.md +8 -5
  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 +68 -34
  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 +9 -2
  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.install.mjs +16 -0
  30. package/web-ui/modules/app.methods.navigation.mjs +76 -0
  31. package/web-ui/modules/app.methods.runtime.mjs +24 -2
  32. package/web-ui/modules/app.methods.session-browser.mjs +73 -1
  33. package/web-ui/modules/app.methods.startup-claude.mjs +12 -0
  34. package/web-ui/modules/app.methods.task-orchestration.mjs +96 -11
  35. package/web-ui/modules/config-mode.computed.mjs +1 -3
  36. package/web-ui/modules/i18n.dict.mjs +2039 -0
  37. package/web-ui/modules/i18n.mjs +2 -1555
  38. package/web-ui/modules/plugins.computed.mjs +2 -219
  39. package/web-ui/modules/plugins.methods.mjs +2 -619
  40. package/web-ui/modules/plugins.storage.mjs +11 -37
  41. package/web-ui/modules/sessions-filters-url.mjs +85 -0
  42. package/web-ui/partials/index/layout-header.html +38 -34
  43. package/web-ui/partials/index/modal-config-template-agents.html +3 -4
  44. package/web-ui/partials/index/modal-health-check.html +33 -60
  45. package/web-ui/partials/index/panel-config-claude.html +56 -15
  46. package/web-ui/partials/index/panel-config-codex.html +68 -19
  47. package/web-ui/partials/index/panel-config-openclaw.html +8 -3
  48. package/web-ui/partials/index/panel-dashboard.html +186 -0
  49. package/web-ui/partials/index/panel-docs.html +1 -1
  50. package/web-ui/partials/index/panel-market.html +3 -0
  51. package/web-ui/partials/index/panel-orchestration.html +105 -111
  52. package/web-ui/partials/index/panel-plugins.html +48 -12
  53. package/web-ui/partials/index/panel-sessions.html +12 -3
  54. package/web-ui/partials/index/panel-settings.html +1 -1
  55. package/web-ui/partials/index/panel-usage.html +7 -6
  56. package/web-ui/styles/controls-forms.css +16 -2
  57. package/web-ui/styles/dashboard.css +274 -0
  58. package/web-ui/styles/layout-shell.css +11 -5
  59. package/web-ui/styles/navigation-panels.css +8 -0
  60. package/web-ui/styles/plugins-panel.css +5 -0
  61. package/web-ui/styles/sessions-list.css +3 -3
  62. package/web-ui/styles/sessions-usage.css +37 -0
  63. package/web-ui/styles/skills-market.css +12 -2
  64. package/web-ui/styles/task-orchestration.css +57 -11
  65. package/web-ui/styles.css +1 -0
  66. package/res/logo.png +0 -0
@@ -0,0 +1,90 @@
1
+ import {
2
+ persistPromptTemplatesToStorage,
3
+ readPromptTemplatesFromStorage,
4
+ readPromptTemplateSelectedIdFromStorage,
5
+ persistPromptTemplateSelectedIdToStorage
6
+ } from './storage.mjs';
7
+ import { buildBuiltinCommentPolishTemplate } from './comment-polish/index.mjs';
8
+ import { buildBuiltinRuleAckTemplate } from './rule-ack/index.mjs';
9
+
10
+ function ensureBuiltinTemplates(rawList, builtins) {
11
+ const list = Array.isArray(rawList) ? rawList.filter(Boolean) : [];
12
+ const builtinList = Array.isArray(builtins) ? builtins.filter(Boolean) : [];
13
+ const rest = list.filter((item) => !(item && item.isBuiltin === true));
14
+ const overridden = new Set(
15
+ rest
16
+ .map((item) => (item && typeof item.id === 'string' ? item.id.trim() : ''))
17
+ .filter(Boolean)
18
+ );
19
+ const resolvedBuiltins = builtinList.filter((item) => !(item && overridden.has(item.id)));
20
+ return [...resolvedBuiltins, ...rest];
21
+ }
22
+
23
+ export async function loadPromptTemplatesOverview(ctx, options = {}) {
24
+ const app = ctx && typeof ctx === 'object' ? ctx : {};
25
+ const silent = !!(options && options.silent);
26
+ const forceRefresh = !!(options && options.forceRefresh);
27
+
28
+ const shouldReload = forceRefresh || app.promptTemplatesLoadedOnce !== true;
29
+ if (!shouldReload) return true;
30
+
31
+ const t = typeof app.t === 'function' ? app.t : null;
32
+ const rawList = readPromptTemplatesFromStorage(localStorage);
33
+ const normalized = ensureBuiltinTemplates(rawList, [
34
+ buildBuiltinCommentPolishTemplate(t),
35
+ buildBuiltinRuleAckTemplate(t)
36
+ ]);
37
+ app.promptTemplatesListRaw = normalized;
38
+ persistPromptTemplatesToStorage(normalized, localStorage);
39
+
40
+ app.promptTemplatesLoadedOnce = true;
41
+
42
+ if (!app.promptTemplatesMode) {
43
+ app.promptTemplatesMode = 'compose';
44
+ }
45
+ if (app.promptTemplatesMode !== 'compose' && app.promptTemplatesMode !== 'manage') {
46
+ app.promptTemplatesMode = 'compose';
47
+ }
48
+ if (app.mainTab === 'plugins') {
49
+ app.promptTemplatesMode = 'compose';
50
+ }
51
+
52
+ if (app.mainTab === 'plugins' && app.promptTemplatesMode === 'compose') {
53
+ const list = Array.isArray(app.promptTemplatesList) ? app.promptTemplatesList : [];
54
+ const storedSelected = readPromptTemplateSelectedIdFromStorage(localStorage);
55
+ const exists = list.some((item) => item && item.id === app.promptComposerSelectedTemplateId);
56
+ const storedExists = storedSelected ? list.some((item) => item && item.id === storedSelected) : false;
57
+ if (storedExists && storedSelected !== app.promptComposerSelectedTemplateId) {
58
+ app.promptComposerSelectedTemplateId = storedSelected;
59
+ } else if (!app.promptComposerSelectedTemplateId || !exists) {
60
+ app.promptComposerSelectedTemplateId = 'builtin_comment_polish';
61
+ }
62
+ persistPromptTemplateSelectedIdToStorage(app.promptComposerSelectedTemplateId, localStorage);
63
+ if (!app.promptComposerVarValuesRaw || typeof app.promptComposerVarValuesRaw !== 'object') {
64
+ app.promptComposerVarValuesRaw = {};
65
+ }
66
+ }
67
+
68
+ if (app.promptTemplatesMode === 'manage') {
69
+ const currentSelected = typeof app.promptTemplateSelectedId === 'string'
70
+ ? app.promptTemplateSelectedId
71
+ : '';
72
+ const first = Array.isArray(app.promptTemplatesList) && app.promptTemplatesList.length
73
+ ? app.promptTemplatesList[0]
74
+ : null;
75
+ if (!currentSelected && first && typeof app.selectPromptTemplate === 'function') {
76
+ app.selectPromptTemplate(first.id);
77
+ }
78
+ }
79
+
80
+ if (app.mainTab === 'plugins' && app.promptTemplatesMode === 'compose' && typeof app.$nextTick === 'function') {
81
+ app.$nextTick(() => {
82
+ const input = app.$refs && app.$refs.promptComposerCodeInput
83
+ ? app.$refs.promptComposerCodeInput
84
+ : null;
85
+ if (input && typeof input.focus === 'function') input.focus();
86
+ });
87
+ }
88
+
89
+ return true;
90
+ }
@@ -0,0 +1,19 @@
1
+ export const pluginOwnership = {
2
+ pluginId: 'prompt-templates',
3
+ createdBy: 'ymkiux',
4
+ maintainers: ['ymkiux']
5
+ };
6
+
7
+ export const templateOwnershipById = {
8
+ builtin_comment_polish: {
9
+ templateId: 'builtin_comment_polish',
10
+ createdBy: 'ymkiux',
11
+ maintainers: ['ymkiux']
12
+ },
13
+ builtin_rule_ack: {
14
+ templateId: 'builtin_rule_ack',
15
+ createdBy: 'ymkiux',
16
+ maintainers: ['ymkiux']
17
+ }
18
+ };
19
+
@@ -0,0 +1,21 @@
1
+ import { pluginOwnership, templateOwnershipById } from '../ownership.mjs';
2
+
3
+ export function buildBuiltinRuleAckTemplate(t) {
4
+ const tr = (key, fallback, params = null) => (typeof t === 'function' ? t(key, params) : fallback);
5
+ const line1 = tr('plugins.builtin.ruleAck.line1', '请根据【{{rule}}】,收到请回复');
6
+ const timestamp = new Date().toISOString();
7
+ const ownership = templateOwnershipById && templateOwnershipById.builtin_rule_ack
8
+ ? templateOwnershipById.builtin_rule_ack
9
+ : pluginOwnership;
10
+ return {
11
+ id: 'builtin_rule_ack',
12
+ name: tr('plugins.builtin.ruleAck.name', '规则确认回复'),
13
+ description: tr('plugins.builtin.ruleAck.desc', '请根据【{{rule}}】,收到请回复'),
14
+ template: line1,
15
+ createdAt: timestamp,
16
+ updatedAt: timestamp,
17
+ isBuiltin: true,
18
+ createdBy: ownership && typeof ownership.createdBy === 'string' ? ownership.createdBy : '',
19
+ maintainers: ownership && Array.isArray(ownership.maintainers) ? ownership.maintainers : []
20
+ };
21
+ }
@@ -0,0 +1,64 @@
1
+ const STORAGE_KEY = 'codexmate.plugins.promptTemplates.v1';
2
+ const SELECTED_TEMPLATE_STORAGE_KEY = 'codexmate.plugins.promptTemplates.selectedTemplateId.v1';
3
+
4
+ export function readPromptTemplatesFromStorage(storage = localStorage) {
5
+ if (!storage) return [];
6
+ let raw = '';
7
+ try {
8
+ raw = storage.getItem(STORAGE_KEY) || '';
9
+ } catch (_) {
10
+ raw = '';
11
+ }
12
+ if (!raw) return [];
13
+ try {
14
+ const parsed = JSON.parse(raw);
15
+ if (!Array.isArray(parsed)) return [];
16
+ return parsed;
17
+ } catch (_) {
18
+ return [];
19
+ }
20
+ }
21
+
22
+ export function persistPromptTemplatesToStorage(list, storage = localStorage) {
23
+ if (!storage) return false;
24
+ try {
25
+ storage.setItem(STORAGE_KEY, JSON.stringify(Array.isArray(list) ? list : []));
26
+ return true;
27
+ } catch (_) {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ export function clearPromptTemplatesStorage(storage = localStorage) {
33
+ if (!storage) return;
34
+ try {
35
+ storage.removeItem(STORAGE_KEY);
36
+ } catch (_) {}
37
+ }
38
+
39
+ export function readPromptTemplateSelectedIdFromStorage(storage = localStorage) {
40
+ if (!storage) return '';
41
+ let raw = '';
42
+ try {
43
+ raw = storage.getItem(SELECTED_TEMPLATE_STORAGE_KEY) || '';
44
+ } catch (_) {
45
+ raw = '';
46
+ }
47
+ const id = typeof raw === 'string' ? raw.trim() : '';
48
+ return id;
49
+ }
50
+
51
+ export function persistPromptTemplateSelectedIdToStorage(templateId, storage = localStorage) {
52
+ if (!storage) return false;
53
+ const id = typeof templateId === 'string' ? templateId.trim() : '';
54
+ try {
55
+ if (!id) {
56
+ storage.removeItem(SELECTED_TEMPLATE_STORAGE_KEY);
57
+ return true;
58
+ }
59
+ storage.setItem(SELECTED_TEMPLATE_STORAGE_KEY, id);
60
+ return true;
61
+ } catch (_) {
62
+ return false;
63
+ }
64
+ }
@@ -0,0 +1,16 @@
1
+ import { pluginMeta as promptTemplatesMeta } from './prompt-templates/manifest.mjs';
2
+ import { loadPromptTemplatesOverview } from './prompt-templates/overview.mjs';
3
+
4
+ export const pluginsRegistry = [
5
+ { id: promptTemplatesMeta.id, meta: promptTemplatesMeta, loadOverview: loadPromptTemplatesOverview }
6
+ ];
7
+
8
+ export function getFirstPluginId() {
9
+ return pluginsRegistry.length ? pluginsRegistry[0].id : '';
10
+ }
11
+
12
+ export function getPluginEntry(id) {
13
+ const key = typeof id === 'string' ? id.trim() : '';
14
+ if (!key) return null;
15
+ return pluginsRegistry.find((item) => item && item.id === key) || null;
16
+ }
Binary file
package/web-ui/app.js CHANGED
@@ -30,8 +30,7 @@ document.addEventListener('DOMContentLoaded', () => {
30
30
  data() {
31
31
  return {
32
32
  lang: 'zh',
33
- // 默认选中首个主标签:Docs
34
- mainTab: 'docs',
33
+ mainTab: 'dashboard',
35
34
  configMode: 'codex',
36
35
  currentProvider: '',
37
36
  currentModel: '',
@@ -63,12 +62,13 @@ document.addEventListener('DOMContentLoaded', () => {
63
62
  showEditConfigModal: false,
64
63
  showOpenclawConfigModal: false,
65
64
  showConfigTemplateModal: false,
66
- showHealthCheckDialog: false,
67
65
  showAgentsModal: false,
68
66
  showSkillsModal: false,
67
+ showHealthCheckModal: false,
69
68
  // Plugins
70
69
  pluginsActiveId: 'prompt-templates',
71
70
  pluginsLoading: false,
71
+ pluginsError: '',
72
72
  promptTemplatesListRaw: [],
73
73
  promptTemplatesLoadedOnce: false,
74
74
  promptTemplatesKeyword: '',
@@ -161,6 +161,7 @@ document.addEventListener('DOMContentLoaded', () => {
161
161
  sessionsUsageTimeRange: '7d',
162
162
  sessionsUsageList: [],
163
163
  sessionsUsageLoadedOnce: false,
164
+ sessionsUsageLoadedLimit: 0,
164
165
  sessionsUsageLoading: false,
165
166
  sessionsUsageError: '',
166
167
  sessionsList: [],
@@ -243,38 +244,14 @@ document.addEventListener('DOMContentLoaded', () => {
243
244
  providerSwitchInProgress: false,
244
245
  pendingProviderSwitch: '',
245
246
  providerSwitchDisplayTarget: '',
246
- healthCheckDialogLockedProvider: '',
247
- healthCheckDialogSelectedProvider: '',
248
- healthCheckDialogPrompt: '请简短回复:连接正常。',
249
- healthCheckDialogMessages: [],
250
- healthCheckDialogSending: false,
251
- healthCheckDialogLastResult: null,
247
+ healthCheckBatchTotal: 0,
248
+ healthCheckBatchDone: 0,
249
+ healthCheckBatchFailed: 0,
252
250
  installPackageManager: 'npm',
253
251
  installCommandAction: 'install',
254
252
  installRegistryPreset: 'default',
255
253
  installRegistryCustom: '',
256
- installStatusTargets: [
257
- {
258
- id: 'claude',
259
- name: 'Claude Code CLI',
260
- packageName: '@anthropic-ai/claude-code',
261
- installed: false,
262
- bin: 'claude',
263
- version: '',
264
- commandPath: '',
265
- error: ''
266
- },
267
- {
268
- id: 'codex',
269
- name: 'Codex CLI',
270
- packageName: '@openai/codex',
271
- installed: false,
272
- bin: 'codex',
273
- version: '',
274
- commandPath: '',
275
- error: ''
276
- }
277
- ],
254
+ installStatusTargets: null,
278
255
  newProvider: { name: '', url: '', key: '', useTransform: false },
279
256
  resetConfigLoading: false,
280
257
  editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false },
@@ -382,7 +359,7 @@ document.addEventListener('DOMContentLoaded', () => {
382
359
  codexImportLoading: false,
383
360
  codexAuthProfiles: [],
384
361
  forceCompactLayout: false,
385
- taskOrchestrationTabEnabled: false,
362
+ taskOrchestrationTabEnabled: true,
386
363
  taskOrchestration: {
387
364
  loading: false,
388
365
  planning: false,
@@ -396,11 +373,11 @@ document.addEventListener('DOMContentLoaded', () => {
396
373
  followUpsText: '',
397
374
  workflowIdsText: '',
398
375
  selectedEngine: 'codex',
399
- allowWrite: false,
400
- dryRun: false,
376
+ runMode: 'write',
401
377
  concurrency: 2,
402
378
  autoFixRounds: 1,
403
379
  plan: null,
380
+ planFingerprint: '',
404
381
  planIssues: [],
405
382
  planWarnings: [],
406
383
  overviewWarnings: [],
@@ -430,6 +407,55 @@ document.addEventListener('DOMContentLoaded', () => {
430
407
  this.agentsModalTitle = this.t('modal.agents.title');
431
408
  this.agentsModalHint = this.t('modal.agents.hint');
432
409
  }
410
+ {
411
+ const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
412
+ const mainTabSet = new Set(['dashboard', 'config', 'sessions', 'usage', 'orchestration', 'market', 'plugins', 'docs', 'settings']);
413
+ let restored = null;
414
+ try {
415
+ const raw = localStorage.getItem(NAV_STATE_STORAGE_KEY) || '';
416
+ restored = raw ? JSON.parse(raw) : null;
417
+ } catch (_) {
418
+ restored = null;
419
+ }
420
+ const nextMainTab = restored && typeof restored.mainTab === 'string'
421
+ ? restored.mainTab.trim().toLowerCase()
422
+ : '';
423
+ const nextConfigMode = restored && typeof restored.configMode === 'string'
424
+ ? restored.configMode.trim().toLowerCase()
425
+ : '';
426
+ let urlMainTab = '';
427
+ try {
428
+ const url = new URL(window.location.href);
429
+ if (url.pathname !== '/session') {
430
+ urlMainTab = String(url.searchParams.get('tab') || '').trim().toLowerCase();
431
+ }
432
+ } catch (_) {
433
+ urlMainTab = '';
434
+ }
435
+ const resolvedMainTab = urlMainTab && mainTabSet.has(urlMainTab)
436
+ ? urlMainTab
437
+ : nextMainTab;
438
+ if (nextConfigMode && typeof this.switchConfigMode === 'function') {
439
+ this.__navStateRestoring = true;
440
+ try {
441
+ if (nextConfigMode === 'codex' || nextConfigMode === 'claude' || nextConfigMode === 'openclaw') {
442
+ this.configMode = nextConfigMode;
443
+ }
444
+ if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
445
+ this.switchMainTab(resolvedMainTab);
446
+ }
447
+ } finally {
448
+ this.__navStateRestoring = false;
449
+ }
450
+ } else if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
451
+ this.__navStateRestoring = true;
452
+ try {
453
+ this.switchMainTab(resolvedMainTab);
454
+ } finally {
455
+ this.__navStateRestoring = false;
456
+ }
457
+ }
458
+ }
433
459
  this.initSessionStandalone();
434
460
  this.updateCompactLayoutMode();
435
461
  if (!this.taskOrchestrationTabEnabled && this.mainTab === 'orchestration') {
@@ -506,6 +532,14 @@ document.addEventListener('DOMContentLoaded', () => {
506
532
  if (!startupOk) {
507
533
  return;
508
534
  }
535
+ if (this.mainTab === 'dashboard') {
536
+ if (!this.__doctorLoadedOnce) {
537
+ this.__doctorLoadedOnce = true;
538
+ if (typeof this.runHealthCheck === 'function') {
539
+ void this.runHealthCheck({ doctor: true, silent: true });
540
+ }
541
+ }
542
+ }
509
543
  void this.refreshClaudeSelectionFromSettings({ silent: true });
510
544
  void this.syncDefaultOpenclawConfigEntry({ silent: true });
511
545
  };
package/web-ui/index.html CHANGED
@@ -4,14 +4,15 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Codex Mate</title>
7
- <link rel="icon" type="image/png" href="/res/logo.png">
8
- <link rel="apple-touch-icon" href="/res/logo.png">
7
+ <link rel="icon" type="image/webp" href="/res/logo-pack.webp">
8
+ <link rel="apple-touch-icon" href="/res/logo-pack.webp">
9
9
  <script src="/res/vue.global.prod.js"></script>
10
10
  <script src="/res/json5.min.js"></script>
11
11
  <link rel="stylesheet" href="/web-ui/styles.css">
12
12
  </head>
13
13
  <body>
14
14
  <!-- @include ./partials/index/layout-header.html -->
15
+ <!-- @include ./partials/index/panel-dashboard.html -->
15
16
  <!-- @include ./partials/index/panel-config-codex.html -->
16
17
  <!-- @include ./partials/index/panel-config-claude.html -->
17
18
  <!-- @include ./partials/index/panel-config-openclaw.html -->
@@ -24,10 +25,10 @@
24
25
  <!-- @include ./partials/index/panel-plugins.html -->
25
26
  <!-- @include ./partials/index/layout-footer.html -->
26
27
  <!-- @include ./partials/index/modals-basic.html -->
27
- <!-- @include ./partials/index/modal-health-check.html -->
28
28
  <!-- @include ./partials/index/modal-openclaw-config.html -->
29
29
  <!-- @include ./partials/index/modal-config-template-agents.html -->
30
30
  <!-- @include ./partials/index/modal-skills.html -->
31
+ <!-- @include ./partials/index/modal-health-check.html -->
31
32
  <!-- @include ./partials/index/modal-confirm-toast.html -->
32
33
  <script type="module" src="/web-ui/app.js"></script>
33
34
  </body>
@@ -37,7 +37,7 @@ export function createDashboardComputed() {
37
37
  },
38
38
  displayProvidersList() {
39
39
  const list = Array.isArray(this.providersList) ? this.providersList : [];
40
- return list.filter((item) => String(item && item.name ? item.name : '').trim().toLowerCase() !== 'codexmate-proxy');
40
+ return list;
41
41
  },
42
42
  installTargetCards() {
43
43
  const targets = Array.isArray(this.installStatusTargets) ? this.installStatusTargets : [];
@@ -59,39 +59,39 @@ export function createDashboardComputed() {
59
59
  },
60
60
  inspectorBusyStatus() {
61
61
  const tasks = [];
62
- if (this.loading) tasks.push('初始化');
63
- if (this.sessionsLoading) tasks.push('会话加载');
64
- if (this.codexModelsLoading || this.claudeModelsLoading) tasks.push('模型加载');
65
- if (this.codexApplying || this.configTemplateApplying || this.openclawApplying) tasks.push('配置应用');
66
- if (this.agentsSaving) tasks.push('AGENTS 保存');
67
- if (this.skillsLoading || this.skillsDeleting || this.skillsScanningImports || this.skillsImporting || this.skillsZipImporting || this.skillsExporting) tasks.push('Skills 管理');
62
+ if (this.loading) tasks.push(this.t('dashboard.busy.init'));
63
+ if (this.sessionsLoading) tasks.push(this.t('dashboard.busy.sessions'));
64
+ if (this.codexModelsLoading || this.claudeModelsLoading) tasks.push(this.t('dashboard.busy.models'));
65
+ if (this.codexApplying || this.configTemplateApplying || this.openclawApplying) tasks.push(this.t('dashboard.busy.configApply'));
66
+ if (this.agentsSaving) tasks.push(this.t('dashboard.busy.agents'));
67
+ if (this.skillsLoading || this.skillsDeleting || this.skillsScanningImports || this.skillsImporting || this.skillsZipImporting || this.skillsExporting) tasks.push(this.t('dashboard.busy.skills'));
68
68
  if (this.taskOrchestration && (this.taskOrchestration.loading || this.taskOrchestration.planning || this.taskOrchestration.running || this.taskOrchestration.queueAdding || this.taskOrchestration.queueStarting || this.taskOrchestration.retrying || this.taskOrchestration.selectedRunLoading)) {
69
- tasks.push('任务编排');
69
+ tasks.push(this.t('dashboard.busy.tasks'));
70
70
  }
71
- return tasks.length ? tasks.join(' / ') : '空闲';
71
+ return tasks.length ? tasks.join(' / ') : this.t('dashboard.busy.idle');
72
72
  },
73
73
  inspectorMessageSummary() {
74
74
  const value = typeof this.message === 'string' ? this.message.trim() : '';
75
- return value || '暂无提示';
75
+ return value || this.t('dashboard.message.none');
76
76
  },
77
77
  inspectorSessionSourceLabel() {
78
- if (this.sessionFilterSource === 'codex') return 'Codex';
79
- if (this.sessionFilterSource === 'claude') return 'Claude Code';
80
- return '全部';
78
+ if (this.sessionFilterSource === 'codex') return this.t('dashboard.sessionSource.codex');
79
+ if (this.sessionFilterSource === 'claude') return this.t('dashboard.sessionSource.claude');
80
+ return this.t('dashboard.sessionSource.all');
81
81
  },
82
82
  inspectorSessionPathLabel() {
83
83
  const value = typeof this.sessionPathFilter === 'string' ? this.sessionPathFilter.trim() : '';
84
- return value || '全部路径';
84
+ return value || this.t('dashboard.sessionPath.all');
85
85
  },
86
86
  inspectorSessionQueryLabel() {
87
- if (!this.isSessionQueryEnabled) return '当前来源不支持';
87
+ if (!this.isSessionQueryEnabled) return this.t('dashboard.sessionQuery.unsupported');
88
88
  const value = typeof this.sessionQuery === 'string' ? this.sessionQuery.trim() : '';
89
- return value || '未设置';
89
+ return value || this.t('dashboard.sessionQuery.unset');
90
90
  },
91
91
  inspectorHealthStatus() {
92
- if (this.initError) return '读取失败';
93
- if (this.loading) return '初始化中';
94
- return '正常';
92
+ if (this.initError) return this.t('dashboard.healthStatus.failRead');
93
+ if (this.loading) return this.t('dashboard.healthStatus.initializing');
94
+ return this.t('dashboard.healthStatus.ok');
95
95
  },
96
96
  inspectorHealthTone() {
97
97
  if (this.initError) return 'error';
@@ -100,12 +100,12 @@ export function createDashboardComputed() {
100
100
  },
101
101
  inspectorModelLoadStatus() {
102
102
  if (this.codexModelsLoading || this.claudeModelsLoading) {
103
- return '加载中';
103
+ return this.t('dashboard.modelStatus.loading');
104
104
  }
105
105
  if (this.modelsSource === 'error' || this.claudeModelsSource === 'error') {
106
- return '加载异常';
106
+ return this.t('dashboard.modelStatus.error');
107
107
  }
108
- return '正常';
108
+ return this.t('dashboard.modelStatus.ok');
109
109
  },
110
110
  installTroubleshootingTips() {
111
111
  const platform = this.resolveInstallPlatform();
@@ -13,12 +13,16 @@ function readTaskOrchestrationDraftMetrics(taskOrchestration) {
13
13
  const workflowIds = normalizeTaskDraftLines(state.workflowIdsText);
14
14
  const followUps = normalizeTaskDraftLines(state.followUpsText);
15
15
  const engine = String(state.selectedEngine || 'codex').trim().toLowerCase() === 'workflow' ? 'workflow' : 'codex';
16
+ const runMode = String(state.runMode || 'write').trim().toLowerCase();
17
+ const allowWrite = runMode === 'write';
18
+ const dryRun = runMode === 'dry-run';
16
19
  const plan = state.plan && typeof state.plan === 'object' ? state.plan : null;
17
20
  const planNodes = Array.isArray(plan && plan.nodes) ? plan.nodes : [];
18
21
  const planIssues = Array.isArray(state.planIssues) ? state.planIssues : [];
19
22
  const planWarnings = Array.isArray(state.planWarnings) ? state.planWarnings : [];
20
23
  return {
21
24
  engine,
25
+ runMode,
22
26
  title,
23
27
  target,
24
28
  notes,
@@ -34,8 +38,8 @@ function readTaskOrchestrationDraftMetrics(taskOrchestration) {
34
38
  workflowCount: workflowIds.length,
35
39
  followUpCount: followUps.length,
36
40
  planNodeCount: planNodes.length,
37
- allowWrite: state.allowWrite === true,
38
- dryRun: state.dryRun === true
41
+ allowWrite,
42
+ dryRun
39
43
  };
40
44
  }
41
45
 
@@ -132,6 +136,7 @@ function createTaskDraftReadiness(metrics) {
132
136
  export function createMainTabsComputed() {
133
137
  return {
134
138
  mainTabKicker() {
139
+ if (this.mainTab === 'dashboard') return this.t('kicker.dashboard');
135
140
  if (this.mainTab === 'config') return this.t('kicker.config');
136
141
  if (this.mainTab === 'sessions') return this.t('kicker.sessions');
137
142
  if (this.mainTab === 'usage') return this.t('kicker.usage');
@@ -142,6 +147,7 @@ export function createMainTabsComputed() {
142
147
  return this.t('kicker.settings');
143
148
  },
144
149
  mainTabTitle() {
150
+ if (this.mainTab === 'dashboard') return this.t('title.dashboard');
145
151
  if (this.mainTab === 'config') return this.t('title.config');
146
152
  if (this.mainTab === 'sessions') return this.t('title.sessions');
147
153
  if (this.mainTab === 'usage') return this.t('title.usage');
@@ -152,6 +158,7 @@ export function createMainTabsComputed() {
152
158
  return this.t('title.settings');
153
159
  },
154
160
  mainTabSubtitle() {
161
+ if (this.mainTab === 'dashboard') return this.t('subtitle.dashboard');
155
162
  if (this.mainTab === 'config') return this.t('subtitle.config');
156
163
  if (this.mainTab === 'sessions') return this.t('subtitle.sessions');
157
164
  if (this.mainTab === 'usage') return this.t('subtitle.usage');