codexmate 0.0.21 → 0.0.22

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 (114) hide show
  1. package/README.md +389 -284
  2. package/README.zh.md +321 -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 +575 -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 +198 -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 +161 -149
  52. package/web-ui/modules/app.methods.navigation.mjs +619 -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 +626 -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 +405 -412
  63. package/web-ui/modules/app.methods.task-orchestration.mjs +471 -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 +1609 -0
  67. package/web-ui/modules/plugins.computed.mjs +220 -0
  68. package/web-ui/modules/plugins.methods.mjs +620 -0
  69. package/web-ui/modules/plugins.storage.mjs +37 -0
  70. package/web-ui/modules/skills.computed.mjs +107 -107
  71. package/web-ui/modules/skills.methods.mjs +481 -481
  72. package/web-ui/partials/index/layout-footer.html +13 -13
  73. package/web-ui/partials/index/layout-header.html +461 -402
  74. package/web-ui/partials/index/modal-config-template-agents.html +175 -125
  75. package/web-ui/partials/index/modal-confirm-toast.html +32 -32
  76. package/web-ui/partials/index/modal-health-check.html +72 -72
  77. package/web-ui/partials/index/modal-openclaw-config.html +280 -280
  78. package/web-ui/partials/index/modal-skills.html +200 -184
  79. package/web-ui/partials/index/modals-basic.html +165 -156
  80. package/web-ui/partials/index/panel-config-claude.html +138 -126
  81. package/web-ui/partials/index/panel-config-codex.html +234 -237
  82. package/web-ui/partials/index/panel-config-openclaw.html +78 -78
  83. package/web-ui/partials/index/panel-docs.html +147 -130
  84. package/web-ui/partials/index/panel-market.html +174 -174
  85. package/web-ui/partials/index/panel-orchestration.html +397 -397
  86. package/web-ui/partials/index/panel-plugins.html +243 -0
  87. package/web-ui/partials/index/panel-sessions.html +292 -292
  88. package/web-ui/partials/index/panel-settings.html +258 -190
  89. package/web-ui/partials/index/panel-usage.html +353 -213
  90. package/web-ui/session-helpers.mjs +573 -559
  91. package/web-ui/source-bundle.cjs +233 -233
  92. package/web-ui/styles/base-theme.css +264 -271
  93. package/web-ui/styles/controls-forms.css +362 -360
  94. package/web-ui/styles/docs-panel.css +247 -182
  95. package/web-ui/styles/feedback.css +108 -108
  96. package/web-ui/styles/health-check-dialog.css +144 -144
  97. package/web-ui/styles/layout-shell.css +596 -376
  98. package/web-ui/styles/modals-core.css +464 -464
  99. package/web-ui/styles/navigation-panels.css +382 -348
  100. package/web-ui/styles/openclaw-structured.css +266 -266
  101. package/web-ui/styles/plugins-panel.css +518 -0
  102. package/web-ui/styles/responsive.css +456 -450
  103. package/web-ui/styles/sessions-list.css +400 -400
  104. package/web-ui/styles/sessions-preview.css +411 -411
  105. package/web-ui/styles/sessions-toolbar-trash.css +268 -243
  106. package/web-ui/styles/sessions-usage.css +851 -628
  107. package/web-ui/styles/settings-panel.css +166 -0
  108. package/web-ui/styles/skills-list.css +303 -296
  109. package/web-ui/styles/skills-market.css +396 -335
  110. package/web-ui/styles/task-orchestration.css +776 -776
  111. package/web-ui/styles/titles-cards.css +408 -408
  112. package/web-ui/styles.css +20 -18
  113. package/web-ui.html +17 -17
  114. package/README.en.md +0 -349
@@ -1,397 +1,397 @@
1
- <div
2
- v-if="taskOrchestrationTabEnabled"
3
- v-show="mainTab === 'orchestration'"
4
- class="mode-content"
5
- id="panel-orchestration"
6
- role="tabpanel"
7
- aria-labelledby="tab-orchestration">
8
- <section class="selector-section task-hero-card">
9
- <div class="task-hero-main">
10
- <div>
11
- <div class="task-hero-kicker">Task orchestration</div>
12
- <div class="selector-title">把需求拆成可执行步骤</div>
13
- <div class="skills-panel-note task-hero-copy">先写目标,再预览,再执行。</div>
14
- </div>
15
- <div class="task-hero-actions settings-tab-actions task-header-actions">
16
- <button type="button" class="btn-tool btn-tool-compact" @click="loadTaskOrchestrationOverview({ forceRefresh: true, includeDetail: true })" :disabled="taskOrchestration.loading">
17
- {{ taskOrchestration.loading ? '刷新中...' : '刷新' }}
18
- </button>
19
- <button type="button" class="btn-tool btn-tool-compact" @click="resetTaskOrchestrationDraft()" :disabled="taskOrchestration.running || taskOrchestration.queueAdding || taskOrchestration.planning">
20
- 重置草稿
21
- </button>
22
- </div>
23
- </div>
24
- <div v-if="taskOrchestrationQueueStats.running || taskOrchestrationQueueStats.queued || taskOrchestration.runs.length" class="task-hero-meta-strip" aria-label="任务编排概览">
25
- <div class="task-hero-meta">运行中 <strong>{{ taskOrchestrationQueueStats.running }}</strong></div>
26
- <div class="task-hero-meta">排队中 <strong>{{ taskOrchestrationQueueStats.queued }}</strong></div>
27
- <div class="task-hero-meta">运行记录 <strong>{{ taskOrchestration.runs.length }}</strong></div>
28
- </div>
29
- </section>
30
-
31
- <div class="task-layout-grid task-layout-grid-primary">
32
- <section class="selector-section task-compose-flow-card">
33
- <div class="task-flow-section task-flow-section-compact">
34
- <div class="task-flow-head">
35
- <div class="task-flow-step">1</div>
36
- <div>
37
- <div class="task-flow-title">先把结果写清楚</div>
38
- <div class="task-flow-copy">只写会影响执行的内容。</div>
39
- </div>
40
- </div>
41
-
42
- <details v-if="!taskOrchestration.target.trim()" class="task-template-panel">
43
- <summary class="task-advanced-summary">快捷示例</summary>
44
- <div class="task-template-block">
45
- <div class="task-template-chip-group">
46
- <button type="button" class="task-template-chip" @click="taskOrchestration.target = '修复当前 PR review 评论,并补对应回归测试'; taskOrchestration.selectedEngine = 'codex'; taskOrchestration.workflowIdsText = ''; taskOrchestration.notes = '不要改动无关模块;需要给出验证结果'; taskOrchestration.followUpsText = '继续处理新增 review 评论\n最后更新 PR 摘要'">修 review + 回归</button>
47
- <button type="button" class="task-template-chip" @click="taskOrchestration.target = '先排查问题根因并给出执行计划,不直接修改代码'; taskOrchestration.selectedEngine = 'codex'; taskOrchestration.workflowIdsText = ''; taskOrchestration.notes = '优先定位根因、影响范围和风险点'; taskOrchestration.followUpsText = ''">只做排查规划</button>
48
- <button type="button" class="task-template-chip" @click="taskOrchestration.target = '用 Workflow 跑一组固定检查并整理结果'; taskOrchestration.selectedEngine = 'workflow'; taskOrchestration.workflowIdsText = 'diagnose-config\nsafe-provider-switch'; taskOrchestration.notes = '输出统一结论,避免重复描述'; taskOrchestration.followUpsText = ''">Workflow 批处理</button>
49
- </div>
50
- </div>
51
- </details>
52
-
53
- <div class="selector-grid task-composer-grid task-composer-grid-primary">
54
- <label class="selector-field task-field task-field-wide task-goal-field">
55
- <span class="selector-label">目标</span>
56
- <textarea v-model="taskOrchestration.target" class="task-textarea task-textarea-goal" rows="5" placeholder="例如:修复当前 PR review 评论,并补对应回归测试;不要改无关模块"></textarea>
57
- <span class="task-field-hint">一句话写清结果、边界和验收标准就够了。</span>
58
- </label>
59
- </div>
60
-
61
- <div class="task-draft-overview task-draft-inline">
62
- <div class="task-draft-inline-head">
63
- <span :class="['pill', taskOrchestrationDraftReadiness.tone]">{{ taskOrchestrationDraftReadiness.title }}</span>
64
- <div class="task-readiness-copy task-draft-inline-copy">{{ taskOrchestrationDraftReadiness.summary }}</div>
65
- </div>
66
- <div class="task-config-strip">
67
- <div class="task-config-pill">{{ taskOrchestration.selectedEngine === 'workflow' ? 'Workflow' : 'Codex' }}</div>
68
- <div class="task-config-pill">{{ taskOrchestration.allowWrite ? '允许写入' : '只读' }}</div>
69
- <div class="task-config-pill">{{ taskOrchestration.dryRun ? '仅预演' : '真实执行' }}</div>
70
- <div v-if="taskOrchestration.title.trim()" class="task-config-pill">标题已设</div>
71
- <div v-if="taskOrchestration.selectedEngine === 'workflow' && taskOrchestrationDraftMetrics.workflowCount > 0" class="task-config-pill">Workflow {{ taskOrchestrationDraftMetrics.workflowCount }}</div>
72
- <div v-if="taskOrchestration.plan" class="task-config-pill">计划 {{ taskOrchestrationDraftMetrics.planNodeCount }} 节点</div>
73
- </div>
74
- </div>
75
- </div>
76
-
77
- <div class="task-flow-section task-flow-section-compact">
78
- <div class="task-flow-head">
79
- <div class="task-flow-step">2</div>
80
- <div>
81
- <div class="task-flow-title">选择执行方式</div>
82
- <div class="task-flow-copy">常用项默认展开,其余放高级设置。</div>
83
- </div>
84
- </div>
85
-
86
- <div class="selector-grid task-composer-grid task-composer-grid-compact">
87
- <label class="selector-field">
88
- <span class="selector-label">引擎</span>
89
- <select v-model="taskOrchestration.selectedEngine">
90
- <option value="codex">Codex</option>
91
- <option value="workflow">Workflow</option>
92
- </select>
93
- </label>
94
- <div class="selector-field task-toggle-card task-toggle-card-inline">
95
- <span class="selector-label">运行模式</span>
96
- <div class="task-toggle-inline">
97
- <label class="task-toggle">
98
- <input v-model="taskOrchestration.allowWrite" type="checkbox">
99
- <span>允许写入</span>
100
- </label>
101
- <label class="task-toggle">
102
- <input v-model="taskOrchestration.dryRun" type="checkbox">
103
- <span>仅预演</span>
104
- </label>
105
- </div>
106
- </div>
107
- </div>
108
-
109
- <details class="task-advanced-panel">
110
- <summary class="task-advanced-summary">高级设置</summary>
111
- <div class="selector-grid task-composer-grid task-composer-grid-secondary">
112
- <label class="selector-field task-field-wide">
113
- <span class="selector-label">标题</span>
114
- <input v-model="taskOrchestration.title" type="text" placeholder="可选,默认从目标自动提取">
115
- </label>
116
- <label class="selector-field task-field-wide">
117
- <span class="selector-label">说明</span>
118
- <textarea v-model="taskOrchestration.notes" class="task-textarea" rows="3" placeholder="例如:不要重写现有架构,只做增量实现"></textarea>
119
- <span class="task-field-hint">补边界、禁区、风格要求或验证要求。</span>
120
- </label>
121
- <label class="selector-field task-field-wide">
122
- <span class="selector-label">后续动作(每行一条)</span>
123
- <textarea v-model="taskOrchestration.followUpsText" class="task-textarea" rows="3" placeholder="例如:\n继续处理 review 评论\n最后补回归测试"></textarea>
124
- </label>
125
- <label class="selector-field">
126
- <span class="selector-label">并发</span>
127
- <input v-model="taskOrchestration.concurrency" type="number" min="1" max="8">
128
- <span class="task-field-hint">复杂任务先从 1~2 开始更稳。</span>
129
- </label>
130
- <label class="selector-field">
131
- <span class="selector-label">自动修复</span>
132
- <input v-model="taskOrchestration.autoFixRounds" type="number" min="0" max="5">
133
- <span class="task-field-hint">失败后自动再试几轮。</span>
134
- </label>
135
- <label v-if="taskOrchestration.selectedEngine === 'workflow'" class="selector-field task-field-wide">
136
- <span class="selector-label">Workflow ID(每行一条)</span>
137
- <textarea v-model="taskOrchestration.workflowIdsText" class="task-textarea" rows="3" placeholder="例如:\ndiagnose-config\nsafe-provider-switch"></textarea>
138
- <span class="task-field-hint">仅 Workflow 模式需要。当前本地可用 {{ taskOrchestration.workflows.length }} 个。</span>
139
- <div v-if="taskOrchestration.workflows.length" class="task-workflow-suggestions">
140
- <button
141
- v-for="workflow in taskOrchestration.workflows"
142
- :key="workflow.id || workflow.name"
143
- type="button"
144
- class="task-workflow-chip"
145
- @click="appendTaskWorkflowId(workflow.id || workflow.name)">
146
- <span>{{ workflow.name || workflow.id }}</span>
147
- <small v-if="workflow.stepCount">{{ workflow.stepCount }} 步</small>
148
- </button>
149
- </div>
150
- </label>
151
- </div>
152
- </details>
153
- </div>
154
-
155
- <div class="task-flow-section task-flow-section-actions task-flow-section-compact">
156
- <div class="task-flow-head">
157
- <div class="task-flow-step">3</div>
158
- <div>
159
- <div class="task-flow-title">先预览,再执行</div>
160
- <div class="task-flow-copy">先确认计划,再决定立即执行还是入队。</div>
161
- </div>
162
- </div>
163
- <div class="task-action-row task-action-row-prominent">
164
- <button type="button" class="btn-tool task-action-preview" @click="previewTaskPlan()" :disabled="taskOrchestration.planning || taskOrchestration.running || !taskOrchestration.target.trim()">
165
- {{ taskOrchestration.planning ? '规划中...' : '预览计划' }}
166
- </button>
167
- <div class="task-action-row-right task-action-row-right-prominent">
168
- <button type="button" class="btn-tool btn-primary" @click="runTaskOrchestration()" :disabled="taskOrchestration.running || !taskOrchestration.target.trim()">
169
- {{ taskOrchestration.running ? '启动中...' : '立即执行' }}
170
- </button>
171
- <button type="button" class="btn-tool" @click="addTaskOrchestrationToQueue()" :disabled="taskOrchestration.queueAdding || !taskOrchestration.target.trim()">
172
- {{ taskOrchestration.queueAdding ? '加入中...' : '加入队列' }}
173
- </button>
174
- <button v-if="taskOrchestration.queue.length" type="button" class="btn-tool" @click="startTaskQueueRunner()" :disabled="taskOrchestration.queueStarting">
175
- {{ taskOrchestration.queueStarting ? '启动中...' : '开始队列' }}
176
- </button>
177
- </div>
178
- </div>
179
- <div class="task-action-caption">预览不会执行。批量任务再入队。</div>
180
- </div>
181
- </section>
182
- </div>
183
-
184
- <section
185
- v-if="!(taskOrchestration.plan || taskOrchestration.planIssues.length || taskOrchestration.planWarnings.length || taskOrchestration.lastError || taskOrchestration.queue.length || taskOrchestration.runs.length || taskOrchestration.selectedRunId || taskOrchestration.selectedRunError)"
186
- class="selector-section task-stage-card">
187
- <div class="task-stage-empty">
188
- <div>
189
- <div class="selector-title">有内容时再展开工作区</div>
190
- <div class="skills-panel-note">先写目标,再预览计划。</div>
191
- </div>
192
- <div class="task-stage-strip">
193
- <div class="task-stage-pill">写目标</div>
194
- <div class="task-stage-pill">预览</div>
195
- <div class="task-stage-pill">执行或入队</div>
196
- </div>
197
- </div>
198
- </section>
199
-
200
- <div
201
- v-else
202
- class="task-layout-grid task-layout-grid-secondary">
203
- <section
204
- v-if="taskOrchestration.plan || taskOrchestration.planIssues.length || taskOrchestration.planWarnings.length || taskOrchestration.lastError"
205
- class="selector-section task-plan-card">
206
- <div v-if="taskOrchestration.lastError" class="task-issue-item">{{ taskOrchestration.lastError }}</div>
207
- <div class="selector-header task-section-header">
208
- <div>
209
- <span class="selector-title">计划预览</span>
210
- <div class="skills-panel-note">先确认节点、波次和依赖。</div>
211
- </div>
212
- </div>
213
- <div v-if="taskOrchestration.planIssues.length" class="task-issues-list">
214
- <div v-for="issue in taskOrchestration.planIssues" :key="issue.code + issue.message" class="task-issue-item">
215
- {{ issue.message }}
216
- </div>
217
- </div>
218
- <div v-if="taskOrchestration.planWarnings.length" class="task-warning-list">
219
- <div v-for="warning in taskOrchestration.planWarnings" :key="warning" class="task-warning-item">
220
- {{ warning }}
221
- </div>
222
- </div>
223
- <template v-if="taskOrchestration.plan">
224
- <div class="task-plan-summary-strip">
225
- <div class="task-plan-summary-item">
226
- <span class="task-plan-summary-label">节点数</span>
227
- <strong>{{ taskOrchestration.plan.nodes.length }}</strong>
228
- </div>
229
- <div class="task-plan-summary-item">
230
- <span class="task-plan-summary-label">波次</span>
231
- <strong>{{ taskOrchestration.plan.waves.length }}</strong>
232
- </div>
233
- <div class="task-plan-summary-item">
234
- <span class="task-plan-summary-label">引擎</span>
235
- <strong>{{ taskOrchestration.plan.engine }}</strong>
236
- </div>
237
- </div>
238
- <div class="task-wave-list">
239
- <div v-for="wave in taskOrchestration.plan.waves" :key="wave.label" class="task-wave-card">
240
- <div class="task-wave-title">{{ wave.label }}</div>
241
- <div class="task-wave-nodes">{{ wave.nodeIds.join(', ') }}</div>
242
- </div>
243
- </div>
244
- <div class="task-node-list">
245
- <div v-for="node in taskOrchestration.plan.nodes" :key="node.id" class="task-node-card">
246
- <div class="task-node-head">
247
- <div>
248
- <div class="task-node-title">{{ node.title || node.id }}</div>
249
- <div class="task-node-meta">{{ node.id }} · {{ node.kind }}<span v-if="node.workflowId"> · {{ node.workflowId }}</span></div>
250
- </div>
251
- <span :class="['pill', node.write ? 'configured' : 'empty']">{{ node.write ? 'write' : 'read-only' }}</span>
252
- </div>
253
- <div class="task-node-deps">依赖:{{ formatTaskNodeDependencies(node) }}</div>
254
- </div>
255
- </div>
256
- </template>
257
- </section>
258
-
259
- <section
260
- v-if="taskOrchestration.queue.length || taskOrchestration.runs.length || taskOrchestration.selectedRunId || taskOrchestration.selectedRunError"
261
- class="selector-section task-workbench-card">
262
- <div class="selector-header task-section-header">
263
- <div>
264
- <span class="selector-title">执行工作台</span>
265
- <div class="skills-panel-note">有内容时才展开。</div>
266
- </div>
267
- <div class="settings-tab-actions task-header-actions">
268
- <button type="button" class="btn-tool btn-tool-compact" @click="loadTaskOrchestrationOverview({ forceRefresh: true, includeDetail: true })" :disabled="taskOrchestration.loading">
269
- {{ taskOrchestration.loading ? '刷新中...' : '刷新' }}
270
- </button>
271
- <button v-if="taskOrchestration.queue.length" type="button" class="btn-tool btn-tool-compact" @click="startTaskQueueRunner()" :disabled="taskOrchestration.queueStarting">
272
- {{ taskOrchestration.queueStarting ? '启动中...' : '开始队列' }}
273
- </button>
274
- </div>
275
- </div>
276
-
277
- <div
278
- v-if="(taskOrchestration.queue.length ? 1 : 0) + (taskOrchestration.runs.length ? 1 : 0) + ((taskOrchestration.selectedRunId || taskOrchestration.selectedRunError) ? 1 : 0) > 1"
279
- class="task-workbench-tabs"
280
- role="group"
281
- aria-label="任务编排工作台视图">
282
- <button v-if="taskOrchestration.queue.length" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'queue' }" @click="taskOrchestration.workspaceTab = 'queue'">队列 {{ taskOrchestration.queue.length }}</button>
283
- <button v-if="taskOrchestration.runs.length" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'runs' }" @click="taskOrchestration.workspaceTab = 'runs'">运行记录 {{ taskOrchestration.runs.length }}</button>
284
- <button v-if="taskOrchestration.selectedRunId || taskOrchestration.selectedRunError" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'detail' }" @click="taskOrchestration.workspaceTab = 'detail'">运行详情</button>
285
- </div>
286
-
287
- <div v-if="taskOrchestration.workspaceTab === 'queue' || (!taskOrchestration.runs.length && !taskOrchestration.selectedRunId && !taskOrchestration.selectedRunError)" class="task-workbench-panel">
288
- <div v-if="!taskOrchestration.queue.length" class="task-empty-state">
289
- <div class="task-empty-title">当前没有排队任务</div>
290
- <div class="task-empty-copy">批量任务可先入队,再启动队列。</div>
291
- </div>
292
- <div v-else class="task-runtime-list">
293
- <div
294
- v-for="item in taskOrchestration.queue"
295
- :key="item.taskId"
296
- :class="['task-runtime-item', { active: item.lastRunId && taskOrchestration.selectedRunId === item.lastRunId, clickable: !!item.lastRunId }]"
297
- :role="item.lastRunId ? 'button' : null"
298
- :tabindex="item.lastRunId ? 0 : -1"
299
- :aria-disabled="item.lastRunId ? null : 'true'"
300
- @click="item.lastRunId ? (taskOrchestration.workspaceTab = 'detail', selectTaskRun(item.lastRunId)) : null"
301
- @keydown.enter.self.prevent="item.lastRunId ? (taskOrchestration.workspaceTab = 'detail', selectTaskRun(item.lastRunId)) : null"
302
- @keydown.space.self.prevent="item.lastRunId ? (taskOrchestration.workspaceTab = 'detail', selectTaskRun(item.lastRunId)) : null">
303
- <div class="task-runtime-item-main">
304
- <div class="task-runtime-item-title">{{ item.title || item.target || item.taskId }}</div>
305
- <div class="task-runtime-item-meta">{{ item.taskId }} · {{ item.updatedAt || item.createdAt }}</div>
306
- <div v-if="item.lastSummary" class="task-runtime-item-summary">{{ item.lastSummary }}</div>
307
- </div>
308
- <div class="task-runtime-item-actions">
309
- <span :class="['pill', taskRunStatusTone(item.status)]">{{ item.status }}</span>
310
- <button type="button" class="btn-mini" @click.stop="cancelTaskRunFromUi(item.taskId)" :disabled="item.status !== 'queued' && item.status !== 'running'">取消</button>
311
- </div>
312
- </div>
313
- </div>
314
- </div>
315
-
316
- <div v-else-if="taskOrchestration.workspaceTab === 'runs' || (!taskOrchestration.queue.length && taskOrchestration.runs.length && !taskOrchestration.selectedRunId && !taskOrchestration.selectedRunError)" class="task-workbench-panel">
317
- <div v-if="!taskOrchestration.runs.length" class="task-empty-state">
318
- <div class="task-empty-title">还没有运行记录</div>
319
- <div class="task-empty-copy">执行后会显示最近运行记录。</div>
320
- </div>
321
- <div v-else class="task-runtime-list">
322
- <button
323
- v-for="item in taskOrchestration.runs"
324
- :key="item.runId"
325
- type="button"
326
- :class="['task-runtime-item', { active: taskOrchestration.selectedRunId === item.runId }]"
327
- @click="taskOrchestration.workspaceTab = 'detail'; selectTaskRun(item.runId)">
328
- <div class="task-runtime-item-main">
329
- <div class="task-runtime-item-title">{{ item.title || item.taskId || item.runId }}</div>
330
- <div class="task-runtime-item-meta">{{ item.runId }} · {{ item.durationMs || 0 }}ms</div>
331
- <div v-if="item.summary" class="task-runtime-item-summary">{{ item.summary }}</div>
332
- </div>
333
- <div class="task-runtime-item-actions">
334
- <span :class="['pill', taskRunStatusTone(item.status)]">{{ item.status }}</span>
335
- </div>
336
- </button>
337
- </div>
338
- </div>
339
-
340
- <div v-else class="task-workbench-panel">
341
- <div class="task-detail-toolbar settings-tab-actions">
342
- <button type="button" class="btn-tool btn-tool-compact" @click="taskOrchestration.selectedRunId ? loadTaskRunDetail(taskOrchestration.selectedRunId) : null" :disabled="!taskOrchestration.selectedRunId || taskOrchestration.selectedRunLoading">
343
- {{ taskOrchestration.selectedRunLoading ? '刷新中...' : '刷新详情' }}
344
- </button>
345
- <button type="button" class="btn-tool btn-tool-compact" @click="retryTaskRunFromUi(taskOrchestration.selectedRunId)" :disabled="!taskOrchestration.selectedRunId || taskOrchestration.retrying">
346
- {{ taskOrchestration.retrying ? '重试中...' : '重试' }}
347
- </button>
348
- <button type="button" class="btn-tool btn-tool-compact" @click="cancelTaskRunFromUi(taskOrchestration.selectedRunId)" :disabled="!taskOrchestrationSelectedRun || !taskOrchestrationSelectedRun.run || !isTaskRunActive(taskOrchestrationSelectedRun.run.status)">
349
- 取消
350
- </button>
351
- </div>
352
-
353
- <div v-if="taskOrchestration.selectedRunError" class="task-issue-item">{{ taskOrchestration.selectedRunError }}</div>
354
- <div v-if="!taskOrchestrationSelectedRun" class="task-empty-state">
355
- <div class="task-empty-title">选择一条运行记录查看详情</div>
356
- <div class="task-empty-copy">这里会显示节点状态、摘要和日志。</div>
357
- </div>
358
- <template v-else>
359
- <div class="task-detail-summary-strip">
360
- <div class="task-plan-summary-item">
361
- <span class="task-plan-summary-label">状态</span>
362
- <strong>{{ taskOrchestrationSelectedRun.run.status }}</strong>
363
- </div>
364
- <div class="task-plan-summary-item">
365
- <span class="task-plan-summary-label">耗时</span>
366
- <strong>{{ taskOrchestrationSelectedRun.run.durationMs || 0 }}ms</strong>
367
- </div>
368
- <div class="task-plan-summary-item">
369
- <span class="task-plan-summary-label">节点数</span>
370
- <strong>{{ taskOrchestrationSelectedRunNodes.length }}</strong>
371
- </div>
372
- <div class="task-plan-summary-item">
373
- <span class="task-plan-summary-label">摘要</span>
374
- <strong>{{ taskOrchestrationSelectedRun.run.summary || '暂无' }}</strong>
375
- </div>
376
- </div>
377
- <div v-if="taskOrchestrationSelectedRun.run.error" class="task-issue-item">{{ taskOrchestrationSelectedRun.run.error }}</div>
378
- <div class="task-node-list">
379
- <div v-for="node in taskOrchestrationSelectedRunNodes" :key="node.id" class="task-node-card task-node-card-detail">
380
- <div class="task-node-head">
381
- <div>
382
- <div class="task-node-title">{{ node.title || node.id }}</div>
383
- <div class="task-node-meta">{{ node.id }} · attempts {{ node.attemptCount || 0 }} · auto-fix {{ node.autoFixRounds || 0 }}</div>
384
- </div>
385
- <span :class="['pill', taskRunStatusTone(node.status)]">{{ node.status }}</span>
386
- </div>
387
- <div v-if="node.summary" class="task-runtime-item-summary">{{ node.summary }}</div>
388
- <div v-if="node.error && node.error !== node.summary" class="task-node-deps">错误:{{ node.error }}</div>
389
- <div class="task-node-deps">依赖:{{ formatTaskNodeDependencies(node) }}</div>
390
- <pre class="task-log-block">{{ formatTaskNodeLogs(node.logs) }}</pre>
391
- </div>
392
- </div>
393
- </template>
394
- </div>
395
- </section>
396
- </div>
397
- </div>
1
+ <div
2
+ v-if="taskOrchestrationTabEnabled"
3
+ v-show="mainTab === 'orchestration'"
4
+ class="mode-content"
5
+ id="panel-orchestration"
6
+ role="tabpanel"
7
+ aria-labelledby="tab-orchestration">
8
+ <section class="selector-section task-hero-card">
9
+ <div class="task-hero-main">
10
+ <div>
11
+ <div class="task-hero-kicker">Task orchestration</div>
12
+ <div class="selector-title">把需求拆成可执行步骤</div>
13
+ <div class="skills-panel-note task-hero-copy">先写目标,再预览,再执行。</div>
14
+ </div>
15
+ <div class="task-hero-actions settings-tab-actions task-header-actions">
16
+ <button type="button" class="btn-tool btn-tool-compact" @click="loadTaskOrchestrationOverview({ forceRefresh: true, includeDetail: true })" :disabled="taskOrchestration.loading">
17
+ {{ taskOrchestration.loading ? '刷新中...' : '刷新' }}
18
+ </button>
19
+ <button type="button" class="btn-tool btn-tool-compact" @click="resetTaskOrchestrationDraft()" :disabled="taskOrchestration.running || taskOrchestration.queueAdding || taskOrchestration.planning">
20
+ 重置草稿
21
+ </button>
22
+ </div>
23
+ </div>
24
+ <div v-if="taskOrchestrationQueueStats.running || taskOrchestrationQueueStats.queued || taskOrchestration.runs.length" class="task-hero-meta-strip" aria-label="任务编排概览">
25
+ <div class="task-hero-meta">运行中 <strong>{{ taskOrchestrationQueueStats.running }}</strong></div>
26
+ <div class="task-hero-meta">排队中 <strong>{{ taskOrchestrationQueueStats.queued }}</strong></div>
27
+ <div class="task-hero-meta">运行记录 <strong>{{ taskOrchestration.runs.length }}</strong></div>
28
+ </div>
29
+ </section>
30
+
31
+ <div class="task-layout-grid task-layout-grid-primary">
32
+ <section class="selector-section task-compose-flow-card">
33
+ <div class="task-flow-section task-flow-section-compact">
34
+ <div class="task-flow-head">
35
+ <div class="task-flow-step">1</div>
36
+ <div>
37
+ <div class="task-flow-title">先把结果写清楚</div>
38
+ <div class="task-flow-copy">只写会影响执行的内容。</div>
39
+ </div>
40
+ </div>
41
+
42
+ <details v-if="!taskOrchestration.target.trim()" class="task-template-panel">
43
+ <summary class="task-advanced-summary">快捷示例</summary>
44
+ <div class="task-template-block">
45
+ <div class="task-template-chip-group">
46
+ <button type="button" class="task-template-chip" @click="taskOrchestration.target = '修复当前 PR review 评论,并补对应回归测试'; taskOrchestration.selectedEngine = 'codex'; taskOrchestration.workflowIdsText = ''; taskOrchestration.notes = '不要改动无关模块;需要给出验证结果'; taskOrchestration.followUpsText = '继续处理新增 review 评论\n最后更新 PR 摘要'">修 review + 回归</button>
47
+ <button type="button" class="task-template-chip" @click="taskOrchestration.target = '先排查问题根因并给出执行计划,不直接修改代码'; taskOrchestration.selectedEngine = 'codex'; taskOrchestration.workflowIdsText = ''; taskOrchestration.notes = '优先定位根因、影响范围和风险点'; taskOrchestration.followUpsText = ''">只做排查规划</button>
48
+ <button type="button" class="task-template-chip" @click="taskOrchestration.target = '用 Workflow 跑一组固定检查并整理结果'; taskOrchestration.selectedEngine = 'workflow'; taskOrchestration.workflowIdsText = 'diagnose-config\nsafe-provider-switch'; taskOrchestration.notes = '输出统一结论,避免重复描述'; taskOrchestration.followUpsText = ''">Workflow 批处理</button>
49
+ </div>
50
+ </div>
51
+ </details>
52
+
53
+ <div class="selector-grid task-composer-grid task-composer-grid-primary">
54
+ <label class="selector-field task-field task-field-wide task-goal-field">
55
+ <span class="selector-label">目标</span>
56
+ <textarea v-model="taskOrchestration.target" class="task-textarea task-textarea-goal" rows="5" placeholder="例如:修复当前 PR review 评论,并补对应回归测试;不要改无关模块"></textarea>
57
+ <span class="task-field-hint">一句话写清结果、边界和验收标准就够了。</span>
58
+ </label>
59
+ </div>
60
+
61
+ <div class="task-draft-overview task-draft-inline">
62
+ <div class="task-draft-inline-head">
63
+ <span :class="['pill', taskOrchestrationDraftReadiness.tone]">{{ taskOrchestrationDraftReadiness.title }}</span>
64
+ <div class="task-readiness-copy task-draft-inline-copy">{{ taskOrchestrationDraftReadiness.summary }}</div>
65
+ </div>
66
+ <div class="task-config-strip">
67
+ <div class="task-config-pill">{{ taskOrchestration.selectedEngine === 'workflow' ? 'Workflow' : 'Codex' }}</div>
68
+ <div class="task-config-pill">{{ taskOrchestration.allowWrite ? '允许写入' : '只读' }}</div>
69
+ <div class="task-config-pill">{{ taskOrchestration.dryRun ? '仅预演' : '真实执行' }}</div>
70
+ <div v-if="taskOrchestration.title.trim()" class="task-config-pill">标题已设</div>
71
+ <div v-if="taskOrchestration.selectedEngine === 'workflow' && taskOrchestrationDraftMetrics.workflowCount > 0" class="task-config-pill">Workflow {{ taskOrchestrationDraftMetrics.workflowCount }}</div>
72
+ <div v-if="taskOrchestration.plan" class="task-config-pill">计划 {{ taskOrchestrationDraftMetrics.planNodeCount }} 节点</div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+
77
+ <div class="task-flow-section task-flow-section-compact">
78
+ <div class="task-flow-head">
79
+ <div class="task-flow-step">2</div>
80
+ <div>
81
+ <div class="task-flow-title">选择执行方式</div>
82
+ <div class="task-flow-copy">常用项默认展开,其余放高级设置。</div>
83
+ </div>
84
+ </div>
85
+
86
+ <div class="selector-grid task-composer-grid task-composer-grid-compact">
87
+ <label class="selector-field">
88
+ <span class="selector-label">引擎</span>
89
+ <select v-model="taskOrchestration.selectedEngine">
90
+ <option value="codex">Codex</option>
91
+ <option value="workflow">Workflow</option>
92
+ </select>
93
+ </label>
94
+ <div class="selector-field task-toggle-card task-toggle-card-inline">
95
+ <span class="selector-label">运行模式</span>
96
+ <div class="task-toggle-inline">
97
+ <label class="task-toggle">
98
+ <input v-model="taskOrchestration.allowWrite" type="checkbox">
99
+ <span>允许写入</span>
100
+ </label>
101
+ <label class="task-toggle">
102
+ <input v-model="taskOrchestration.dryRun" type="checkbox">
103
+ <span>仅预演</span>
104
+ </label>
105
+ </div>
106
+ </div>
107
+ </div>
108
+
109
+ <details class="task-advanced-panel">
110
+ <summary class="task-advanced-summary">高级设置</summary>
111
+ <div class="selector-grid task-composer-grid task-composer-grid-secondary">
112
+ <label class="selector-field task-field-wide">
113
+ <span class="selector-label">标题</span>
114
+ <input v-model="taskOrchestration.title" type="text" placeholder="可选,默认从目标自动提取">
115
+ </label>
116
+ <label class="selector-field task-field-wide">
117
+ <span class="selector-label">说明</span>
118
+ <textarea v-model="taskOrchestration.notes" class="task-textarea" rows="3" placeholder="例如:不要重写现有架构,只做增量实现"></textarea>
119
+ <span class="task-field-hint">补边界、禁区、风格要求或验证要求。</span>
120
+ </label>
121
+ <label class="selector-field task-field-wide">
122
+ <span class="selector-label">后续动作(每行一条)</span>
123
+ <textarea v-model="taskOrchestration.followUpsText" class="task-textarea" rows="3" placeholder="例如:\n继续处理 review 评论\n最后补回归测试"></textarea>
124
+ </label>
125
+ <label class="selector-field">
126
+ <span class="selector-label">并发</span>
127
+ <input v-model="taskOrchestration.concurrency" type="number" min="1" max="8">
128
+ <span class="task-field-hint">复杂任务先从 1~2 开始更稳。</span>
129
+ </label>
130
+ <label class="selector-field">
131
+ <span class="selector-label">自动修复</span>
132
+ <input v-model="taskOrchestration.autoFixRounds" type="number" min="0" max="5">
133
+ <span class="task-field-hint">失败后自动再试几轮。</span>
134
+ </label>
135
+ <label v-if="taskOrchestration.selectedEngine === 'workflow'" class="selector-field task-field-wide">
136
+ <span class="selector-label">Workflow ID(每行一条)</span>
137
+ <textarea v-model="taskOrchestration.workflowIdsText" class="task-textarea" rows="3" placeholder="例如:\ndiagnose-config\nsafe-provider-switch"></textarea>
138
+ <span class="task-field-hint">仅 Workflow 模式需要。当前本地可用 {{ taskOrchestration.workflows.length }} 个。</span>
139
+ <div v-if="taskOrchestration.workflows.length" class="task-workflow-suggestions">
140
+ <button
141
+ v-for="workflow in taskOrchestration.workflows"
142
+ :key="workflow.id || workflow.name"
143
+ type="button"
144
+ class="task-workflow-chip"
145
+ @click="appendTaskWorkflowId(workflow.id || workflow.name)">
146
+ <span>{{ workflow.name || workflow.id }}</span>
147
+ <small v-if="workflow.stepCount">{{ workflow.stepCount }} 步</small>
148
+ </button>
149
+ </div>
150
+ </label>
151
+ </div>
152
+ </details>
153
+ </div>
154
+
155
+ <div class="task-flow-section task-flow-section-actions task-flow-section-compact">
156
+ <div class="task-flow-head">
157
+ <div class="task-flow-step">3</div>
158
+ <div>
159
+ <div class="task-flow-title">先预览,再执行</div>
160
+ <div class="task-flow-copy">先确认计划,再决定立即执行还是入队。</div>
161
+ </div>
162
+ </div>
163
+ <div class="task-action-row task-action-row-prominent">
164
+ <button type="button" class="btn-tool task-action-preview" @click="previewTaskPlan()" :disabled="taskOrchestration.planning || taskOrchestration.running || !taskOrchestration.target.trim()">
165
+ {{ taskOrchestration.planning ? '规划中...' : '预览计划' }}
166
+ </button>
167
+ <div class="task-action-row-right task-action-row-right-prominent">
168
+ <button type="button" class="btn-tool btn-primary" @click="runTaskOrchestration()" :disabled="taskOrchestration.running || !taskOrchestration.target.trim()">
169
+ {{ taskOrchestration.running ? '启动中...' : '立即执行' }}
170
+ </button>
171
+ <button type="button" class="btn-tool" @click="addTaskOrchestrationToQueue()" :disabled="taskOrchestration.queueAdding || !taskOrchestration.target.trim()">
172
+ {{ taskOrchestration.queueAdding ? '加入中...' : '加入队列' }}
173
+ </button>
174
+ <button v-if="taskOrchestration.queue.length" type="button" class="btn-tool" @click="startTaskQueueRunner()" :disabled="taskOrchestration.queueStarting">
175
+ {{ taskOrchestration.queueStarting ? '启动中...' : '开始队列' }}
176
+ </button>
177
+ </div>
178
+ </div>
179
+ <div class="task-action-caption">预览不会执行。批量任务再入队。</div>
180
+ </div>
181
+ </section>
182
+ </div>
183
+
184
+ <section
185
+ v-if="!(taskOrchestration.plan || taskOrchestration.planIssues.length || taskOrchestration.planWarnings.length || taskOrchestration.lastError || taskOrchestration.queue.length || taskOrchestration.runs.length || taskOrchestration.selectedRunId || taskOrchestration.selectedRunError)"
186
+ class="selector-section task-stage-card">
187
+ <div class="task-stage-empty">
188
+ <div>
189
+ <div class="selector-title">有内容时再展开工作区</div>
190
+ <div class="skills-panel-note">先写目标,再预览计划。</div>
191
+ </div>
192
+ <div class="task-stage-strip">
193
+ <div class="task-stage-pill">写目标</div>
194
+ <div class="task-stage-pill">预览</div>
195
+ <div class="task-stage-pill">执行或入队</div>
196
+ </div>
197
+ </div>
198
+ </section>
199
+
200
+ <div
201
+ v-else
202
+ class="task-layout-grid task-layout-grid-secondary">
203
+ <section
204
+ v-if="taskOrchestration.plan || taskOrchestration.planIssues.length || taskOrchestration.planWarnings.length || taskOrchestration.lastError"
205
+ class="selector-section task-plan-card">
206
+ <div v-if="taskOrchestration.lastError" class="task-issue-item">{{ taskOrchestration.lastError }}</div>
207
+ <div class="selector-header task-section-header">
208
+ <div>
209
+ <span class="selector-title">计划预览</span>
210
+ <div class="skills-panel-note">先确认节点、波次和依赖。</div>
211
+ </div>
212
+ </div>
213
+ <div v-if="taskOrchestration.planIssues.length" class="task-issues-list">
214
+ <div v-for="issue in taskOrchestration.planIssues" :key="issue.code + issue.message" class="task-issue-item">
215
+ {{ issue.message }}
216
+ </div>
217
+ </div>
218
+ <div v-if="taskOrchestration.planWarnings.length" class="task-warning-list">
219
+ <div v-for="warning in taskOrchestration.planWarnings" :key="warning" class="task-warning-item">
220
+ {{ warning }}
221
+ </div>
222
+ </div>
223
+ <template v-if="taskOrchestration.plan">
224
+ <div class="task-plan-summary-strip">
225
+ <div class="task-plan-summary-item">
226
+ <span class="task-plan-summary-label">节点数</span>
227
+ <strong>{{ taskOrchestration.plan.nodes.length }}</strong>
228
+ </div>
229
+ <div class="task-plan-summary-item">
230
+ <span class="task-plan-summary-label">波次</span>
231
+ <strong>{{ taskOrchestration.plan.waves.length }}</strong>
232
+ </div>
233
+ <div class="task-plan-summary-item">
234
+ <span class="task-plan-summary-label">引擎</span>
235
+ <strong>{{ taskOrchestration.plan.engine }}</strong>
236
+ </div>
237
+ </div>
238
+ <div class="task-wave-list">
239
+ <div v-for="wave in taskOrchestration.plan.waves" :key="wave.label" class="task-wave-card">
240
+ <div class="task-wave-title">{{ wave.label }}</div>
241
+ <div class="task-wave-nodes">{{ wave.nodeIds.join(', ') }}</div>
242
+ </div>
243
+ </div>
244
+ <div class="task-node-list">
245
+ <div v-for="node in taskOrchestration.plan.nodes" :key="node.id" class="task-node-card">
246
+ <div class="task-node-head">
247
+ <div>
248
+ <div class="task-node-title">{{ node.title || node.id }}</div>
249
+ <div class="task-node-meta">{{ node.id }} · {{ node.kind }}<span v-if="node.workflowId"> · {{ node.workflowId }}</span></div>
250
+ </div>
251
+ <span :class="['pill', node.write ? 'configured' : 'empty']">{{ node.write ? 'write' : 'read-only' }}</span>
252
+ </div>
253
+ <div class="task-node-deps">依赖:{{ formatTaskNodeDependencies(node) }}</div>
254
+ </div>
255
+ </div>
256
+ </template>
257
+ </section>
258
+
259
+ <section
260
+ v-if="taskOrchestration.queue.length || taskOrchestration.runs.length || taskOrchestration.selectedRunId || taskOrchestration.selectedRunError"
261
+ class="selector-section task-workbench-card">
262
+ <div class="selector-header task-section-header">
263
+ <div>
264
+ <span class="selector-title">执行工作台</span>
265
+ <div class="skills-panel-note">有内容时才展开。</div>
266
+ </div>
267
+ <div class="settings-tab-actions task-header-actions">
268
+ <button type="button" class="btn-tool btn-tool-compact" @click="loadTaskOrchestrationOverview({ forceRefresh: true, includeDetail: true })" :disabled="taskOrchestration.loading">
269
+ {{ taskOrchestration.loading ? '刷新中...' : '刷新' }}
270
+ </button>
271
+ <button v-if="taskOrchestration.queue.length" type="button" class="btn-tool btn-tool-compact" @click="startTaskQueueRunner()" :disabled="taskOrchestration.queueStarting">
272
+ {{ taskOrchestration.queueStarting ? '启动中...' : '开始队列' }}
273
+ </button>
274
+ </div>
275
+ </div>
276
+
277
+ <div
278
+ v-if="(taskOrchestration.queue.length ? 1 : 0) + (taskOrchestration.runs.length ? 1 : 0) + ((taskOrchestration.selectedRunId || taskOrchestration.selectedRunError) ? 1 : 0) > 1"
279
+ class="task-workbench-tabs"
280
+ role="group"
281
+ aria-label="任务编排工作台视图">
282
+ <button v-if="taskOrchestration.queue.length" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'queue' }" @click="taskOrchestration.workspaceTab = 'queue'">队列 {{ taskOrchestration.queue.length }}</button>
283
+ <button v-if="taskOrchestration.runs.length" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'runs' }" @click="taskOrchestration.workspaceTab = 'runs'">运行记录 {{ taskOrchestration.runs.length }}</button>
284
+ <button v-if="taskOrchestration.selectedRunId || taskOrchestration.selectedRunError" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'detail' }" @click="taskOrchestration.workspaceTab = 'detail'">运行详情</button>
285
+ </div>
286
+
287
+ <div v-if="taskOrchestration.workspaceTab === 'queue' || (!taskOrchestration.runs.length && !taskOrchestration.selectedRunId && !taskOrchestration.selectedRunError)" class="task-workbench-panel">
288
+ <div v-if="!taskOrchestration.queue.length" class="task-empty-state">
289
+ <div class="task-empty-title">当前没有排队任务</div>
290
+ <div class="task-empty-copy">批量任务可先入队,再启动队列。</div>
291
+ </div>
292
+ <div v-else class="task-runtime-list">
293
+ <div
294
+ v-for="item in taskOrchestration.queue"
295
+ :key="item.taskId"
296
+ :class="['task-runtime-item', { active: item.lastRunId && taskOrchestration.selectedRunId === item.lastRunId, clickable: !!item.lastRunId }]"
297
+ :role="item.lastRunId ? 'button' : null"
298
+ :tabindex="item.lastRunId ? 0 : -1"
299
+ :aria-disabled="item.lastRunId ? null : 'true'"
300
+ @click="item.lastRunId ? (taskOrchestration.workspaceTab = 'detail', selectTaskRun(item.lastRunId)) : null"
301
+ @keydown.enter.self.prevent="item.lastRunId ? (taskOrchestration.workspaceTab = 'detail', selectTaskRun(item.lastRunId)) : null"
302
+ @keydown.space.self.prevent="item.lastRunId ? (taskOrchestration.workspaceTab = 'detail', selectTaskRun(item.lastRunId)) : null">
303
+ <div class="task-runtime-item-main">
304
+ <div class="task-runtime-item-title">{{ item.title || item.target || item.taskId }}</div>
305
+ <div class="task-runtime-item-meta">{{ item.taskId }} · {{ item.updatedAt || item.createdAt }}</div>
306
+ <div v-if="item.lastSummary" class="task-runtime-item-summary">{{ item.lastSummary }}</div>
307
+ </div>
308
+ <div class="task-runtime-item-actions">
309
+ <span :class="['pill', taskRunStatusTone(item.status)]">{{ item.status }}</span>
310
+ <button type="button" class="btn-mini" @click.stop="cancelTaskRunFromUi(item.taskId)" :disabled="item.status !== 'queued' && item.status !== 'running'">取消</button>
311
+ </div>
312
+ </div>
313
+ </div>
314
+ </div>
315
+
316
+ <div v-else-if="taskOrchestration.workspaceTab === 'runs' || (!taskOrchestration.queue.length && taskOrchestration.runs.length && !taskOrchestration.selectedRunId && !taskOrchestration.selectedRunError)" class="task-workbench-panel">
317
+ <div v-if="!taskOrchestration.runs.length" class="task-empty-state">
318
+ <div class="task-empty-title">还没有运行记录</div>
319
+ <div class="task-empty-copy">执行后会显示最近运行记录。</div>
320
+ </div>
321
+ <div v-else class="task-runtime-list">
322
+ <button
323
+ v-for="item in taskOrchestration.runs"
324
+ :key="item.runId"
325
+ type="button"
326
+ :class="['task-runtime-item', { active: taskOrchestration.selectedRunId === item.runId }]"
327
+ @click="taskOrchestration.workspaceTab = 'detail'; selectTaskRun(item.runId)">
328
+ <div class="task-runtime-item-main">
329
+ <div class="task-runtime-item-title">{{ item.title || item.taskId || item.runId }}</div>
330
+ <div class="task-runtime-item-meta">{{ item.runId }} · {{ item.durationMs || 0 }}ms</div>
331
+ <div v-if="item.summary" class="task-runtime-item-summary">{{ item.summary }}</div>
332
+ </div>
333
+ <div class="task-runtime-item-actions">
334
+ <span :class="['pill', taskRunStatusTone(item.status)]">{{ item.status }}</span>
335
+ </div>
336
+ </button>
337
+ </div>
338
+ </div>
339
+
340
+ <div v-else class="task-workbench-panel">
341
+ <div class="task-detail-toolbar settings-tab-actions">
342
+ <button type="button" class="btn-tool btn-tool-compact" @click="taskOrchestration.selectedRunId ? loadTaskRunDetail(taskOrchestration.selectedRunId) : null" :disabled="!taskOrchestration.selectedRunId || taskOrchestration.selectedRunLoading">
343
+ {{ taskOrchestration.selectedRunLoading ? '刷新中...' : '刷新详情' }}
344
+ </button>
345
+ <button type="button" class="btn-tool btn-tool-compact" @click="retryTaskRunFromUi(taskOrchestration.selectedRunId)" :disabled="!taskOrchestration.selectedRunId || taskOrchestration.retrying">
346
+ {{ taskOrchestration.retrying ? '重试中...' : '重试' }}
347
+ </button>
348
+ <button type="button" class="btn-tool btn-tool-compact" @click="cancelTaskRunFromUi(taskOrchestration.selectedRunId)" :disabled="!taskOrchestrationSelectedRun || !taskOrchestrationSelectedRun.run || !isTaskRunActive(taskOrchestrationSelectedRun.run.status)">
349
+ 取消
350
+ </button>
351
+ </div>
352
+
353
+ <div v-if="taskOrchestration.selectedRunError" class="task-issue-item">{{ taskOrchestration.selectedRunError }}</div>
354
+ <div v-if="!taskOrchestrationSelectedRun" class="task-empty-state">
355
+ <div class="task-empty-title">选择一条运行记录查看详情</div>
356
+ <div class="task-empty-copy">这里会显示节点状态、摘要和日志。</div>
357
+ </div>
358
+ <template v-else>
359
+ <div class="task-detail-summary-strip">
360
+ <div class="task-plan-summary-item">
361
+ <span class="task-plan-summary-label">状态</span>
362
+ <strong>{{ taskOrchestrationSelectedRun.run.status }}</strong>
363
+ </div>
364
+ <div class="task-plan-summary-item">
365
+ <span class="task-plan-summary-label">耗时</span>
366
+ <strong>{{ taskOrchestrationSelectedRun.run.durationMs || 0 }}ms</strong>
367
+ </div>
368
+ <div class="task-plan-summary-item">
369
+ <span class="task-plan-summary-label">节点数</span>
370
+ <strong>{{ taskOrchestrationSelectedRunNodes.length }}</strong>
371
+ </div>
372
+ <div class="task-plan-summary-item">
373
+ <span class="task-plan-summary-label">摘要</span>
374
+ <strong>{{ taskOrchestrationSelectedRun.run.summary || '暂无' }}</strong>
375
+ </div>
376
+ </div>
377
+ <div v-if="taskOrchestrationSelectedRun.run.error" class="task-issue-item">{{ taskOrchestrationSelectedRun.run.error }}</div>
378
+ <div class="task-node-list">
379
+ <div v-for="node in taskOrchestrationSelectedRunNodes" :key="node.id" class="task-node-card task-node-card-detail">
380
+ <div class="task-node-head">
381
+ <div>
382
+ <div class="task-node-title">{{ node.title || node.id }}</div>
383
+ <div class="task-node-meta">{{ node.id }} · attempts {{ node.attemptCount || 0 }} · auto-fix {{ node.autoFixRounds || 0 }}</div>
384
+ </div>
385
+ <span :class="['pill', taskRunStatusTone(node.status)]">{{ node.status }}</span>
386
+ </div>
387
+ <div v-if="node.summary" class="task-runtime-item-summary">{{ node.summary }}</div>
388
+ <div v-if="node.error && node.error !== node.summary" class="task-node-deps">错误:{{ node.error }}</div>
389
+ <div class="task-node-deps">依赖:{{ formatTaskNodeDependencies(node) }}</div>
390
+ <pre class="task-log-block">{{ formatTaskNodeLogs(node.logs) }}</pre>
391
+ </div>
392
+ </div>
393
+ </template>
394
+ </div>
395
+ </section>
396
+ </div>
397
+ </div>