codexmate 0.0.41 → 0.0.43

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 -168
  2. package/README.zh.md +168 -168
  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 -407
  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 +16495 -16458
  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 -311
  46. package/plugins/prompt-templates/index.mjs +8 -8
  47. package/plugins/prompt-templates/manifest.mjs +18 -18
  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 +705 -695
  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 -214
  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 -96
  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 +602 -601
  79. package/web-ui/modules/app.methods.runtime.mjs +426 -420
  80. package/web-ui/modules/app.methods.session-actions.mjs +617 -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 +557 -554
  85. package/web-ui/modules/app.methods.task-orchestration.mjs +556 -556
  86. package/web-ui/modules/app.methods.tool-config-permissions.mjs +90 -87
  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 +1187 -1140
  91. package/web-ui/modules/i18n/locales/ja.mjs +1178 -1130
  92. package/web-ui/modules/i18n/locales/vi.mjs +294 -239
  93. package/web-ui/modules/i18n/locales/zh.mjs +1190 -1143
  94. package/web-ui/modules/i18n.dict.mjs +14 -14
  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 -187
  113. package/web-ui/partials/index/panel-config-codex.html +205 -205
  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 +325 -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 +7671 -7666
  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 -532
  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 +492 -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,695 +1,705 @@
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
- });
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: (function() {
370
+ try {
371
+ const cached = localStorage.getItem('toolConfigPermissions');
372
+ if (cached) {
373
+ const parsed = JSON.parse(cached);
374
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
375
+ return {
376
+ codex: parsed.codex === true,
377
+ claude: parsed.claude === true
378
+ };
379
+ }
380
+ }
381
+ } catch (_) {}
382
+ return { codex: false, claude: false };
383
+ })(),
384
+ toolConfigPermissionSaving: { codex: false, claude: false },
385
+ sessionTrashEnabled: true,
386
+ sessionTrashItems: [],
387
+ sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE,
388
+ sessionTrashTotalCount: 0,
389
+ sessionTrashCountLoadedOnce: false,
390
+ sessionTrashLoadedOnce: false,
391
+ sessionTrashLastLoadFailed: false,
392
+ sessionTrashCountRequestToken: 0,
393
+ sessionTrashListRequestToken: 0,
394
+ sessionTrashCountPendingOptions: null,
395
+ sessionTrashPendingOptions: null,
396
+ sessionTrashCountLoading: false,
397
+ sessionTrashLoading: false,
398
+ sessionTrashRestoring: {},
399
+ sessionTrashPurging: {},
400
+ sessionTrashClearing: false,
401
+ sessionTrashRetentionDays: 30,
402
+ claudeImportLoading: false,
403
+ codexImportLoading: false,
404
+ codexAuthProfiles: [],
405
+ forceCompactLayout: false,
406
+ taskOrchestrationTabEnabled: true,
407
+ taskOrchestration: {
408
+ loading: false,
409
+ planning: false,
410
+ running: false,
411
+ queueAdding: false,
412
+ queueStarting: false,
413
+ retrying: false,
414
+ target: '',
415
+ title: '',
416
+ notes: '',
417
+ followUpsText: '',
418
+ workflowIdsText: '',
419
+ selectedEngine: 'codex',
420
+ runMode: 'write',
421
+ concurrency: 2,
422
+ autoFixRounds: 1,
423
+ plan: null,
424
+ planFingerprint: '',
425
+ planIssues: [],
426
+ planWarnings: [],
427
+ overviewWarnings: [],
428
+ workflows: [],
429
+ queue: [],
430
+ runs: [],
431
+ selectedRunId: '',
432
+ workspaceTab: 'queue',
433
+ selectedRunDetail: null,
434
+ selectedRunLoading: false,
435
+ selectedRunError: '',
436
+ detailRequestToken: 0,
437
+ lastLoadedAt: '',
438
+ lastError: ''
439
+ },
440
+ _taskOrchestrationPollTimer: 0,
441
+ webhookConfig: { enabled: false, url: '', events: ['provider-switch', 'claude-md-edit'] },
442
+ webhookEventOptions: ['provider-switch', 'claude-md-edit'],
443
+ webhookSaving: false,
444
+ webhookTestResult: null,
445
+ webhookTesting: false,
446
+ };
447
+ },
448
+
449
+ mounted() {
450
+ // URL 规范化:将 /web-ui/* 重定向到根路径 /
451
+ try {
452
+ const pathname = window.location.pathname;
453
+ if (pathname === '/web-ui' || pathname === '/web-ui/' || pathname === '/web-ui/index.html') {
454
+ const url = new URL(window.location.href);
455
+ url.pathname = '/';
456
+ // 移除查询参数和 hash,保持 URL 纯净
457
+ url.search = '';
458
+ url.hash = '';
459
+ window.location.replace(url.toString());
460
+ return;
461
+ }
462
+ // Do not strip query/hash during startup: /session uses them to identify the
463
+ // standalone session, and shareable tab/filter URLs are consumed below before
464
+ // later runtime canonicalization can clean the address bar.
465
+ } catch (_) {}
466
+
467
+ if (typeof this.initI18n === 'function') {
468
+ this.initI18n();
469
+ }
470
+ if (typeof this.loadWebhookSettings === 'function') {
471
+ this.loadWebhookSettings();
472
+ }
473
+ if (typeof this.t === 'function') {
474
+ this.confirmDialogConfirmText = this.t('confirm.ok');
475
+ this.confirmDialogCancelText = this.t('confirm.cancel');
476
+ this.agentsModalTitle = this.t('modal.agents.title');
477
+ this.agentsModalHint = this.t('modal.agents.hint');
478
+ }
479
+ {
480
+ const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
481
+ const mainTabSet = new Set(['dashboard', 'config', 'sessions', 'usage', 'orchestration', 'market', 'plugins', 'docs', 'settings', 'trash']);
482
+ let restored = null;
483
+ try {
484
+ const raw = localStorage.getItem(NAV_STATE_STORAGE_KEY) || '';
485
+ restored = raw ? JSON.parse(raw) : null;
486
+ } catch (_) {
487
+ restored = null;
488
+ }
489
+ const nextMainTab = restored && typeof restored.mainTab === 'string'
490
+ ? restored.mainTab.trim().toLowerCase()
491
+ : '';
492
+ const nextConfigMode = restored && typeof restored.configMode === 'string'
493
+ ? restored.configMode.trim().toLowerCase()
494
+ : '';
495
+ const nextSettingsTab = restored && typeof restored.settingsTab === 'string'
496
+ ? restored.settingsTab.trim().toLowerCase()
497
+ : '';
498
+ let urlMainTab = '';
499
+ try {
500
+ const url = new URL(window.location.href);
501
+ if (url.pathname !== '/session') {
502
+ urlMainTab = String(url.searchParams.get('tab') || '').trim().toLowerCase();
503
+ }
504
+ } catch (_) {
505
+ urlMainTab = '';
506
+ }
507
+ const resolvedMainTab = urlMainTab && mainTabSet.has(urlMainTab)
508
+ ? urlMainTab
509
+ : nextMainTab;
510
+ if (nextSettingsTab && (nextSettingsTab === 'general' || nextSettingsTab === 'data')) {
511
+ this.settingsTab = nextSettingsTab;
512
+ }
513
+ if (nextConfigMode && typeof this.switchConfigMode === 'function') {
514
+ this.__navStateRestoring = true;
515
+ try {
516
+ if (nextConfigMode === 'codex' || nextConfigMode === 'claude' || nextConfigMode === 'openclaw') {
517
+ this.configMode = nextConfigMode;
518
+ }
519
+ if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
520
+ this.switchMainTab(resolvedMainTab);
521
+ }
522
+ } finally {
523
+ this.__navStateRestoring = false;
524
+ }
525
+ } else if (resolvedMainTab && mainTabSet.has(resolvedMainTab) && resolvedMainTab !== this.mainTab) {
526
+ this.__navStateRestoring = true;
527
+ try {
528
+ this.switchMainTab(resolvedMainTab);
529
+ } finally {
530
+ this.__navStateRestoring = false;
531
+ }
532
+ }
533
+ }
534
+ this.initSessionStandalone();
535
+ this.updateCompactLayoutMode();
536
+ if (!this.taskOrchestrationTabEnabled && this.mainTab === 'orchestration') {
537
+ this.mainTab = 'config';
538
+ }
539
+ this.restoreSessionFilterCache();
540
+ this.restoreSessionPinnedMap();
541
+ this.shareCommandPrefix = this.normalizeShareCommandPrefix(localStorage.getItem('codexmateShareCommandPrefix'));
542
+ this.sessionTrashEnabled = this.normalizeSessionTrashEnabled(localStorage.getItem('codexmateSessionTrashEnabled'));
543
+ this.sessionTrashRetentionDays = this.normalizeSessionTrashRetentionDays(localStorage.getItem('codexmateSessionTrashRetentionDays'));
544
+ this.configTemplateDiffConfirmEnabled = loadConfigTemplateDiffConfirmEnabledFromStorage(localStorage);
545
+ window.addEventListener('resize', this.onWindowResize);
546
+ window.addEventListener('keydown', this.handleGlobalKeydown);
547
+ window.addEventListener('beforeunload', this.handleBeforeUnload);
548
+ const savedConfigs = localStorage.getItem('claudeConfigs');
549
+ if (savedConfigs) {
550
+ try {
551
+ this.claudeConfigs = JSON.parse(savedConfigs);
552
+ for (const [name, config] of Object.entries(this.claudeConfigs)) {
553
+ if (config.apiKey && config.apiKey.includes('****')) {
554
+ config.apiKey = '';
555
+ config.hasKey = false;
556
+ }
557
+ }
558
+ localStorage.setItem('claudeConfigs', JSON.stringify(this.claudeConfigs));
559
+ } catch (e) {
560
+ console.error('加载 Claude 配置失败:', e);
561
+ }
562
+ }
563
+ {
564
+ const savedCurrentClaudeConfig = localStorage.getItem('currentClaudeConfig');
565
+ if (savedCurrentClaudeConfig && this.claudeConfigs[savedCurrentClaudeConfig]) {
566
+ this.currentClaudeConfig = savedCurrentClaudeConfig;
567
+ }
568
+ }
569
+ if (!this.currentClaudeConfig) {
570
+ const claudeConfigNames = Object.keys(this.claudeConfigs || {});
571
+ if (claudeConfigNames.length > 0) {
572
+ this.currentClaudeConfig = claudeConfigNames[0];
573
+ }
574
+ }
575
+ if (this.currentClaudeConfig && !this.currentClaudeModel) {
576
+ const initialClaudeConfig = this.claudeConfigs[this.currentClaudeConfig];
577
+ this.currentClaudeModel = initialClaudeConfig && initialClaudeConfig.model ? initialClaudeConfig.model : '';
578
+ }
579
+ const normalizeOpenclawConfigs = (configs) => {
580
+ const source = configs && typeof configs === 'object' && !Array.isArray(configs)
581
+ ? configs
582
+ : {};
583
+ const defaultEntry = source['默认配置']
584
+ && typeof source['默认配置'] === 'object'
585
+ && !Array.isArray(source['默认配置'])
586
+ ? source['默认配置']
587
+ : { content: DEFAULT_OPENCLAW_TEMPLATE };
588
+ const normalized = {
589
+ '默认配置': {
590
+ content: typeof defaultEntry.content === 'string' ? defaultEntry.content : DEFAULT_OPENCLAW_TEMPLATE,
591
+ isDefault: true
592
+ }
593
+ };
594
+ for (const [name, value] of Object.entries(source)) {
595
+ if (name === '默认配置') continue;
596
+ normalized[name] = value;
597
+ }
598
+ return normalized;
599
+ };
600
+ const savedOpenclawConfigs = localStorage.getItem('openclawConfigs');
601
+ if (savedOpenclawConfigs) {
602
+ try {
603
+ this.openclawConfigs = normalizeOpenclawConfigs(JSON.parse(savedOpenclawConfigs));
604
+ } catch (e) {
605
+ console.error('加载 OpenClaw 配置失败:', e);
606
+ this.openclawConfigs = normalizeOpenclawConfigs(this.openclawConfigs);
607
+ }
608
+ } else {
609
+ this.openclawConfigs = normalizeOpenclawConfigs(this.openclawConfigs);
610
+ }
611
+ const configNames = Object.keys(this.openclawConfigs);
612
+ if (configNames.length > 0) {
613
+ this.currentOpenclawConfig = this.openclawConfigs['默认配置'] ? '默认配置' : configNames[0];
614
+ }
615
+ const runInitialLoad = () => {
616
+ const triggerLoad = async () => {
617
+ this._initialLoadTimer = 0;
618
+ const startupOk = await this.loadAll();
619
+ if (!startupOk) {
620
+ return;
621
+ }
622
+ if (this.mainTab === 'dashboard') {
623
+ if (!this.__doctorLoadedOnce) {
624
+ this.__doctorLoadedOnce = true;
625
+ if (typeof this.runHealthCheck === 'function') {
626
+ void this.runHealthCheck({ doctor: true, silent: true });
627
+ }
628
+ if (typeof this.runProvidersHealthCheck === 'function') {
629
+ void this.runProvidersHealthCheck({ remote: true });
630
+ }
631
+ }
632
+ }
633
+ void this.refreshClaudeSelectionFromSettings({ silent: true });
634
+ void this.syncDefaultOpenclawConfigEntry({ silent: true });
635
+ };
636
+ if (typeof requestAnimationFrame === 'function') {
637
+ this._initialLoadRafId = requestAnimationFrame(() => {
638
+ this._initialLoadRafId = 0;
639
+ if (typeof setTimeout === 'function') {
640
+ this._initialLoadTimer = setTimeout(triggerLoad, 120);
641
+ return;
642
+ }
643
+ triggerLoad();
644
+ });
645
+ return;
646
+ }
647
+ if (typeof setTimeout === 'function') {
648
+ this._initialLoadTimer = setTimeout(triggerLoad, 120);
649
+ return;
650
+ }
651
+ triggerLoad();
652
+ };
653
+ if (document.readyState === 'complete') {
654
+ runInitialLoad();
655
+ } else {
656
+ this._initialLoadOnWindowLoad = () => {
657
+ if (typeof window !== 'undefined' && typeof window.removeEventListener === 'function') {
658
+ window.removeEventListener('load', this._initialLoadOnWindowLoad);
659
+ }
660
+ this._initialLoadOnWindowLoad = null;
661
+ runInitialLoad();
662
+ };
663
+ window.addEventListener('load', this._initialLoadOnWindowLoad, { once: true });
664
+ }
665
+ },
666
+
667
+ beforeUnmount() {
668
+ this.teardownSessionTabRender();
669
+ this.cancelScheduledSessionTabDeferredTeardown();
670
+ this.disconnectSessionPreviewHeaderResizeObserver();
671
+ if (this._initialLoadOnWindowLoad) {
672
+ window.removeEventListener('load', this._initialLoadOnWindowLoad);
673
+ this._initialLoadOnWindowLoad = null;
674
+ }
675
+ if (this._initialLoadRafId) {
676
+ cancelAnimationFrame(this._initialLoadRafId);
677
+ this._initialLoadRafId = 0;
678
+ }
679
+ if (this._initialLoadTimer) {
680
+ clearTimeout(this._initialLoadTimer);
681
+ this._initialLoadTimer = 0;
682
+ }
683
+ window.removeEventListener('resize', this.onWindowResize);
684
+ window.removeEventListener('keydown', this.handleGlobalKeydown);
685
+ window.removeEventListener('beforeunload', this.handleBeforeUnload);
686
+ this.applyCompactLayoutClass(false);
687
+ this.stopTaskOrchestrationPolling();
688
+ this.sessionPreviewScrollEl = null;
689
+ this.sessionPreviewContainerEl = null;
690
+ this.sessionPreviewHeaderEl = null;
691
+ this.clearSessionTimelineRefs();
692
+ },
693
+
694
+ computed: createAppComputed(),
695
+ methods: createAppMethods()
696
+ };
697
+
698
+ if (typeof window.__CODEXMATE_WEB_UI_RENDER__ === 'function') {
699
+ appOptions.render = window.__CODEXMATE_WEB_UI_RENDER__;
700
+ }
701
+
702
+ const app = createApp(appOptions);
703
+
704
+ app.mount('#app');
705
+ });