codexmate 0.0.39 → 0.0.41

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 (155) hide show
  1. package/README.md +168 -156
  2. package/README.zh.md +168 -156
  3. package/cli/agents-files.js +230 -230
  4. package/cli/analytics-export-args.js +68 -68
  5. package/cli/archive-helpers.js +453 -453
  6. package/cli/auth-profiles.js +375 -375
  7. package/cli/builtin-proxy.js +2144 -2144
  8. package/cli/claude-proxy.js +1022 -1022
  9. package/cli/config-bootstrap.js +407 -402
  10. package/cli/config-health.js +454 -454
  11. package/cli/doctor-core.js +903 -903
  12. package/cli/import-skills-url.js +356 -356
  13. package/cli/local-bridge.js +556 -556
  14. package/cli/openai-bridge.js +1984 -1984
  15. package/cli/openclaw-config.js +629 -629
  16. package/cli/session-convert-args.js +69 -69
  17. package/cli/session-convert-io.js +82 -82
  18. package/cli/session-convert.js +150 -150
  19. package/cli/session-usage.concurrent.js +28 -28
  20. package/cli/session-usage.js +304 -304
  21. package/cli/session-usage.models.js +176 -176
  22. package/cli/skills.js +1141 -1141
  23. package/cli/update.js +171 -171
  24. package/cli/zip-commands.js +510 -510
  25. package/cli.js +16458 -16174
  26. package/lib/automation.js +404 -404
  27. package/lib/cli-file-utils.js +151 -151
  28. package/lib/cli-models-utils.js +440 -440
  29. package/lib/cli-network-utils.js +190 -190
  30. package/lib/cli-path-utils.js +85 -85
  31. package/lib/cli-session-utils.js +121 -121
  32. package/lib/cli-sessions.js +427 -427
  33. package/lib/cli-utils.js +155 -155
  34. package/lib/cli-webhook.js +154 -154
  35. package/lib/download-artifacts.js +92 -92
  36. package/lib/mcp-stdio.js +453 -453
  37. package/lib/task-orchestrator.js +869 -869
  38. package/lib/text-diff.js +303 -303
  39. package/lib/win-tray.js +119 -119
  40. package/lib/workflow-engine.js +340 -340
  41. package/package.json +77 -77
  42. package/plugins/README.md +20 -20
  43. package/plugins/README.zh-CN.md +20 -20
  44. package/plugins/prompt-templates/comment-polish/index.mjs +25 -25
  45. package/plugins/prompt-templates/computed.mjs +311 -253
  46. package/plugins/prompt-templates/index.mjs +8 -8
  47. package/plugins/prompt-templates/manifest.mjs +18 -15
  48. package/plugins/prompt-templates/methods.mjs +553 -553
  49. package/plugins/prompt-templates/overview.mjs +91 -91
  50. package/plugins/prompt-templates/ownership.mjs +19 -19
  51. package/plugins/prompt-templates/rule-ack/index.mjs +21 -21
  52. package/plugins/prompt-templates/storage.mjs +64 -64
  53. package/plugins/registry.mjs +16 -16
  54. package/web-ui/app.js +695 -693
  55. package/web-ui/index.html +37 -37
  56. package/web-ui/logic.agents-diff.mjs +386 -386
  57. package/web-ui/logic.claude.mjs +172 -172
  58. package/web-ui/logic.codex.mjs +69 -69
  59. package/web-ui/logic.mjs +5 -5
  60. package/web-ui/logic.runtime.mjs +128 -128
  61. package/web-ui/logic.session-convert.mjs +70 -70
  62. package/web-ui/logic.sessions.mjs +782 -782
  63. package/web-ui/modules/api.mjs +90 -90
  64. package/web-ui/modules/app.computed.dashboard.mjs +252 -252
  65. package/web-ui/modules/app.computed.index.mjs +17 -17
  66. package/web-ui/modules/app.computed.main-tabs.mjs +214 -212
  67. package/web-ui/modules/app.computed.session.mjs +876 -876
  68. package/web-ui/modules/app.constants.mjs +15 -15
  69. package/web-ui/modules/app.methods.agents.mjs +651 -651
  70. package/web-ui/modules/app.methods.claude-config.mjs +412 -412
  71. package/web-ui/modules/app.methods.codex-config.mjs +869 -869
  72. package/web-ui/modules/app.methods.index.mjs +96 -94
  73. package/web-ui/modules/app.methods.install.mjs +205 -205
  74. package/web-ui/modules/app.methods.navigation.mjs +804 -804
  75. package/web-ui/modules/app.methods.openclaw-core.mjs +814 -814
  76. package/web-ui/modules/app.methods.openclaw-editing.mjs +420 -420
  77. package/web-ui/modules/app.methods.openclaw-persist.mjs +375 -375
  78. package/web-ui/modules/app.methods.providers.mjs +601 -601
  79. package/web-ui/modules/app.methods.runtime.mjs +420 -420
  80. package/web-ui/modules/app.methods.session-actions.mjs +591 -591
  81. package/web-ui/modules/app.methods.session-browser.mjs +1018 -1018
  82. package/web-ui/modules/app.methods.session-timeline.mjs +479 -479
  83. package/web-ui/modules/app.methods.session-trash.mjs +468 -468
  84. package/web-ui/modules/app.methods.startup-claude.mjs +554 -548
  85. package/web-ui/modules/app.methods.task-orchestration.mjs +556 -556
  86. package/web-ui/modules/app.methods.tool-config-permissions.mjs +87 -0
  87. package/web-ui/modules/app.methods.webhook.mjs +87 -87
  88. package/web-ui/modules/config-mode.computed.mjs +124 -124
  89. package/web-ui/modules/config-template-confirm-pref.mjs +33 -33
  90. package/web-ui/modules/i18n/locales/en.mjs +1140 -0
  91. package/web-ui/modules/i18n/locales/ja.mjs +1130 -0
  92. package/web-ui/modules/i18n/locales/vi.mjs +239 -0
  93. package/web-ui/modules/i18n/locales/zh.mjs +1143 -0
  94. package/web-ui/modules/i18n.dict.mjs +14 -3213
  95. package/web-ui/modules/i18n.mjs +111 -111
  96. package/web-ui/modules/plugins.computed.mjs +3 -3
  97. package/web-ui/modules/plugins.methods.mjs +3 -3
  98. package/web-ui/modules/plugins.storage.mjs +11 -11
  99. package/web-ui/modules/provider-url-display.mjs +17 -17
  100. package/web-ui/modules/sessions-filters-url.mjs +138 -138
  101. package/web-ui/modules/skills.computed.mjs +107 -107
  102. package/web-ui/modules/skills.methods.mjs +513 -513
  103. package/web-ui/partials/index/layout-footer.html +13 -13
  104. package/web-ui/partials/index/layout-header.html +478 -478
  105. package/web-ui/partials/index/modal-config-template-agents.html +185 -185
  106. package/web-ui/partials/index/modal-confirm-toast.html +32 -32
  107. package/web-ui/partials/index/modal-health-check.html +45 -45
  108. package/web-ui/partials/index/modal-openclaw-config.html +344 -344
  109. package/web-ui/partials/index/modal-skills.html +200 -200
  110. package/web-ui/partials/index/modal-webhook.html +42 -42
  111. package/web-ui/partials/index/modals-basic.html +263 -263
  112. package/web-ui/partials/index/panel-config-claude.html +187 -157
  113. package/web-ui/partials/index/panel-config-codex.html +205 -176
  114. package/web-ui/partials/index/panel-config-openclaw.html +89 -89
  115. package/web-ui/partials/index/panel-dashboard.html +171 -171
  116. package/web-ui/partials/index/panel-docs.html +114 -114
  117. package/web-ui/partials/index/panel-market.html +104 -104
  118. package/web-ui/partials/index/panel-orchestration.html +391 -391
  119. package/web-ui/partials/index/panel-plugins.html +253 -253
  120. package/web-ui/partials/index/panel-sessions.html +319 -319
  121. package/web-ui/partials/index/panel-settings.html +181 -181
  122. package/web-ui/partials/index/panel-trash.html +82 -82
  123. package/web-ui/partials/index/panel-usage.html +181 -181
  124. package/web-ui/res/json5.min.js +1 -1
  125. package/web-ui/res/vue.global.prod.js +13 -13
  126. package/web-ui/res/vue.runtime.global.prod.js +7 -7
  127. package/web-ui/res/web-ui-render.precompiled.js +7666 -7576
  128. package/web-ui/session-helpers.mjs +602 -602
  129. package/web-ui/source-bundle.cjs +305 -305
  130. package/web-ui/styles/base-theme.css +291 -291
  131. package/web-ui/styles/bridge-pool.css +266 -266
  132. package/web-ui/styles/controls-forms.css +532 -439
  133. package/web-ui/styles/dashboard.css +438 -438
  134. package/web-ui/styles/docs-panel.css +245 -245
  135. package/web-ui/styles/feedback.css +108 -108
  136. package/web-ui/styles/health-check-dialog.css +144 -144
  137. package/web-ui/styles/layout-shell.css +711 -711
  138. package/web-ui/styles/modals-core.css +499 -499
  139. package/web-ui/styles/navigation-panels.css +399 -399
  140. package/web-ui/styles/openclaw-structured.css +616 -616
  141. package/web-ui/styles/plugins-panel.css +564 -564
  142. package/web-ui/styles/responsive.css +501 -501
  143. package/web-ui/styles/sessions-list.css +683 -683
  144. package/web-ui/styles/sessions-preview.css +407 -407
  145. package/web-ui/styles/sessions-toolbar-trash.css +518 -518
  146. package/web-ui/styles/sessions-usage.css +849 -849
  147. package/web-ui/styles/settings-panel.css +419 -419
  148. package/web-ui/styles/skills-list.css +305 -305
  149. package/web-ui/styles/skills-market.css +723 -723
  150. package/web-ui/styles/task-orchestration.css +822 -822
  151. package/web-ui/styles/titles-cards.css +486 -486
  152. package/web-ui/styles/trash-panel.css +90 -90
  153. package/web-ui/styles/webhook.css +115 -115
  154. package/web-ui/styles.css +24 -24
  155. package/web-ui.html +17 -17
package/web-ui/app.js CHANGED
@@ -1,693 +1,695 @@
1
- import {
2
- DEFAULT_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
3
- DEFAULT_MODEL_CONTEXT_WINDOW,
4
- DEFAULT_OPENCLAW_TEMPLATE,
5
- SESSION_TRASH_PAGE_SIZE
6
- } from './modules/app.constants.mjs';
7
- import { createAppComputed } from './modules/app.computed.index.mjs';
8
- import { createAppMethods } from './modules/app.methods.index.mjs';
9
- import { loadConfigTemplateDiffConfirmEnabledFromStorage } from './modules/config-template-confirm-pref.mjs';
10
- import { installWebUiUrlCanonicalization } from './modules/sessions-filters-url.mjs';
11
-
12
- document.addEventListener('DOMContentLoaded', () => {
13
- installWebUiUrlCanonicalization();
14
- if (typeof Vue === 'undefined') {
15
- console.error('Vue 库未能在 DOMContentLoaded 触发前加载完成。');
16
- const fallbackTarget = document.querySelector('#app') || document.querySelector('[v-cloak]');
17
- if (fallbackTarget) {
18
- fallbackTarget.removeAttribute('v-cloak');
19
- fallbackTarget.classList.remove('v-cloak');
20
- fallbackTarget.innerHTML = '';
21
- const notice = document.createElement('div');
22
- notice.className = 'fallback-message';
23
- notice.textContent = 'Web UI 加载失败:Vue 未加载。请检查网络或刷新页面。';
24
- fallbackTarget.appendChild(notice);
25
- }
26
- return;
27
- }
28
-
29
- const { createApp } = Vue;
30
-
31
- const appOptions = {
32
- data() {
33
- return {
34
- lang: 'zh',
35
- appVersion: '',
36
- mainTab: 'dashboard',
37
- configMode: 'codex',
38
- currentProvider: '',
39
- currentModel: '',
40
- currentModels: {},
41
- serviceTier: 'fast',
42
- modelReasoningEffort: 'medium',
43
- modelContextWindowInput: String(DEFAULT_MODEL_CONTEXT_WINDOW),
44
- modelAutoCompactTokenLimitInput: String(DEFAULT_MODEL_AUTO_COMPACT_TOKEN_LIMIT),
45
- editingCodexBudgetField: '',
46
- providersList: [],
47
- localBridgeExcluded: [],
48
- claudeLocalBridgeExcluded: [],
49
- models: [],
50
- codexModelsLoading: false,
51
- modelsSource: 'remote',
52
- modelsHasCurrent: true,
53
- claudeModels: [],
54
- claudeModelsSource: 'idle',
55
- claudeModelsHasCurrent: true,
56
- claudeModelsLoading: false,
57
- codexModelsRequestSeq: 0,
58
- claudeModelsRequestSeq: 0,
59
- loading: true,
60
- initError: '',
61
- message: '',
62
- messageType: '',
63
- showAddModal: false,
64
- showEditModal: false,
65
- showAddProviderKey: false,
66
- showEditProviderKey: false,
67
- showModelModal: false,
68
- showModelListModal: false,
69
- showClaudeConfigModal: false,
70
- showEditConfigModal: false,
71
- showAddClaudeConfigKey: false,
72
- showEditClaudeConfigKey: false,
73
- showOpenclawConfigModal: false,
74
- showConfigTemplateModal: false,
75
- showAgentsModal: false,
76
- showSkillsModal: false,
77
- showHealthCheckModal: false,
78
- showCodexBridgePoolModal: false,
79
- showClaudeBridgePoolModal: false,
80
- showWebhookModal: false,
81
- // Plugins
82
- pluginsActiveId: 'prompt-templates',
83
- pluginsLoading: false,
84
- pluginsError: '',
85
- promptTemplatesListRaw: [],
86
- promptTemplatesLoadedOnce: false,
87
- promptTemplatesKeyword: '',
88
- promptTemplateSelectedId: '',
89
- promptTemplateDraftRaw: null,
90
- promptTemplateVarValuesRaw: {},
91
- promptTemplatesMode: 'compose',
92
- promptComposerCommand: '',
93
- promptComposerPickerVisible: false,
94
- promptComposerPickerKeyword: '',
95
- promptComposerSelectedTemplateId: '',
96
- promptComposerVarValuesRaw: {},
97
- showConfirmDialog: false,
98
- confirmDialogTitle: '',
99
- confirmDialogMessage: '',
100
- confirmDialogConfirmText: '确认',
101
- confirmDialogCancelText: '取消',
102
- confirmDialogDanger: false,
103
- confirmDialogConfirmDisabled: false,
104
- confirmDialogDisableWhen: null,
105
- confirmDialogResolver: null,
106
- configTemplateContent: '',
107
- configTemplateApplying: false,
108
- configTemplateContext: 'codex',
109
- configTemplateDiffVisible: false,
110
- configTemplateDiffLoading: false,
111
- configTemplateDiffError: '',
112
- configTemplateDiffLines: [],
113
- configTemplateDiffStats: {
114
- added: 0,
115
- removed: 0,
116
- unchanged: 0
117
- },
118
- configTemplateDiffHasChangesValue: false,
119
- configTemplateDiffFingerprint: '',
120
- _configTemplateDiffPreviewRequestToken: null,
121
- configTemplateDiffConfirmEnabled: true,
122
- codexApplying: false,
123
- _pendingCodexApplyOptions: null,
124
- agentsContent: '',
125
- agentsPath: '',
126
- agentsExists: false,
127
- agentsLineEnding: '\n',
128
- agentsLoading: false,
129
- agentsSaving: false,
130
- agentsOriginalContent: '',
131
- agentsDiffVisible: false,
132
- agentsDiffLoading: false,
133
- agentsDiffError: '',
134
- agentsDiffLines: [],
135
- agentsDiffStats: {
136
- added: 0,
137
- removed: 0,
138
- unchanged: 0
139
- },
140
- agentsDiffTruncated: false,
141
- agentsDiffHasChangesValue: false,
142
- agentsDiffFingerprint: '',
143
- agentsContext: 'codex',
144
- agentsModalTitle: 'AGENTS.md 编辑器',
145
- agentsModalHint: '保存后会写入目标 AGENTS.md(与 config.toml 同级)。',
146
- skillsTargetApp: 'codex',
147
- skillsRootPath: '',
148
- skillsList: [],
149
- skillsSelectedNames: [],
150
- skillsLoading: false,
151
- skillsDeleting: false,
152
- skillsKeyword: '',
153
- skillsStatusFilter: 'all',
154
- skillsImportList: [],
155
- skillsImportSelectedKeys: [],
156
- skillsScanningImports: false,
157
- skillsImporting: false,
158
- skillsZipImporting: false,
159
- skillsExporting: false,
160
- skillsMarketLoading: false,
161
- skillsMarketLocalLoadedOnce: false,
162
- skillsMarketImportLoadedOnce: false,
163
- sessionPinnedMap: {},
164
- __mainTabSwitchState: {
165
- intent: '',
166
- pendingTarget: '',
167
- pendingConfigMode: '',
168
- ticket: 0
169
- },
170
- sessionsViewMode: 'browser',
171
- sessionsUsageTimeRange: (function () { try { const saved = localStorage.getItem('sessionsUsageTimeRange'); if (saved === '7d' || saved === '30d' || saved === 'all') return saved; } catch (_) {} return '7d'; })(),
172
- sessionsUsageList: [],
173
- sessionsUsageCompareEnabled: false,
174
- sessionsUsageSelectedDayKey: '',
175
- sessionsUsageLoadedOnce: false,
176
- sessionsUsageLoadedLimit: 0,
177
- sessionsUsageLoading: false,
178
- sessionsUsageError: '',
179
- sessionsList: [],
180
- sessionsLoadedOnce: false,
181
- sessionsLoading: false,
182
- sessionFilterSource: 'all',
183
- sessionPathFilter: '',
184
- sessionQuery: '',
185
- sessionRoleFilter: 'all',
186
- sessionTimePreset: 'all',
187
- sessionSortMode: 'time',
188
- sessionPathOptions: [],
189
- sessionPathOptionsLoading: false,
190
- sessionPathOptionsMap: {
191
- all: [],
192
- codex: [],
193
- claude: [],
194
- gemini: []
195
- },
196
- sessionPathOptionsLoadedMap: {
197
- all: false,
198
- codex: false,
199
- claude: false,
200
- gemini: false
201
- },
202
- sessionPathRequestSeqMap: {
203
- all: 0,
204
- codex: 0,
205
- claude: 0,
206
- gemini: 0
207
- },
208
- sessionExporting: {},
209
- sessionConverting: {},
210
- sessionImportingNative: {},
211
- sessionCloning: {},
212
- sessionDeleting: {},
213
- activeSession: null,
214
- activeSessionMessages: [],
215
- activeSessionDetailError: '',
216
- activeSessionDetailClipped: false,
217
- sessionDetailLoading: false,
218
- sessionDetailRequestSeq: 0,
219
- sessionDetailInitialMessageLimit: 300,
220
- sessionDetailFetchStep: 300,
221
- sessionDetailMessageLimit: 80,
222
- sessionDetailMessageLimitCap: 1000,
223
- sessionTimelineActiveKey: '',
224
- sessionTimelineRafId: 0,
225
- sessionTimelineLastSyncAt: 0,
226
- sessionTimelineLastScrollTop: 0,
227
- sessionTimelineLastAnchorY: 0,
228
- sessionTimelineLastDirection: 0,
229
- sessionTimelineEnabled: true,
230
- sessionMessageRefMap: Object.create(null),
231
- sessionMessageRefBinderMap: Object.create(null),
232
- sessionPreviewScrollEl: null,
233
- sessionPreviewContainerEl: null,
234
- sessionPreviewHeaderEl: null,
235
- sessionPreviewHeaderResizeObserver: null,
236
- sessionListRenderEnabled: false,
237
- preserveSessionRenderOnTabLeave: true,
238
- sessionListVisibleCount: 0,
239
- sessionListInitialBatchSize: 40,
240
- sessionListLoadStep: 80,
241
- sessionPreviewRenderEnabled: false,
242
- sessionTabRenderTicket: 0,
243
- sessionPreviewVisibleCount: 0,
244
- sessionPreviewInitialBatchSize: 12,
245
- sessionPreviewLoadStep: 24,
246
- sessionPreviewPendingVisibleCount: 0,
247
- sessionPreviewLoadingMore: false,
248
- sessionStandalone: false,
249
- sessionStandaloneError: '',
250
- sessionStandaloneText: '',
251
- sessionStandaloneTitle: '',
252
- sessionStandaloneSourceLabel: '',
253
- sessionStandaloneLoading: false,
254
- sessionStandaloneRequestSeq: 0,
255
- speedResults: {},
256
- speedLoading: {},
257
- claudeSpeedResults: {},
258
- claudeSpeedLoading: {},
259
- claudeShareLoading: {},
260
- providerShareLoading: {},
261
- shareCommandPrefix: 'npm start',
262
- providerSwitchInProgress: false,
263
- pendingProviderSwitch: '',
264
- providerSwitchDisplayTarget: '',
265
- healthCheckBatchTotal: 0,
266
- healthCheckBatchDone: 0,
267
- healthCheckBatchFailed: 0,
268
- installPackageManager: 'npm',
269
- installCommandAction: 'install',
270
- installRegistryPreset: 'default',
271
- installRegistryCustom: '',
272
- installStatusTargets: null,
273
- newProvider: { name: '', url: '', key: '', model: '', useTransform: false },
274
- resetConfigLoading: false,
275
- editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false },
276
- newModelName: '',
277
- currentClaudeConfig: '',
278
- currentClaudeModel: '',
279
- claudeCustomModelDraft: '',
280
- editingConfig: { name: '', apiKey: '', baseUrl: '', model: '' },
281
- claudeConfigs: {
282
- '智谱GLM': {
283
- apiKey: '',
284
- baseUrl: 'https://open.bigmodel.cn/api/anthropic',
285
- model: 'glm-4.7',
286
- hasKey: false
287
- }
288
- },
289
- newClaudeConfig: {
290
- name: '',
291
- apiKey: '',
292
- baseUrl: '',
293
- model: ''
294
- },
295
- currentOpenclawConfig: '',
296
- openclawConfigs: {
297
- '默认配置': {
298
- content: DEFAULT_OPENCLAW_TEMPLATE,
299
- isDefault: true
300
- }
301
- },
302
- openclawEditing: { name: '', content: '', lockName: false },
303
- openclawEditorTitle: '添加 OpenClaw 配置',
304
- openclawConfigPath: '',
305
- openclawConfigExists: false,
306
- openclawLineEnding: '\n',
307
- openclawAuthProfilesByProvider: {},
308
- openclawPendingAuthProfileUpdates: {},
309
- openclawFileLoading: false,
310
- openclawSaving: false,
311
- openclawApplying: false,
312
- openclawWorkspaceFileName: 'SOUL.md',
313
- agentsWorkspaceFileName: '',
314
- openclawStructured: {
315
- agentPrimary: '',
316
- agentFallbacks: [],
317
- workspace: '',
318
- timeout: '',
319
- contextTokens: '',
320
- maxConcurrent: '',
321
- envItems: [],
322
- toolsProfile: 'default',
323
- toolsAllow: [],
324
- toolsDeny: []
325
- },
326
- openclawQuick: {
327
- providerName: '',
328
- baseUrl: '',
329
- baseUrlReadOnly: false,
330
- baseUrlDisplayKind: 'missing',
331
- apiKey: '',
332
- apiKeyReadOnly: false,
333
- apiKeyDisplayKind: 'missing',
334
- apiKeySourceKind: '',
335
- apiKeySourceProfileId: '',
336
- apiKeySourceWriteField: '',
337
- apiKeySourceOriginalValue: '',
338
- apiKeySourceCredentialType: '',
339
- apiType: 'openai-responses',
340
- modelId: '',
341
- modelName: '',
342
- contextWindow: '',
343
- maxTokens: '',
344
- setPrimary: true,
345
- overrideProvider: true,
346
- overrideModels: true,
347
- showKey: false
348
- },
349
- openclawAccordionStep: 1,
350
- openclawValidation: {
351
- providerName: { valid: true, message: '' },
352
- modelId: { valid: true, message: '' }
353
- },
354
- openclawAgentsList: [],
355
- openclawProviders: [],
356
- openclawMissingProviders: [],
357
- healthCheckLoading: false,
358
- healthCheckResult: null,
359
- healthCheckRemote: false,
360
- providersHealthLoading: false,
361
- providersHealthResult: null,
362
- claudeDownloadLoading: false,
363
- claudeDownloadProgress: 0,
364
- claudeDownloadTimer: null,
365
- codexDownloadLoading: false,
366
- codexDownloadProgress: 0,
367
- codexDownloadTimer: null,
368
- settingsTab: 'general',
369
- sessionTrashEnabled: true,
370
- sessionTrashItems: [],
371
- sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE,
372
- sessionTrashTotalCount: 0,
373
- sessionTrashCountLoadedOnce: false,
374
- sessionTrashLoadedOnce: false,
375
- sessionTrashLastLoadFailed: false,
376
- sessionTrashCountRequestToken: 0,
377
- sessionTrashListRequestToken: 0,
378
- sessionTrashCountPendingOptions: null,
379
- sessionTrashPendingOptions: null,
380
- sessionTrashCountLoading: false,
381
- sessionTrashLoading: false,
382
- sessionTrashRestoring: {},
383
- sessionTrashPurging: {},
384
- sessionTrashClearing: false,
385
- sessionTrashRetentionDays: 30,
386
- claudeImportLoading: false,
387
- codexImportLoading: false,
388
- codexAuthProfiles: [],
389
- forceCompactLayout: false,
390
- taskOrchestrationTabEnabled: true,
391
- taskOrchestration: {
392
- loading: false,
393
- planning: false,
394
- running: false,
395
- queueAdding: false,
396
- queueStarting: false,
397
- retrying: false,
398
- target: '',
399
- title: '',
400
- notes: '',
401
- followUpsText: '',
402
- workflowIdsText: '',
403
- selectedEngine: 'codex',
404
- runMode: 'write',
405
- concurrency: 2,
406
- autoFixRounds: 1,
407
- plan: null,
408
- planFingerprint: '',
409
- planIssues: [],
410
- planWarnings: [],
411
- overviewWarnings: [],
412
- workflows: [],
413
- queue: [],
414
- runs: [],
415
- selectedRunId: '',
416
- workspaceTab: 'queue',
417
- selectedRunDetail: null,
418
- selectedRunLoading: false,
419
- selectedRunError: '',
420
- detailRequestToken: 0,
421
- lastLoadedAt: '',
422
- lastError: ''
423
- },
424
- _taskOrchestrationPollTimer: 0,
425
- webhookConfig: { enabled: false, url: '', events: ['provider-switch', 'claude-md-edit'] },
426
- webhookEventOptions: ['provider-switch', 'claude-md-edit'],
427
- webhookSaving: false,
428
- webhookTestResult: null,
429
- webhookTesting: false,
430
- };
431
- },
432
-
433
- mounted() {
434
- // URL 规范化:将 /web-ui/* 重定向到根路径 /
435
- try {
436
- const pathname = window.location.pathname;
437
- if (pathname === '/web-ui' || pathname === '/web-ui/' || pathname === '/web-ui/index.html') {
438
- const url = new URL(window.location.href);
439
- url.pathname = '/';
440
- // 移除查询参数和 hash,保持 URL 纯净
441
- url.search = '';
442
- url.hash = '';
443
- window.location.replace(url.toString());
444
- return;
445
- }
446
- // 清理任何查询参数和 hash,保持 URL 为 /
447
- if (window.location.search || window.location.hash) {
448
- const url = new URL(window.location.href);
449
- url.search = '';
450
- url.hash = '';
451
- window.history.replaceState(null, '', url.toString());
452
- }
453
- } catch (_) {}
454
-
455
- if (typeof this.initI18n === 'function') {
456
- this.initI18n();
457
- }
458
- if (typeof this.loadWebhookSettings === 'function') {
459
- this.loadWebhookSettings();
460
- }
461
- if (typeof this.t === 'function') {
462
- this.confirmDialogConfirmText = this.t('confirm.ok');
463
- this.confirmDialogCancelText = this.t('confirm.cancel');
464
- this.agentsModalTitle = this.t('modal.agents.title');
465
- this.agentsModalHint = this.t('modal.agents.hint');
466
- }
467
- {
468
- const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
469
- const mainTabSet = new Set(['dashboard', 'config', 'sessions', 'usage', 'orchestration', 'market', 'plugins', 'docs', 'settings', 'trash']);
470
- let restored = null;
471
- try {
472
- const raw = localStorage.getItem(NAV_STATE_STORAGE_KEY) || '';
473
- restored = raw ? JSON.parse(raw) : null;
474
- } catch (_) {
475
- restored = null;
476
- }
477
- const nextMainTab = restored && typeof restored.mainTab === 'string'
478
- ? restored.mainTab.trim().toLowerCase()
479
- : '';
480
- const nextConfigMode = restored && typeof restored.configMode === 'string'
481
- ? restored.configMode.trim().toLowerCase()
482
- : '';
483
- const nextSettingsTab = restored && typeof restored.settingsTab === 'string'
484
- ? restored.settingsTab.trim().toLowerCase()
485
- : '';
486
- let urlMainTab = '';
487
- try {
488
- const url = new URL(window.location.href);
489
- if (url.pathname !== '/session') {
490
- urlMainTab = String(url.searchParams.get('tab') || '').trim().toLowerCase();
491
- }
492
- } catch (_) {
493
- urlMainTab = '';
494
- }
495
- const resolvedMainTab = urlMainTab && mainTabSet.has(urlMainTab)
496
- ? urlMainTab
497
- : nextMainTab;
498
- if (nextSettingsTab && (nextSettingsTab === 'general' || nextSettingsTab === 'data')) {
499
- this.settingsTab = nextSettingsTab;
500
- }
501
- if (nextConfigMode && typeof this.switchConfigMode === 'function') {
502
- this.__navStateRestoring = true;
503
- try {
504
- if (nextConfigMode === 'codex' || nextConfigMode === 'claude' || nextConfigMode === 'openclaw') {
505
- this.configMode = nextConfigMode;
506
- }
507
- if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
508
- this.switchMainTab(resolvedMainTab);
509
- }
510
- } finally {
511
- this.__navStateRestoring = false;
512
- }
513
- } else if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
514
- this.__navStateRestoring = true;
515
- try {
516
- this.switchMainTab(resolvedMainTab);
517
- } finally {
518
- this.__navStateRestoring = false;
519
- }
520
- }
521
- }
522
- this.initSessionStandalone();
523
- this.updateCompactLayoutMode();
524
- if (!this.taskOrchestrationTabEnabled && this.mainTab === 'orchestration') {
525
- this.mainTab = 'config';
526
- }
527
- this.restoreSessionFilterCache();
528
- this.restoreSessionPinnedMap();
529
- this.shareCommandPrefix = this.normalizeShareCommandPrefix(localStorage.getItem('codexmateShareCommandPrefix'));
530
- this.sessionTrashEnabled = this.normalizeSessionTrashEnabled(localStorage.getItem('codexmateSessionTrashEnabled'));
531
- this.sessionTrashRetentionDays = this.normalizeSessionTrashRetentionDays(localStorage.getItem('codexmateSessionTrashRetentionDays'));
532
- this.configTemplateDiffConfirmEnabled = loadConfigTemplateDiffConfirmEnabledFromStorage(localStorage);
533
- window.addEventListener('resize', this.onWindowResize);
534
- window.addEventListener('keydown', this.handleGlobalKeydown);
535
- window.addEventListener('beforeunload', this.handleBeforeUnload);
536
- const savedConfigs = localStorage.getItem('claudeConfigs');
537
- if (savedConfigs) {
538
- try {
539
- this.claudeConfigs = JSON.parse(savedConfigs);
540
- for (const [name, config] of Object.entries(this.claudeConfigs)) {
541
- if (config.apiKey && config.apiKey.includes('****')) {
542
- config.apiKey = '';
543
- config.hasKey = false;
544
- }
545
- }
546
- localStorage.setItem('claudeConfigs', JSON.stringify(this.claudeConfigs));
547
- } catch (e) {
548
- console.error('加载 Claude 配置失败:', e);
549
- }
550
- }
551
- {
552
- const savedCurrentClaudeConfig = localStorage.getItem('currentClaudeConfig');
553
- if (savedCurrentClaudeConfig && this.claudeConfigs[savedCurrentClaudeConfig]) {
554
- this.currentClaudeConfig = savedCurrentClaudeConfig;
555
- }
556
- }
557
- if (!this.currentClaudeConfig) {
558
- const claudeConfigNames = Object.keys(this.claudeConfigs || {});
559
- if (claudeConfigNames.length > 0) {
560
- this.currentClaudeConfig = claudeConfigNames[0];
561
- }
562
- }
563
- if (this.currentClaudeConfig && !this.currentClaudeModel) {
564
- const initialClaudeConfig = this.claudeConfigs[this.currentClaudeConfig];
565
- this.currentClaudeModel = initialClaudeConfig && initialClaudeConfig.model ? initialClaudeConfig.model : '';
566
- }
567
- const normalizeOpenclawConfigs = (configs) => {
568
- const source = configs && typeof configs === 'object' && !Array.isArray(configs)
569
- ? configs
570
- : {};
571
- const defaultEntry = source['默认配置']
572
- && typeof source['默认配置'] === 'object'
573
- && !Array.isArray(source['默认配置'])
574
- ? source['默认配置']
575
- : { content: DEFAULT_OPENCLAW_TEMPLATE };
576
- const normalized = {
577
- '默认配置': {
578
- content: typeof defaultEntry.content === 'string' ? defaultEntry.content : DEFAULT_OPENCLAW_TEMPLATE,
579
- isDefault: true
580
- }
581
- };
582
- for (const [name, value] of Object.entries(source)) {
583
- if (name === '默认配置') continue;
584
- normalized[name] = value;
585
- }
586
- return normalized;
587
- };
588
- const savedOpenclawConfigs = localStorage.getItem('openclawConfigs');
589
- if (savedOpenclawConfigs) {
590
- try {
591
- this.openclawConfigs = normalizeOpenclawConfigs(JSON.parse(savedOpenclawConfigs));
592
- } catch (e) {
593
- console.error('加载 OpenClaw 配置失败:', e);
594
- this.openclawConfigs = normalizeOpenclawConfigs(this.openclawConfigs);
595
- }
596
- } else {
597
- this.openclawConfigs = normalizeOpenclawConfigs(this.openclawConfigs);
598
- }
599
- const configNames = Object.keys(this.openclawConfigs);
600
- if (configNames.length > 0) {
601
- this.currentOpenclawConfig = this.openclawConfigs['默认配置'] ? '默认配置' : configNames[0];
602
- }
603
- const runInitialLoad = () => {
604
- const triggerLoad = async () => {
605
- this._initialLoadTimer = 0;
606
- const startupOk = await this.loadAll();
607
- if (!startupOk) {
608
- return;
609
- }
610
- if (this.mainTab === 'dashboard') {
611
- if (!this.__doctorLoadedOnce) {
612
- this.__doctorLoadedOnce = true;
613
- if (typeof this.runHealthCheck === 'function') {
614
- void this.runHealthCheck({ doctor: true, silent: true });
615
- }
616
- if (typeof this.runProvidersHealthCheck === 'function') {
617
- void this.runProvidersHealthCheck({ remote: true });
618
- }
619
- }
620
- }
621
- void this.refreshClaudeSelectionFromSettings({ silent: true });
622
- void this.syncDefaultOpenclawConfigEntry({ silent: true });
623
- };
624
- if (typeof requestAnimationFrame === 'function') {
625
- this._initialLoadRafId = requestAnimationFrame(() => {
626
- this._initialLoadRafId = 0;
627
- if (typeof setTimeout === 'function') {
628
- this._initialLoadTimer = setTimeout(triggerLoad, 120);
629
- return;
630
- }
631
- triggerLoad();
632
- });
633
- return;
634
- }
635
- if (typeof setTimeout === 'function') {
636
- this._initialLoadTimer = setTimeout(triggerLoad, 120);
637
- return;
638
- }
639
- triggerLoad();
640
- };
641
- if (document.readyState === 'complete') {
642
- runInitialLoad();
643
- } else {
644
- this._initialLoadOnWindowLoad = () => {
645
- if (typeof window !== 'undefined' && typeof window.removeEventListener === 'function') {
646
- window.removeEventListener('load', this._initialLoadOnWindowLoad);
647
- }
648
- this._initialLoadOnWindowLoad = null;
649
- runInitialLoad();
650
- };
651
- window.addEventListener('load', this._initialLoadOnWindowLoad, { once: true });
652
- }
653
- },
654
-
655
- beforeUnmount() {
656
- this.teardownSessionTabRender();
657
- this.cancelScheduledSessionTabDeferredTeardown();
658
- this.disconnectSessionPreviewHeaderResizeObserver();
659
- if (this._initialLoadOnWindowLoad) {
660
- window.removeEventListener('load', this._initialLoadOnWindowLoad);
661
- this._initialLoadOnWindowLoad = null;
662
- }
663
- if (this._initialLoadRafId) {
664
- cancelAnimationFrame(this._initialLoadRafId);
665
- this._initialLoadRafId = 0;
666
- }
667
- if (this._initialLoadTimer) {
668
- clearTimeout(this._initialLoadTimer);
669
- this._initialLoadTimer = 0;
670
- }
671
- window.removeEventListener('resize', this.onWindowResize);
672
- window.removeEventListener('keydown', this.handleGlobalKeydown);
673
- window.removeEventListener('beforeunload', this.handleBeforeUnload);
674
- this.applyCompactLayoutClass(false);
675
- this.stopTaskOrchestrationPolling();
676
- this.sessionPreviewScrollEl = null;
677
- this.sessionPreviewContainerEl = null;
678
- this.sessionPreviewHeaderEl = null;
679
- this.clearSessionTimelineRefs();
680
- },
681
-
682
- computed: createAppComputed(),
683
- methods: createAppMethods()
684
- };
685
-
686
- if (typeof window.__CODEXMATE_WEB_UI_RENDER__ === 'function') {
687
- appOptions.render = window.__CODEXMATE_WEB_UI_RENDER__;
688
- }
689
-
690
- const app = createApp(appOptions);
691
-
692
- app.mount('#app');
693
- });
1
+ import {
2
+ DEFAULT_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
3
+ DEFAULT_MODEL_CONTEXT_WINDOW,
4
+ DEFAULT_OPENCLAW_TEMPLATE,
5
+ SESSION_TRASH_PAGE_SIZE
6
+ } from './modules/app.constants.mjs';
7
+ import { createAppComputed } from './modules/app.computed.index.mjs';
8
+ import { createAppMethods } from './modules/app.methods.index.mjs';
9
+ import { loadConfigTemplateDiffConfirmEnabledFromStorage } from './modules/config-template-confirm-pref.mjs';
10
+ import { installWebUiUrlCanonicalization } from './modules/sessions-filters-url.mjs';
11
+
12
+ document.addEventListener('DOMContentLoaded', () => {
13
+ installWebUiUrlCanonicalization();
14
+ if (typeof Vue === 'undefined') {
15
+ console.error('Vue 库未能在 DOMContentLoaded 触发前加载完成。');
16
+ const fallbackTarget = document.querySelector('#app') || document.querySelector('[v-cloak]');
17
+ if (fallbackTarget) {
18
+ fallbackTarget.removeAttribute('v-cloak');
19
+ fallbackTarget.classList.remove('v-cloak');
20
+ fallbackTarget.innerHTML = '';
21
+ const notice = document.createElement('div');
22
+ notice.className = 'fallback-message';
23
+ notice.textContent = 'Web UI 加载失败:Vue 未加载。请检查网络或刷新页面。';
24
+ fallbackTarget.appendChild(notice);
25
+ }
26
+ return;
27
+ }
28
+
29
+ const { createApp } = Vue;
30
+
31
+ const appOptions = {
32
+ data() {
33
+ return {
34
+ lang: 'zh',
35
+ appVersion: '',
36
+ mainTab: 'dashboard',
37
+ configMode: 'codex',
38
+ currentProvider: '',
39
+ currentModel: '',
40
+ currentModels: {},
41
+ serviceTier: 'fast',
42
+ modelReasoningEffort: 'medium',
43
+ modelContextWindowInput: String(DEFAULT_MODEL_CONTEXT_WINDOW),
44
+ modelAutoCompactTokenLimitInput: String(DEFAULT_MODEL_AUTO_COMPACT_TOKEN_LIMIT),
45
+ editingCodexBudgetField: '',
46
+ providersList: [],
47
+ localBridgeExcluded: [],
48
+ claudeLocalBridgeExcluded: [],
49
+ models: [],
50
+ codexModelsLoading: false,
51
+ modelsSource: 'remote',
52
+ modelsHasCurrent: true,
53
+ claudeModels: [],
54
+ claudeModelsSource: 'idle',
55
+ claudeModelsHasCurrent: true,
56
+ claudeModelsLoading: false,
57
+ codexModelsRequestSeq: 0,
58
+ claudeModelsRequestSeq: 0,
59
+ loading: true,
60
+ initError: '',
61
+ message: '',
62
+ messageType: '',
63
+ showAddModal: false,
64
+ showEditModal: false,
65
+ showAddProviderKey: false,
66
+ showEditProviderKey: false,
67
+ showModelModal: false,
68
+ showModelListModal: false,
69
+ showClaudeConfigModal: false,
70
+ showEditConfigModal: false,
71
+ showAddClaudeConfigKey: false,
72
+ showEditClaudeConfigKey: false,
73
+ showOpenclawConfigModal: false,
74
+ showConfigTemplateModal: false,
75
+ showAgentsModal: false,
76
+ showSkillsModal: false,
77
+ showHealthCheckModal: false,
78
+ showCodexBridgePoolModal: false,
79
+ showClaudeBridgePoolModal: false,
80
+ showWebhookModal: false,
81
+ // Plugins
82
+ pluginsActiveId: 'prompt-templates',
83
+ pluginsLoading: false,
84
+ pluginsError: '',
85
+ promptTemplatesListRaw: [],
86
+ promptTemplatesLoadedOnce: false,
87
+ promptTemplatesKeyword: '',
88
+ promptTemplateSelectedId: '',
89
+ promptTemplateDraftRaw: null,
90
+ promptTemplateVarValuesRaw: {},
91
+ promptTemplatesMode: 'compose',
92
+ promptComposerCommand: '',
93
+ promptComposerPickerVisible: false,
94
+ promptComposerPickerKeyword: '',
95
+ promptComposerSelectedTemplateId: '',
96
+ promptComposerVarValuesRaw: {},
97
+ showConfirmDialog: false,
98
+ confirmDialogTitle: '',
99
+ confirmDialogMessage: '',
100
+ confirmDialogConfirmText: '确认',
101
+ confirmDialogCancelText: '取消',
102
+ confirmDialogDanger: false,
103
+ confirmDialogConfirmDisabled: false,
104
+ confirmDialogDisableWhen: null,
105
+ confirmDialogResolver: null,
106
+ configTemplateContent: '',
107
+ configTemplateApplying: false,
108
+ configTemplateContext: 'codex',
109
+ configTemplateDiffVisible: false,
110
+ configTemplateDiffLoading: false,
111
+ configTemplateDiffError: '',
112
+ configTemplateDiffLines: [],
113
+ configTemplateDiffStats: {
114
+ added: 0,
115
+ removed: 0,
116
+ unchanged: 0
117
+ },
118
+ configTemplateDiffHasChangesValue: false,
119
+ configTemplateDiffFingerprint: '',
120
+ _configTemplateDiffPreviewRequestToken: null,
121
+ configTemplateDiffConfirmEnabled: true,
122
+ codexApplying: false,
123
+ _pendingCodexApplyOptions: null,
124
+ agentsContent: '',
125
+ agentsPath: '',
126
+ agentsExists: false,
127
+ agentsLineEnding: '\n',
128
+ agentsLoading: false,
129
+ agentsSaving: false,
130
+ agentsOriginalContent: '',
131
+ agentsDiffVisible: false,
132
+ agentsDiffLoading: false,
133
+ agentsDiffError: '',
134
+ agentsDiffLines: [],
135
+ agentsDiffStats: {
136
+ added: 0,
137
+ removed: 0,
138
+ unchanged: 0
139
+ },
140
+ agentsDiffTruncated: false,
141
+ agentsDiffHasChangesValue: false,
142
+ agentsDiffFingerprint: '',
143
+ agentsContext: 'codex',
144
+ agentsModalTitle: 'AGENTS.md 编辑器',
145
+ agentsModalHint: '保存后会写入目标 AGENTS.md(与 config.toml 同级)。',
146
+ skillsTargetApp: 'codex',
147
+ skillsRootPath: '',
148
+ skillsList: [],
149
+ skillsSelectedNames: [],
150
+ skillsLoading: false,
151
+ skillsDeleting: false,
152
+ skillsKeyword: '',
153
+ skillsStatusFilter: 'all',
154
+ skillsImportList: [],
155
+ skillsImportSelectedKeys: [],
156
+ skillsScanningImports: false,
157
+ skillsImporting: false,
158
+ skillsZipImporting: false,
159
+ skillsExporting: false,
160
+ skillsMarketLoading: false,
161
+ skillsMarketLocalLoadedOnce: false,
162
+ skillsMarketImportLoadedOnce: false,
163
+ sessionPinnedMap: {},
164
+ __mainTabSwitchState: {
165
+ intent: '',
166
+ pendingTarget: '',
167
+ pendingConfigMode: '',
168
+ ticket: 0
169
+ },
170
+ sessionsViewMode: 'browser',
171
+ sessionsUsageTimeRange: (function () { try { const saved = localStorage.getItem('sessionsUsageTimeRange'); if (saved === '7d' || saved === '30d' || saved === 'all') return saved; } catch (_) {} return '7d'; })(),
172
+ sessionsUsageList: [],
173
+ sessionsUsageCompareEnabled: false,
174
+ sessionsUsageSelectedDayKey: '',
175
+ sessionsUsageLoadedOnce: false,
176
+ sessionsUsageLoadedLimit: 0,
177
+ sessionsUsageLoading: false,
178
+ sessionsUsageError: '',
179
+ sessionsList: [],
180
+ sessionsLoadedOnce: false,
181
+ sessionsLoading: false,
182
+ sessionFilterSource: 'all',
183
+ sessionPathFilter: '',
184
+ sessionQuery: '',
185
+ sessionRoleFilter: 'all',
186
+ sessionTimePreset: 'all',
187
+ sessionSortMode: 'time',
188
+ sessionPathOptions: [],
189
+ sessionPathOptionsLoading: false,
190
+ sessionPathOptionsMap: {
191
+ all: [],
192
+ codex: [],
193
+ claude: [],
194
+ gemini: []
195
+ },
196
+ sessionPathOptionsLoadedMap: {
197
+ all: false,
198
+ codex: false,
199
+ claude: false,
200
+ gemini: false
201
+ },
202
+ sessionPathRequestSeqMap: {
203
+ all: 0,
204
+ codex: 0,
205
+ claude: 0,
206
+ gemini: 0
207
+ },
208
+ sessionExporting: {},
209
+ sessionConverting: {},
210
+ sessionImportingNative: {},
211
+ sessionCloning: {},
212
+ sessionDeleting: {},
213
+ activeSession: null,
214
+ activeSessionMessages: [],
215
+ activeSessionDetailError: '',
216
+ activeSessionDetailClipped: false,
217
+ sessionDetailLoading: false,
218
+ sessionDetailRequestSeq: 0,
219
+ sessionDetailInitialMessageLimit: 300,
220
+ sessionDetailFetchStep: 300,
221
+ sessionDetailMessageLimit: 80,
222
+ sessionDetailMessageLimitCap: 1000,
223
+ sessionTimelineActiveKey: '',
224
+ sessionTimelineRafId: 0,
225
+ sessionTimelineLastSyncAt: 0,
226
+ sessionTimelineLastScrollTop: 0,
227
+ sessionTimelineLastAnchorY: 0,
228
+ sessionTimelineLastDirection: 0,
229
+ sessionTimelineEnabled: true,
230
+ sessionMessageRefMap: Object.create(null),
231
+ sessionMessageRefBinderMap: Object.create(null),
232
+ sessionPreviewScrollEl: null,
233
+ sessionPreviewContainerEl: null,
234
+ sessionPreviewHeaderEl: null,
235
+ sessionPreviewHeaderResizeObserver: null,
236
+ sessionListRenderEnabled: false,
237
+ preserveSessionRenderOnTabLeave: true,
238
+ sessionListVisibleCount: 0,
239
+ sessionListInitialBatchSize: 40,
240
+ sessionListLoadStep: 80,
241
+ sessionPreviewRenderEnabled: false,
242
+ sessionTabRenderTicket: 0,
243
+ sessionPreviewVisibleCount: 0,
244
+ sessionPreviewInitialBatchSize: 12,
245
+ sessionPreviewLoadStep: 24,
246
+ sessionPreviewPendingVisibleCount: 0,
247
+ sessionPreviewLoadingMore: false,
248
+ sessionStandalone: false,
249
+ sessionStandaloneError: '',
250
+ sessionStandaloneText: '',
251
+ sessionStandaloneTitle: '',
252
+ sessionStandaloneSourceLabel: '',
253
+ sessionStandaloneLoading: false,
254
+ sessionStandaloneRequestSeq: 0,
255
+ speedResults: {},
256
+ speedLoading: {},
257
+ claudeSpeedResults: {},
258
+ claudeSpeedLoading: {},
259
+ claudeShareLoading: {},
260
+ providerShareLoading: {},
261
+ shareCommandPrefix: 'npm start',
262
+ providerSwitchInProgress: false,
263
+ pendingProviderSwitch: '',
264
+ providerSwitchDisplayTarget: '',
265
+ healthCheckBatchTotal: 0,
266
+ healthCheckBatchDone: 0,
267
+ healthCheckBatchFailed: 0,
268
+ installPackageManager: 'npm',
269
+ installCommandAction: 'install',
270
+ installRegistryPreset: 'default',
271
+ installRegistryCustom: '',
272
+ installStatusTargets: null,
273
+ newProvider: { name: '', url: '', key: '', model: '', useTransform: false },
274
+ resetConfigLoading: false,
275
+ editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false },
276
+ newModelName: '',
277
+ currentClaudeConfig: '',
278
+ currentClaudeModel: '',
279
+ claudeCustomModelDraft: '',
280
+ editingConfig: { name: '', apiKey: '', baseUrl: '', model: '' },
281
+ claudeConfigs: {
282
+ '智谱GLM': {
283
+ apiKey: '',
284
+ baseUrl: 'https://open.bigmodel.cn/api/anthropic',
285
+ model: 'glm-4.7',
286
+ hasKey: false
287
+ }
288
+ },
289
+ newClaudeConfig: {
290
+ name: '',
291
+ apiKey: '',
292
+ baseUrl: '',
293
+ model: ''
294
+ },
295
+ currentOpenclawConfig: '',
296
+ openclawConfigs: {
297
+ '默认配置': {
298
+ content: DEFAULT_OPENCLAW_TEMPLATE,
299
+ isDefault: true
300
+ }
301
+ },
302
+ openclawEditing: { name: '', content: '', lockName: false },
303
+ openclawEditorTitle: '添加 OpenClaw 配置',
304
+ openclawConfigPath: '',
305
+ openclawConfigExists: false,
306
+ openclawLineEnding: '\n',
307
+ openclawAuthProfilesByProvider: {},
308
+ openclawPendingAuthProfileUpdates: {},
309
+ openclawFileLoading: false,
310
+ openclawSaving: false,
311
+ openclawApplying: false,
312
+ openclawWorkspaceFileName: 'SOUL.md',
313
+ agentsWorkspaceFileName: '',
314
+ openclawStructured: {
315
+ agentPrimary: '',
316
+ agentFallbacks: [],
317
+ workspace: '',
318
+ timeout: '',
319
+ contextTokens: '',
320
+ maxConcurrent: '',
321
+ envItems: [],
322
+ toolsProfile: 'default',
323
+ toolsAllow: [],
324
+ toolsDeny: []
325
+ },
326
+ openclawQuick: {
327
+ providerName: '',
328
+ baseUrl: '',
329
+ baseUrlReadOnly: false,
330
+ baseUrlDisplayKind: 'missing',
331
+ apiKey: '',
332
+ apiKeyReadOnly: false,
333
+ apiKeyDisplayKind: 'missing',
334
+ apiKeySourceKind: '',
335
+ apiKeySourceProfileId: '',
336
+ apiKeySourceWriteField: '',
337
+ apiKeySourceOriginalValue: '',
338
+ apiKeySourceCredentialType: '',
339
+ apiType: 'openai-responses',
340
+ modelId: '',
341
+ modelName: '',
342
+ contextWindow: '',
343
+ maxTokens: '',
344
+ setPrimary: true,
345
+ overrideProvider: true,
346
+ overrideModels: true,
347
+ showKey: false
348
+ },
349
+ openclawAccordionStep: 1,
350
+ openclawValidation: {
351
+ providerName: { valid: true, message: '' },
352
+ modelId: { valid: true, message: '' }
353
+ },
354
+ openclawAgentsList: [],
355
+ openclawProviders: [],
356
+ openclawMissingProviders: [],
357
+ healthCheckLoading: false,
358
+ healthCheckResult: null,
359
+ healthCheckRemote: false,
360
+ providersHealthLoading: false,
361
+ providersHealthResult: null,
362
+ claudeDownloadLoading: false,
363
+ claudeDownloadProgress: 0,
364
+ claudeDownloadTimer: null,
365
+ codexDownloadLoading: false,
366
+ codexDownloadProgress: 0,
367
+ codexDownloadTimer: null,
368
+ settingsTab: 'general',
369
+ toolConfigPermissions: { codex: false, claude: false },
370
+ toolConfigPermissionSaving: { codex: false, claude: false },
371
+ sessionTrashEnabled: true,
372
+ sessionTrashItems: [],
373
+ sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE,
374
+ sessionTrashTotalCount: 0,
375
+ sessionTrashCountLoadedOnce: false,
376
+ sessionTrashLoadedOnce: false,
377
+ sessionTrashLastLoadFailed: false,
378
+ sessionTrashCountRequestToken: 0,
379
+ sessionTrashListRequestToken: 0,
380
+ sessionTrashCountPendingOptions: null,
381
+ sessionTrashPendingOptions: null,
382
+ sessionTrashCountLoading: false,
383
+ sessionTrashLoading: false,
384
+ sessionTrashRestoring: {},
385
+ sessionTrashPurging: {},
386
+ sessionTrashClearing: false,
387
+ sessionTrashRetentionDays: 30,
388
+ claudeImportLoading: false,
389
+ codexImportLoading: false,
390
+ codexAuthProfiles: [],
391
+ forceCompactLayout: false,
392
+ taskOrchestrationTabEnabled: true,
393
+ taskOrchestration: {
394
+ loading: false,
395
+ planning: false,
396
+ running: false,
397
+ queueAdding: false,
398
+ queueStarting: false,
399
+ retrying: false,
400
+ target: '',
401
+ title: '',
402
+ notes: '',
403
+ followUpsText: '',
404
+ workflowIdsText: '',
405
+ selectedEngine: 'codex',
406
+ runMode: 'write',
407
+ concurrency: 2,
408
+ autoFixRounds: 1,
409
+ plan: null,
410
+ planFingerprint: '',
411
+ planIssues: [],
412
+ planWarnings: [],
413
+ overviewWarnings: [],
414
+ workflows: [],
415
+ queue: [],
416
+ runs: [],
417
+ selectedRunId: '',
418
+ workspaceTab: 'queue',
419
+ selectedRunDetail: null,
420
+ selectedRunLoading: false,
421
+ selectedRunError: '',
422
+ detailRequestToken: 0,
423
+ lastLoadedAt: '',
424
+ lastError: ''
425
+ },
426
+ _taskOrchestrationPollTimer: 0,
427
+ webhookConfig: { enabled: false, url: '', events: ['provider-switch', 'claude-md-edit'] },
428
+ webhookEventOptions: ['provider-switch', 'claude-md-edit'],
429
+ webhookSaving: false,
430
+ webhookTestResult: null,
431
+ webhookTesting: false,
432
+ };
433
+ },
434
+
435
+ mounted() {
436
+ // URL 规范化:将 /web-ui/* 重定向到根路径 /
437
+ try {
438
+ const pathname = window.location.pathname;
439
+ if (pathname === '/web-ui' || pathname === '/web-ui/' || pathname === '/web-ui/index.html') {
440
+ const url = new URL(window.location.href);
441
+ url.pathname = '/';
442
+ // 移除查询参数和 hash,保持 URL 纯净
443
+ url.search = '';
444
+ url.hash = '';
445
+ window.location.replace(url.toString());
446
+ return;
447
+ }
448
+ // 清理任何查询参数和 hash,保持 URL 为 /
449
+ if (window.location.search || window.location.hash) {
450
+ const url = new URL(window.location.href);
451
+ url.search = '';
452
+ url.hash = '';
453
+ window.history.replaceState(null, '', url.toString());
454
+ }
455
+ } catch (_) {}
456
+
457
+ if (typeof this.initI18n === 'function') {
458
+ this.initI18n();
459
+ }
460
+ if (typeof this.loadWebhookSettings === 'function') {
461
+ this.loadWebhookSettings();
462
+ }
463
+ if (typeof this.t === 'function') {
464
+ this.confirmDialogConfirmText = this.t('confirm.ok');
465
+ this.confirmDialogCancelText = this.t('confirm.cancel');
466
+ this.agentsModalTitle = this.t('modal.agents.title');
467
+ this.agentsModalHint = this.t('modal.agents.hint');
468
+ }
469
+ {
470
+ const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
471
+ const mainTabSet = new Set(['dashboard', 'config', 'sessions', 'usage', 'orchestration', 'market', 'plugins', 'docs', 'settings', 'trash']);
472
+ let restored = null;
473
+ try {
474
+ const raw = localStorage.getItem(NAV_STATE_STORAGE_KEY) || '';
475
+ restored = raw ? JSON.parse(raw) : null;
476
+ } catch (_) {
477
+ restored = null;
478
+ }
479
+ const nextMainTab = restored && typeof restored.mainTab === 'string'
480
+ ? restored.mainTab.trim().toLowerCase()
481
+ : '';
482
+ const nextConfigMode = restored && typeof restored.configMode === 'string'
483
+ ? restored.configMode.trim().toLowerCase()
484
+ : '';
485
+ const nextSettingsTab = restored && typeof restored.settingsTab === 'string'
486
+ ? restored.settingsTab.trim().toLowerCase()
487
+ : '';
488
+ let urlMainTab = '';
489
+ try {
490
+ const url = new URL(window.location.href);
491
+ if (url.pathname !== '/session') {
492
+ urlMainTab = String(url.searchParams.get('tab') || '').trim().toLowerCase();
493
+ }
494
+ } catch (_) {
495
+ urlMainTab = '';
496
+ }
497
+ const resolvedMainTab = urlMainTab && mainTabSet.has(urlMainTab)
498
+ ? urlMainTab
499
+ : nextMainTab;
500
+ if (nextSettingsTab && (nextSettingsTab === 'general' || nextSettingsTab === 'data')) {
501
+ this.settingsTab = nextSettingsTab;
502
+ }
503
+ if (nextConfigMode && typeof this.switchConfigMode === 'function') {
504
+ this.__navStateRestoring = true;
505
+ try {
506
+ if (nextConfigMode === 'codex' || nextConfigMode === 'claude' || nextConfigMode === 'openclaw') {
507
+ this.configMode = nextConfigMode;
508
+ }
509
+ if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
510
+ this.switchMainTab(resolvedMainTab);
511
+ }
512
+ } finally {
513
+ this.__navStateRestoring = false;
514
+ }
515
+ } else if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
516
+ this.__navStateRestoring = true;
517
+ try {
518
+ this.switchMainTab(resolvedMainTab);
519
+ } finally {
520
+ this.__navStateRestoring = false;
521
+ }
522
+ }
523
+ }
524
+ this.initSessionStandalone();
525
+ this.updateCompactLayoutMode();
526
+ if (!this.taskOrchestrationTabEnabled && this.mainTab === 'orchestration') {
527
+ this.mainTab = 'config';
528
+ }
529
+ this.restoreSessionFilterCache();
530
+ this.restoreSessionPinnedMap();
531
+ this.shareCommandPrefix = this.normalizeShareCommandPrefix(localStorage.getItem('codexmateShareCommandPrefix'));
532
+ this.sessionTrashEnabled = this.normalizeSessionTrashEnabled(localStorage.getItem('codexmateSessionTrashEnabled'));
533
+ this.sessionTrashRetentionDays = this.normalizeSessionTrashRetentionDays(localStorage.getItem('codexmateSessionTrashRetentionDays'));
534
+ this.configTemplateDiffConfirmEnabled = loadConfigTemplateDiffConfirmEnabledFromStorage(localStorage);
535
+ window.addEventListener('resize', this.onWindowResize);
536
+ window.addEventListener('keydown', this.handleGlobalKeydown);
537
+ window.addEventListener('beforeunload', this.handleBeforeUnload);
538
+ const savedConfigs = localStorage.getItem('claudeConfigs');
539
+ if (savedConfigs) {
540
+ try {
541
+ this.claudeConfigs = JSON.parse(savedConfigs);
542
+ for (const [name, config] of Object.entries(this.claudeConfigs)) {
543
+ if (config.apiKey && config.apiKey.includes('****')) {
544
+ config.apiKey = '';
545
+ config.hasKey = false;
546
+ }
547
+ }
548
+ localStorage.setItem('claudeConfigs', JSON.stringify(this.claudeConfigs));
549
+ } catch (e) {
550
+ console.error('加载 Claude 配置失败:', e);
551
+ }
552
+ }
553
+ {
554
+ const savedCurrentClaudeConfig = localStorage.getItem('currentClaudeConfig');
555
+ if (savedCurrentClaudeConfig && this.claudeConfigs[savedCurrentClaudeConfig]) {
556
+ this.currentClaudeConfig = savedCurrentClaudeConfig;
557
+ }
558
+ }
559
+ if (!this.currentClaudeConfig) {
560
+ const claudeConfigNames = Object.keys(this.claudeConfigs || {});
561
+ if (claudeConfigNames.length > 0) {
562
+ this.currentClaudeConfig = claudeConfigNames[0];
563
+ }
564
+ }
565
+ if (this.currentClaudeConfig && !this.currentClaudeModel) {
566
+ const initialClaudeConfig = this.claudeConfigs[this.currentClaudeConfig];
567
+ this.currentClaudeModel = initialClaudeConfig && initialClaudeConfig.model ? initialClaudeConfig.model : '';
568
+ }
569
+ const normalizeOpenclawConfigs = (configs) => {
570
+ const source = configs && typeof configs === 'object' && !Array.isArray(configs)
571
+ ? configs
572
+ : {};
573
+ const defaultEntry = source['默认配置']
574
+ && typeof source['默认配置'] === 'object'
575
+ && !Array.isArray(source['默认配置'])
576
+ ? source['默认配置']
577
+ : { content: DEFAULT_OPENCLAW_TEMPLATE };
578
+ const normalized = {
579
+ '默认配置': {
580
+ content: typeof defaultEntry.content === 'string' ? defaultEntry.content : DEFAULT_OPENCLAW_TEMPLATE,
581
+ isDefault: true
582
+ }
583
+ };
584
+ for (const [name, value] of Object.entries(source)) {
585
+ if (name === '默认配置') continue;
586
+ normalized[name] = value;
587
+ }
588
+ return normalized;
589
+ };
590
+ const savedOpenclawConfigs = localStorage.getItem('openclawConfigs');
591
+ if (savedOpenclawConfigs) {
592
+ try {
593
+ this.openclawConfigs = normalizeOpenclawConfigs(JSON.parse(savedOpenclawConfigs));
594
+ } catch (e) {
595
+ console.error('加载 OpenClaw 配置失败:', e);
596
+ this.openclawConfigs = normalizeOpenclawConfigs(this.openclawConfigs);
597
+ }
598
+ } else {
599
+ this.openclawConfigs = normalizeOpenclawConfigs(this.openclawConfigs);
600
+ }
601
+ const configNames = Object.keys(this.openclawConfigs);
602
+ if (configNames.length > 0) {
603
+ this.currentOpenclawConfig = this.openclawConfigs['默认配置'] ? '默认配置' : configNames[0];
604
+ }
605
+ const runInitialLoad = () => {
606
+ const triggerLoad = async () => {
607
+ this._initialLoadTimer = 0;
608
+ const startupOk = await this.loadAll();
609
+ if (!startupOk) {
610
+ return;
611
+ }
612
+ if (this.mainTab === 'dashboard') {
613
+ if (!this.__doctorLoadedOnce) {
614
+ this.__doctorLoadedOnce = true;
615
+ if (typeof this.runHealthCheck === 'function') {
616
+ void this.runHealthCheck({ doctor: true, silent: true });
617
+ }
618
+ if (typeof this.runProvidersHealthCheck === 'function') {
619
+ void this.runProvidersHealthCheck({ remote: true });
620
+ }
621
+ }
622
+ }
623
+ void this.refreshClaudeSelectionFromSettings({ silent: true });
624
+ void this.syncDefaultOpenclawConfigEntry({ silent: true });
625
+ };
626
+ if (typeof requestAnimationFrame === 'function') {
627
+ this._initialLoadRafId = requestAnimationFrame(() => {
628
+ this._initialLoadRafId = 0;
629
+ if (typeof setTimeout === 'function') {
630
+ this._initialLoadTimer = setTimeout(triggerLoad, 120);
631
+ return;
632
+ }
633
+ triggerLoad();
634
+ });
635
+ return;
636
+ }
637
+ if (typeof setTimeout === 'function') {
638
+ this._initialLoadTimer = setTimeout(triggerLoad, 120);
639
+ return;
640
+ }
641
+ triggerLoad();
642
+ };
643
+ if (document.readyState === 'complete') {
644
+ runInitialLoad();
645
+ } else {
646
+ this._initialLoadOnWindowLoad = () => {
647
+ if (typeof window !== 'undefined' && typeof window.removeEventListener === 'function') {
648
+ window.removeEventListener('load', this._initialLoadOnWindowLoad);
649
+ }
650
+ this._initialLoadOnWindowLoad = null;
651
+ runInitialLoad();
652
+ };
653
+ window.addEventListener('load', this._initialLoadOnWindowLoad, { once: true });
654
+ }
655
+ },
656
+
657
+ beforeUnmount() {
658
+ this.teardownSessionTabRender();
659
+ this.cancelScheduledSessionTabDeferredTeardown();
660
+ this.disconnectSessionPreviewHeaderResizeObserver();
661
+ if (this._initialLoadOnWindowLoad) {
662
+ window.removeEventListener('load', this._initialLoadOnWindowLoad);
663
+ this._initialLoadOnWindowLoad = null;
664
+ }
665
+ if (this._initialLoadRafId) {
666
+ cancelAnimationFrame(this._initialLoadRafId);
667
+ this._initialLoadRafId = 0;
668
+ }
669
+ if (this._initialLoadTimer) {
670
+ clearTimeout(this._initialLoadTimer);
671
+ this._initialLoadTimer = 0;
672
+ }
673
+ window.removeEventListener('resize', this.onWindowResize);
674
+ window.removeEventListener('keydown', this.handleGlobalKeydown);
675
+ window.removeEventListener('beforeunload', this.handleBeforeUnload);
676
+ this.applyCompactLayoutClass(false);
677
+ this.stopTaskOrchestrationPolling();
678
+ this.sessionPreviewScrollEl = null;
679
+ this.sessionPreviewContainerEl = null;
680
+ this.sessionPreviewHeaderEl = null;
681
+ this.clearSessionTimelineRefs();
682
+ },
683
+
684
+ computed: createAppComputed(),
685
+ methods: createAppMethods()
686
+ };
687
+
688
+ if (typeof window.__CODEXMATE_WEB_UI_RENDER__ === 'function') {
689
+ appOptions.render = window.__CODEXMATE_WEB_UI_RENDER__;
690
+ }
691
+
692
+ const app = createApp(appOptions);
693
+
694
+ app.mount('#app');
695
+ });