codexmate 0.0.21 → 0.0.23

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