codexmate 0.0.56 → 0.0.57
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.
- package/cli.js +680 -129
- package/lib/task-orchestrator.js +90 -21
- package/lib/task-workspace-chat.js +292 -0
- package/package.json +2 -2
- package/web-ui/app.js +55 -129
- package/web-ui/modules/app.computed.main-tabs.mjs +304 -7
- package/web-ui/modules/app.methods.agents.mjs +3 -0
- package/web-ui/modules/app.methods.claude-config.mjs +65 -25
- package/web-ui/modules/app.methods.navigation.mjs +67 -83
- package/web-ui/modules/app.methods.openclaw-editing.mjs +3 -6
- package/web-ui/modules/app.methods.session-actions.mjs +1 -12
- package/web-ui/modules/app.methods.session-browser.mjs +23 -54
- package/web-ui/modules/app.methods.session-trash.mjs +0 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +16 -31
- package/web-ui/modules/app.methods.task-orchestration.mjs +347 -8
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +0 -3
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +415 -68
- package/web-ui/modules/config-template-confirm-pref.mjs +2 -3
- package/web-ui/modules/i18n/locales/en.mjs +146 -26
- package/web-ui/modules/i18n/locales/ja.mjs +143 -23
- package/web-ui/modules/i18n/locales/vi.mjs +145 -25
- package/web-ui/modules/i18n/locales/zh-tw.mjs +146 -26
- package/web-ui/modules/i18n/locales/zh.mjs +148 -28
- package/web-ui/modules/i18n.mjs +5 -12
- package/web-ui/modules/sessions-filters-url.mjs +1 -2
- package/web-ui/partials/index/layout-header.html +37 -14
- package/web-ui/partials/index/panel-orchestration.html +489 -282
- package/web-ui/res/web-ui-render.precompiled.js +1049 -610
- package/web-ui/styles/layout-shell.css +157 -1
- package/web-ui/styles/navigation-panels.css +11 -0
- package/web-ui/styles/task-orchestration.css +2161 -4
|
@@ -12,7 +12,8 @@ function readTaskOrchestrationDraftMetrics(taskOrchestration) {
|
|
|
12
12
|
const title = String(state.title || '').trim();
|
|
13
13
|
const workflowIds = normalizeTaskDraftLines(state.workflowIdsText);
|
|
14
14
|
const followUps = normalizeTaskDraftLines(state.followUpsText);
|
|
15
|
-
const
|
|
15
|
+
const requestCount = target ? followUps.length + 1 : 0;
|
|
16
|
+
const engine = String(state.selectedEngine || 'openai-chat').trim().toLowerCase() === 'workflow' ? 'workflow' : 'openai-chat';
|
|
16
17
|
const runMode = String(state.runMode || 'write').trim().toLowerCase();
|
|
17
18
|
const allowWrite = runMode === 'write';
|
|
18
19
|
const dryRun = runMode === 'dry-run';
|
|
@@ -37,12 +38,234 @@ function readTaskOrchestrationDraftMetrics(taskOrchestration) {
|
|
|
37
38
|
planWarnings,
|
|
38
39
|
workflowCount: workflowIds.length,
|
|
39
40
|
followUpCount: followUps.length,
|
|
41
|
+
requestCount,
|
|
42
|
+
hasSequentialFollowUps: followUps.length > 0,
|
|
40
43
|
planNodeCount: planNodes.length,
|
|
41
44
|
allowWrite,
|
|
42
45
|
dryRun
|
|
43
46
|
};
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
|
|
50
|
+
function formatTaskConversationMeta(items) {
|
|
51
|
+
return items.filter(Boolean).join(' · ');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function collectTaskRunWorkspaceFileLines(detail) {
|
|
55
|
+
const run = detail && detail.run && typeof detail.run === 'object' ? detail.run : null;
|
|
56
|
+
const nodes = Array.isArray(detail && detail.nodes)
|
|
57
|
+
? detail.nodes
|
|
58
|
+
: (Array.isArray(run && run.nodes) ? run.nodes : []);
|
|
59
|
+
const files = [];
|
|
60
|
+
for (const node of nodes) {
|
|
61
|
+
const output = node && node.output && typeof node.output === 'object' ? node.output : null;
|
|
62
|
+
const workspaceFiles = Array.isArray(output && output.workspaceFiles) ? output.workspaceFiles : [];
|
|
63
|
+
const materializedFiles = Array.isArray(output && output.materializedFiles) ? output.materializedFiles : [];
|
|
64
|
+
for (const file of workspaceFiles) {
|
|
65
|
+
if (!file || typeof file !== 'object') continue;
|
|
66
|
+
const op = String(file.operation || '').trim().toUpperCase() || 'FILE';
|
|
67
|
+
const filePath = String(file.relativePath || file.path || '').trim();
|
|
68
|
+
if (filePath) files.push(`${op} ${filePath}`);
|
|
69
|
+
}
|
|
70
|
+
for (const file of materializedFiles) {
|
|
71
|
+
if (!file || typeof file !== 'object') continue;
|
|
72
|
+
const filePath = String(file.relativePath || file.path || '').trim();
|
|
73
|
+
if (filePath) files.push(`WRITE ${filePath}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return Array.from(new Set(files)).slice(0, 6);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizeTaskWorkspacePath(value) {
|
|
80
|
+
return String(value || '').trim().replace(/\\+/g, '/').replace(/\/$/, '');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function readTaskWorkspacePathFromItem(item) {
|
|
84
|
+
if (!item || typeof item !== 'object') return '';
|
|
85
|
+
return normalizeTaskWorkspacePath(item.cwd || item.workspacePath || item.projectPath || item.path);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function formatTaskWorkspaceLabel(workspacePath, fallback = 'Auto workspace') {
|
|
89
|
+
const normalized = normalizeTaskWorkspacePath(workspacePath);
|
|
90
|
+
if (!normalized) return fallback;
|
|
91
|
+
const parts = normalized.split('/').filter(Boolean);
|
|
92
|
+
return parts.length ? parts[parts.length - 1] : normalized;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function readTaskItemTimestamp(item) {
|
|
96
|
+
if (!item || typeof item !== 'object') return '';
|
|
97
|
+
return String(item.updatedAt || item.completedAt || item.startedAt || item.createdAt || item.enqueuedAt || '').trim();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function addTaskWorkspaceCandidate(workspaces, workspacePath, source, timestamp = '') {
|
|
101
|
+
const normalized = normalizeTaskWorkspacePath(workspacePath);
|
|
102
|
+
if (!normalized) return;
|
|
103
|
+
const key = normalized.toLowerCase();
|
|
104
|
+
const current = workspaces.get(key) || {
|
|
105
|
+
key,
|
|
106
|
+
path: normalized,
|
|
107
|
+
label: formatTaskWorkspaceLabel(normalized),
|
|
108
|
+
meta: normalized,
|
|
109
|
+
queueCount: 0,
|
|
110
|
+
runCount: 0,
|
|
111
|
+
lastActivity: ''
|
|
112
|
+
};
|
|
113
|
+
if (source === 'queue') current.queueCount += 1;
|
|
114
|
+
if (source === 'run') current.runCount += 1;
|
|
115
|
+
if (timestamp && (!current.lastActivity || timestamp > current.lastActivity)) {
|
|
116
|
+
current.lastActivity = timestamp;
|
|
117
|
+
}
|
|
118
|
+
workspaces.set(key, current);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function taskItemMatchesWorkspace(item, workspacePath) {
|
|
122
|
+
const selectedWorkspacePath = normalizeTaskWorkspacePath(workspacePath);
|
|
123
|
+
if (!selectedWorkspacePath) return true;
|
|
124
|
+
return readTaskWorkspacePathFromItem(item) === selectedWorkspacePath;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function createTaskWorkspaceItems(taskOrchestration) {
|
|
128
|
+
const state = taskOrchestration && typeof taskOrchestration === 'object' ? taskOrchestration : {};
|
|
129
|
+
const workspaces = new Map();
|
|
130
|
+
addTaskWorkspaceCandidate(workspaces, state.workspacePath, 'draft');
|
|
131
|
+
const detail = state.selectedRunDetail && typeof state.selectedRunDetail === 'object' ? state.selectedRunDetail : null;
|
|
132
|
+
if (detail) addTaskWorkspaceCandidate(workspaces, detail.cwd, 'detail');
|
|
133
|
+
const queue = Array.isArray(state.queue) ? state.queue : [];
|
|
134
|
+
const runs = Array.isArray(state.runs) ? state.runs : [];
|
|
135
|
+
queue.forEach((item) => addTaskWorkspaceCandidate(workspaces, readTaskWorkspacePathFromItem(item), 'queue', readTaskItemTimestamp(item)));
|
|
136
|
+
runs.forEach((item) => addTaskWorkspaceCandidate(workspaces, readTaskWorkspacePathFromItem(item), 'run', readTaskItemTimestamp(item)));
|
|
137
|
+
const items = Array.from(workspaces.values()).sort((a, b) => {
|
|
138
|
+
const byActivity = String(b.lastActivity || '').localeCompare(String(a.lastActivity || ''));
|
|
139
|
+
if (byActivity !== 0) return byActivity;
|
|
140
|
+
return a.label.localeCompare(b.label);
|
|
141
|
+
});
|
|
142
|
+
if (items.length === 0) {
|
|
143
|
+
items.push({
|
|
144
|
+
key: '__auto__',
|
|
145
|
+
path: '',
|
|
146
|
+
label: 'Auto workspace',
|
|
147
|
+
meta: 'Server default cwd',
|
|
148
|
+
queueCount: queue.length,
|
|
149
|
+
runCount: runs.length,
|
|
150
|
+
lastActivity: ''
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return items;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function createTaskWorkspaceSessions(taskOrchestration, workspacePath) {
|
|
157
|
+
const state = taskOrchestration && typeof taskOrchestration === 'object' ? taskOrchestration : {};
|
|
158
|
+
const queue = Array.isArray(state.queue) ? state.queue : [];
|
|
159
|
+
const runs = Array.isArray(state.runs) ? state.runs : [];
|
|
160
|
+
const queueSessions = queue
|
|
161
|
+
.filter((item) => taskItemMatchesWorkspace(item, workspacePath))
|
|
162
|
+
.map((item) => ({
|
|
163
|
+
id: item.taskId || item.lastRunId || `queue-${readTaskItemTimestamp(item)}`,
|
|
164
|
+
type: 'queue',
|
|
165
|
+
status: item.status || item.runStatus || 'queued',
|
|
166
|
+
title: item.title || item.target || item.taskId || 'Queued task',
|
|
167
|
+
meta: item.threadId || item.cwd || item.taskId || '',
|
|
168
|
+
taskId: item.taskId || '',
|
|
169
|
+
runId: item.lastRunId || '',
|
|
170
|
+
threadId: item.threadId || '',
|
|
171
|
+
cwd: readTaskWorkspacePathFromItem(item),
|
|
172
|
+
timestamp: readTaskItemTimestamp(item),
|
|
173
|
+
active: true
|
|
174
|
+
}));
|
|
175
|
+
const runSessions = runs
|
|
176
|
+
.filter((item) => taskItemMatchesWorkspace(item, workspacePath))
|
|
177
|
+
.map((item) => ({
|
|
178
|
+
id: item.runId || item.taskId || `run-${readTaskItemTimestamp(item)}`,
|
|
179
|
+
type: 'run',
|
|
180
|
+
status: item.status || 'completed',
|
|
181
|
+
title: item.title || item.summary || item.taskId || item.runId || 'Run',
|
|
182
|
+
meta: item.threadId || item.cwd || `${item.durationMs || 0}ms`,
|
|
183
|
+
taskId: item.taskId || '',
|
|
184
|
+
runId: item.runId || '',
|
|
185
|
+
threadId: item.threadId || '',
|
|
186
|
+
cwd: readTaskWorkspacePathFromItem(item),
|
|
187
|
+
timestamp: readTaskItemTimestamp(item),
|
|
188
|
+
active: false
|
|
189
|
+
}));
|
|
190
|
+
return queueSessions.concat(runSessions).sort((a, b) => {
|
|
191
|
+
if (a.active !== b.active) return a.active ? -1 : 1;
|
|
192
|
+
return String(b.timestamp || '').localeCompare(String(a.timestamp || ''));
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function createTaskConversationMessages(taskOrchestration, t = null) {
|
|
197
|
+
const state = taskOrchestration && typeof taskOrchestration === 'object' ? taskOrchestration : {};
|
|
198
|
+
const messages = [];
|
|
199
|
+
const target = String(state.target || '').trim();
|
|
200
|
+
const followUps = normalizeTaskDraftLines(state.followUpsText);
|
|
201
|
+
const detail = state.selectedRunDetail && typeof state.selectedRunDetail === 'object' ? state.selectedRunDetail : null;
|
|
202
|
+
const detailRun = detail && detail.run && typeof detail.run === 'object' ? detail.run : null;
|
|
203
|
+
if (detail) {
|
|
204
|
+
const fileLines = collectTaskRunWorkspaceFileLines(detail);
|
|
205
|
+
const resultLines = [];
|
|
206
|
+
if (detailRun && detailRun.summary) {
|
|
207
|
+
resultLines.push(detailRun.summary);
|
|
208
|
+
} else {
|
|
209
|
+
resultLines.push(translateTaskText(t, 'orchestration.chat.assistant.contextFallback', '已选中一个历史任务,继续时会继承它的线程和工作区。'));
|
|
210
|
+
}
|
|
211
|
+
if (fileLines.length > 0) {
|
|
212
|
+
resultLines.push(translateTaskText(t, 'orchestration.chat.assistant.filesSummary', `文件操作:${fileLines.join(';')}`, { files: fileLines.join(';') }));
|
|
213
|
+
}
|
|
214
|
+
messages.push({
|
|
215
|
+
id: 'context-run',
|
|
216
|
+
role: 'assistant',
|
|
217
|
+
label: detailRun && detailRun.status === 'success'
|
|
218
|
+
? translateTaskText(t, 'orchestration.chat.assistant.resultLabel', '实现结果')
|
|
219
|
+
: translateTaskText(t, 'orchestration.chat.assistant.contextLabel', '上一轮上下文'),
|
|
220
|
+
text: resultLines.join('\n'),
|
|
221
|
+
meta: formatTaskConversationMeta([
|
|
222
|
+
detail.threadId ? translateTaskText(t, 'orchestration.chat.meta.thread', `线程 ${detail.threadId}`, { value: detail.threadId }) : '',
|
|
223
|
+
detail.cwd ? translateTaskText(t, 'orchestration.chat.meta.workspace', `工作区 ${detail.cwd}`, { value: detail.cwd }) : '',
|
|
224
|
+
detailRun && detailRun.status ? detailRun.status : ''
|
|
225
|
+
])
|
|
226
|
+
});
|
|
227
|
+
} else if (!target && followUps.length === 0) {
|
|
228
|
+
messages.push({
|
|
229
|
+
id: 'assistant-empty',
|
|
230
|
+
role: 'assistant',
|
|
231
|
+
label: translateTaskText(t, 'orchestration.chat.assistant.readyLabel', 'Codexmate'),
|
|
232
|
+
text: translateTaskText(t, 'orchestration.chat.assistant.empty', '先发第一个需求;如果还有第二个需求,继续发送,Codexmate 会按顺序处理并保留上下文。'),
|
|
233
|
+
meta: translateTaskText(t, 'orchestration.chat.meta.order', '顺序执行 · 保留上下文')
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (target) {
|
|
237
|
+
messages.push({
|
|
238
|
+
id: 'user-target',
|
|
239
|
+
role: 'user',
|
|
240
|
+
label: translateTaskText(t, 'orchestration.chat.user.step', '需求 {count}', { count: 1 }),
|
|
241
|
+
text: target,
|
|
242
|
+
meta: translateTaskText(t, 'orchestration.chat.meta.first', '先完成这一条')
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
followUps.forEach((item, index) => {
|
|
246
|
+
const step = index + 2;
|
|
247
|
+
messages.push({
|
|
248
|
+
id: `user-follow-up-${index}`,
|
|
249
|
+
role: 'user',
|
|
250
|
+
label: translateTaskText(t, 'orchestration.chat.user.step', '需求 {count}', { count: step }),
|
|
251
|
+
text: item,
|
|
252
|
+
meta: translateTaskText(t, 'orchestration.chat.meta.afterPrevious', '等待前一条完成后继续')
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
if (!state.plan && target) {
|
|
256
|
+
messages.push({
|
|
257
|
+
id: 'assistant-next',
|
|
258
|
+
role: 'assistant',
|
|
259
|
+
label: translateTaskText(t, 'orchestration.chat.assistant.readyLabel', 'Codexmate'),
|
|
260
|
+
text: followUps.length > 0
|
|
261
|
+
? translateTaskText(t, 'orchestration.chat.assistant.sequenceReady', '已收到多条需求;执行时会先完成需求 1,再带着上下文继续后续需求。')
|
|
262
|
+
: translateTaskText(t, 'orchestration.chat.assistant.singleReady', '已收到第一条需求。可以继续补需求 2,或直接预览并执行。'),
|
|
263
|
+
meta: translateTaskText(t, 'orchestration.chat.meta.previewNext', '下一步:预览计划')
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return messages;
|
|
267
|
+
}
|
|
268
|
+
|
|
46
269
|
function translateTaskText(t, key, fallback, params = null) {
|
|
47
270
|
if (typeof t !== 'function') return fallback;
|
|
48
271
|
const translated = t(key, params);
|
|
@@ -60,13 +283,23 @@ function createTaskDraftChecklist(metrics, t = null) {
|
|
|
60
283
|
done: metrics.hasTarget,
|
|
61
284
|
detail: metrics.hasTarget ? translateTaskText(t, 'orchestration.readiness.target.done', '已写目标') : translateTaskText(t, 'orchestration.readiness.target.missing', '还没写目标')
|
|
62
285
|
},
|
|
286
|
+
{
|
|
287
|
+
key: 'sequence',
|
|
288
|
+
label: translateTaskText(t, 'orchestration.readiness.sequence.label', '顺序'),
|
|
289
|
+
done: metrics.hasTarget,
|
|
290
|
+
detail: !metrics.hasTarget
|
|
291
|
+
? translateTaskText(t, 'orchestration.readiness.sequence.missing', '先发送需求 1')
|
|
292
|
+
: (metrics.hasSequentialFollowUps
|
|
293
|
+
? translateTaskText(t, 'orchestration.readiness.sequence.multiple', '{count} 条需求会按顺序执行:先完成需求 1,再继续需求 2。', { count: metrics.requestCount })
|
|
294
|
+
: translateTaskText(t, 'orchestration.readiness.sequence.single', '当前只有需求 1;继续发送会变成需求 2。'))
|
|
295
|
+
},
|
|
63
296
|
{
|
|
64
297
|
key: 'engine',
|
|
65
298
|
label: metrics.engine === 'workflow' ? 'Workflow' : translateTaskText(t, 'orchestration.readiness.engine.label', '执行策略'),
|
|
66
299
|
done: workflowReady,
|
|
67
300
|
detail: metrics.engine === 'workflow'
|
|
68
301
|
? (metrics.workflowCount > 0 ? translateTaskText(t, 'orchestration.readiness.workflow.done', `已选 ${metrics.workflowCount} 个 Workflow`, { count: metrics.workflowCount }) : translateTaskText(t, 'orchestration.readiness.workflow.missing', '还没选 Workflow ID'))
|
|
69
|
-
: translateTaskText(t, 'orchestration.readiness.engine.
|
|
302
|
+
: translateTaskText(t, 'orchestration.readiness.engine.openaiChat', '使用 OpenAI Chat-compatible 节点')
|
|
70
303
|
},
|
|
71
304
|
{
|
|
72
305
|
key: 'scope',
|
|
@@ -81,7 +314,7 @@ function createTaskDraftChecklist(metrics, t = null) {
|
|
|
81
314
|
label: translateTaskText(t, 'orchestration.readiness.preview.label', '预览'),
|
|
82
315
|
done: previewReady,
|
|
83
316
|
detail: !metrics.hasPlan
|
|
84
|
-
? translateTaskText(t, 'orchestration.readiness.preview.missing', '
|
|
317
|
+
? translateTaskText(t, 'orchestration.readiness.preview.missing', '还没发送 /plan')
|
|
85
318
|
: (metrics.planIssues.length > 0 ? translateTaskText(t, 'orchestration.readiness.preview.blocked', `有 ${metrics.planIssues.length} 个阻塞项`, { count: metrics.planIssues.length }) : translateTaskText(t, 'orchestration.readiness.preview.ready', `计划可用,${metrics.planNodeCount} 个节点`, { count: metrics.planNodeCount }))
|
|
86
319
|
}
|
|
87
320
|
];
|
|
@@ -106,7 +339,9 @@ function createTaskDraftReadiness(metrics, t = null) {
|
|
|
106
339
|
return {
|
|
107
340
|
tone: 'warn',
|
|
108
341
|
title: translateTaskText(t, 'orchestration.readiness.preview.title', '建议先预览'),
|
|
109
|
-
summary:
|
|
342
|
+
summary: metrics.hasSequentialFollowUps
|
|
343
|
+
? translateTaskText(t, 'orchestration.readiness.preview.sequenceSummary', '草稿已成形,已锁定 {count} 条顺序需求:先完成需求 1,再继续需求 2。', { count: metrics.requestCount })
|
|
344
|
+
: translateTaskText(t, 'orchestration.readiness.preview.summary', '草稿已成形,发送 /plan 预览方案,确认节点和依赖再执行。')
|
|
110
345
|
};
|
|
111
346
|
}
|
|
112
347
|
if (metrics.planIssues.length > 0) {
|
|
@@ -120,7 +355,7 @@ function createTaskDraftReadiness(metrics, t = null) {
|
|
|
120
355
|
return {
|
|
121
356
|
tone: 'warn',
|
|
122
357
|
title: translateTaskText(t, 'orchestration.readiness.warn.title', '可以执行,但有提醒'),
|
|
123
|
-
summary: translateTaskText(t, 'orchestration.readiness.warn.summary',
|
|
358
|
+
summary: translateTaskText(t, 'orchestration.readiness.warn.summary', `/plan 方案已生成,但还有 ${metrics.planWarnings.length} 条提醒值得先看一眼。`, { count: metrics.planWarnings.length })
|
|
124
359
|
};
|
|
125
360
|
}
|
|
126
361
|
if (metrics.dryRun) {
|
|
@@ -134,7 +369,7 @@ function createTaskDraftReadiness(metrics, t = null) {
|
|
|
134
369
|
tone: 'success',
|
|
135
370
|
title: translateTaskText(t, 'orchestration.readiness.ready.title', '可以执行'),
|
|
136
371
|
summary: metrics.followUpCount > 0
|
|
137
|
-
? translateTaskText(t, 'orchestration.readiness.ready.withFollowUps',
|
|
372
|
+
? translateTaskText(t, 'orchestration.readiness.ready.withFollowUps', `已锁定 ${metrics.requestCount} 条顺序需求:先完成需求 1,再带上下文继续需求 2。`, { count: metrics.requestCount })
|
|
138
373
|
: translateTaskText(t, 'orchestration.readiness.ready.summary', '主目标已经够清楚了,可以直接执行或入队。')
|
|
139
374
|
};
|
|
140
375
|
}
|
|
@@ -191,13 +426,72 @@ export function createMainTabsComputed() {
|
|
|
191
426
|
if (detail && Array.isArray(detail.nodes)) return detail.nodes;
|
|
192
427
|
return Array.isArray(run.nodes) ? run.nodes : [];
|
|
193
428
|
},
|
|
429
|
+
taskOrchestrationActiveQueue() {
|
|
430
|
+
const queue = this.taskOrchestration && Array.isArray(this.taskOrchestration.queue)
|
|
431
|
+
? this.taskOrchestration.queue
|
|
432
|
+
: [];
|
|
433
|
+
return queue.filter((item) => {
|
|
434
|
+
const status = String((item && (item.status || item.runStatus)) || '').trim().toLowerCase();
|
|
435
|
+
return status === 'queued' || status === 'running';
|
|
436
|
+
});
|
|
437
|
+
},
|
|
438
|
+
taskOrchestrationEngineLabel() {
|
|
439
|
+
const state = this.taskOrchestration && typeof this.taskOrchestration === 'object'
|
|
440
|
+
? this.taskOrchestration
|
|
441
|
+
: {};
|
|
442
|
+
return state.selectedEngine === 'workflow' ? 'Workflow' : 'Codex';
|
|
443
|
+
},
|
|
444
|
+
taskOrchestrationWorkbenchVisible() {
|
|
445
|
+
const state = this.taskOrchestration && typeof this.taskOrchestration === 'object'
|
|
446
|
+
? this.taskOrchestration
|
|
447
|
+
: {};
|
|
448
|
+
const activeQueue = Array.isArray(this.taskOrchestrationActiveQueue)
|
|
449
|
+
? this.taskOrchestrationActiveQueue
|
|
450
|
+
: [];
|
|
451
|
+
return activeQueue.length > 0
|
|
452
|
+
|| !!String(state.selectedRunId || '').trim()
|
|
453
|
+
|| !!String(state.selectedRunError || '').trim()
|
|
454
|
+
|| (state.workspaceTab === 'runs' && Array.isArray(state.runs) && state.runs.length > 0);
|
|
455
|
+
},
|
|
456
|
+
taskOrchestrationWorkspacePath() {
|
|
457
|
+
const state = this.taskOrchestration && typeof this.taskOrchestration === 'object'
|
|
458
|
+
? this.taskOrchestration
|
|
459
|
+
: {};
|
|
460
|
+
const detail = state.selectedRunDetail && typeof state.selectedRunDetail === 'object'
|
|
461
|
+
? state.selectedRunDetail
|
|
462
|
+
: null;
|
|
463
|
+
return normalizeTaskWorkspacePath(state.workspacePath || (detail && detail.cwd) || '');
|
|
464
|
+
},
|
|
465
|
+
taskOrchestrationWorkspaceItems() {
|
|
466
|
+
const items = createTaskWorkspaceItems(this.taskOrchestration);
|
|
467
|
+
const selectedPath = this.taskOrchestrationWorkspacePath;
|
|
468
|
+
return items.map((item) => ({
|
|
469
|
+
...item,
|
|
470
|
+
active: normalizeTaskWorkspacePath(item.path) === selectedPath || (!selectedPath && !item.path)
|
|
471
|
+
}));
|
|
472
|
+
},
|
|
473
|
+
taskOrchestrationWorkspaceQueue() {
|
|
474
|
+
const queue = this.taskOrchestration && Array.isArray(this.taskOrchestration.queue)
|
|
475
|
+
? this.taskOrchestration.queue
|
|
476
|
+
: [];
|
|
477
|
+
return queue.filter((item) => taskItemMatchesWorkspace(item, this.taskOrchestrationWorkspacePath));
|
|
478
|
+
},
|
|
479
|
+
taskOrchestrationWorkspaceRuns() {
|
|
480
|
+
const runs = this.taskOrchestration && Array.isArray(this.taskOrchestration.runs)
|
|
481
|
+
? this.taskOrchestration.runs
|
|
482
|
+
: [];
|
|
483
|
+
return runs.filter((item) => taskItemMatchesWorkspace(item, this.taskOrchestrationWorkspacePath));
|
|
484
|
+
},
|
|
485
|
+
taskOrchestrationWorkspaceSessions() {
|
|
486
|
+
return createTaskWorkspaceSessions(this.taskOrchestration, this.taskOrchestrationWorkspacePath);
|
|
487
|
+
},
|
|
194
488
|
taskOrchestrationQueueStats() {
|
|
195
489
|
const queue = this.taskOrchestration && Array.isArray(this.taskOrchestration.queue)
|
|
196
490
|
? this.taskOrchestration.queue
|
|
197
491
|
: [];
|
|
198
492
|
const stats = { queued: 0, running: 0, failed: 0 };
|
|
199
493
|
for (const item of queue) {
|
|
200
|
-
const status = String(item && item.status || '').trim().toLowerCase();
|
|
494
|
+
const status = String((item && (item.status || item.runStatus)) || '').trim().toLowerCase();
|
|
201
495
|
if (status === 'queued') stats.queued += 1;
|
|
202
496
|
else if (status === 'running') stats.running += 1;
|
|
203
497
|
else if (status === 'failed') stats.failed += 1;
|
|
@@ -212,6 +506,9 @@ export function createMainTabsComputed() {
|
|
|
212
506
|
},
|
|
213
507
|
taskOrchestrationDraftReadiness() {
|
|
214
508
|
return createTaskDraftReadiness(this.taskOrchestrationDraftMetrics, this.t && this.t.bind(this));
|
|
509
|
+
},
|
|
510
|
+
taskOrchestrationConversationMessages() {
|
|
511
|
+
return createTaskConversationMessages(this.taskOrchestration, this.t && this.t.bind(this));
|
|
215
512
|
}
|
|
216
513
|
};
|
|
217
514
|
}
|
|
@@ -354,6 +354,9 @@ export function createAgentsMethods(options = {}) {
|
|
|
354
354
|
return !!this.agentsSaving || this.hasAgentsContentChanged() || this.agentsDiffVisible;
|
|
355
355
|
},
|
|
356
356
|
handleBeforeUnload(event) {
|
|
357
|
+
if (typeof this.flushWebUiPreferences === 'function') {
|
|
358
|
+
this.flushWebUiPreferences();
|
|
359
|
+
}
|
|
357
360
|
if (!this.hasPendingAgentsDraft()) {
|
|
358
361
|
return;
|
|
359
362
|
}
|
|
@@ -8,8 +8,6 @@ function normalizeClaudeBaseUrl(value) {
|
|
|
8
8
|
return normalizeClaudeText(value).replace(/\/+$/g, '');
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const DELETED_CLAUDE_SETTINGS_IMPORTS_KEY = 'deletedClaudeSettingsImports';
|
|
12
|
-
|
|
13
11
|
function buildDeletedClaudeSettingsFingerprint(config = {}) {
|
|
14
12
|
const safe = config && typeof config === 'object' ? config : {};
|
|
15
13
|
const baseUrl = normalizeClaudeBaseUrl(safe.baseUrl);
|
|
@@ -92,10 +90,41 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
92
90
|
return {
|
|
93
91
|
switchClaudeConfig(name) {
|
|
94
92
|
this.currentClaudeConfig = name;
|
|
95
|
-
|
|
93
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
94
|
+
this.persistWebUiPreferences({ currentClaudeConfig: name || '' });
|
|
95
|
+
}
|
|
96
96
|
this.refreshClaudeModelContext();
|
|
97
97
|
},
|
|
98
98
|
|
|
99
|
+
normalizeStoredClaudeConfigs() {
|
|
100
|
+
const configs = this.claudeConfigs && typeof this.claudeConfigs === 'object' && !Array.isArray(this.claudeConfigs)
|
|
101
|
+
? this.claudeConfigs
|
|
102
|
+
: {};
|
|
103
|
+
let changed = configs !== this.claudeConfigs;
|
|
104
|
+
for (const config of Object.values(configs)) {
|
|
105
|
+
if (!config || typeof config !== 'object') continue;
|
|
106
|
+
if (config.apiKey && config.apiKey.includes('****')) {
|
|
107
|
+
config.apiKey = '';
|
|
108
|
+
config.hasKey = false;
|
|
109
|
+
changed = true;
|
|
110
|
+
}
|
|
111
|
+
const targetApiRaw = typeof config.targetApi === 'string' ? config.targetApi.trim().toLowerCase() : '';
|
|
112
|
+
const previousTargetApi = config.targetApi;
|
|
113
|
+
if (targetApiRaw === 'chat_completions' || targetApiRaw === 'chat-completions' || targetApiRaw === 'chat/completions') {
|
|
114
|
+
config.targetApi = 'chat_completions';
|
|
115
|
+
} else if (targetApiRaw === 'ollama') {
|
|
116
|
+
config.targetApi = 'ollama';
|
|
117
|
+
} else {
|
|
118
|
+
config.targetApi = 'responses';
|
|
119
|
+
}
|
|
120
|
+
if (config.targetApi !== previousTargetApi) {
|
|
121
|
+
changed = true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
this.claudeConfigs = configs;
|
|
125
|
+
return changed;
|
|
126
|
+
},
|
|
127
|
+
|
|
99
128
|
onClaudeModelChange() {
|
|
100
129
|
const name = this.currentClaudeConfig;
|
|
101
130
|
if (!name || name === 'claude-local') {
|
|
@@ -124,9 +153,14 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
124
153
|
},
|
|
125
154
|
|
|
126
155
|
saveClaudeConfigs() {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
156
|
+
if (typeof this.normalizeStoredClaudeConfigs === 'function') {
|
|
157
|
+
this.normalizeStoredClaudeConfigs();
|
|
158
|
+
}
|
|
159
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
160
|
+
this.persistWebUiPreferences({
|
|
161
|
+
claudeConfigs: this.claudeConfigs,
|
|
162
|
+
currentClaudeConfig: this.currentClaudeConfig || ''
|
|
163
|
+
});
|
|
130
164
|
}
|
|
131
165
|
this.syncClaudeBridgeProviders();
|
|
132
166
|
},
|
|
@@ -134,18 +168,17 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
134
168
|
rememberDeletedClaudeSettingsImport(config) {
|
|
135
169
|
const fingerprint = buildDeletedClaudeSettingsFingerprint(config);
|
|
136
170
|
if (!fingerprint) return;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
} catch (_) {}
|
|
171
|
+
const entries = Array.isArray(this.deletedClaudeSettingsImports) ? this.deletedClaudeSettingsImports : [];
|
|
172
|
+
const deduped = entries.filter((entry) => {
|
|
173
|
+
if (!entry || typeof entry !== 'object') return false;
|
|
174
|
+
return normalizeClaudeBaseUrl(entry.baseUrl) !== fingerprint.baseUrl
|
|
175
|
+
|| normalizeClaudeText(entry.model) !== fingerprint.model;
|
|
176
|
+
});
|
|
177
|
+
deduped.push(fingerprint);
|
|
178
|
+
this.deletedClaudeSettingsImports = deduped.slice(-50);
|
|
179
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
180
|
+
this.persistWebUiPreferences({ deletedClaudeSettingsImports: this.deletedClaudeSettingsImports });
|
|
181
|
+
}
|
|
149
182
|
},
|
|
150
183
|
|
|
151
184
|
async applyCurrentClaudeConfigSilently() {
|
|
@@ -227,15 +260,18 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
227
260
|
const current = normalizeClaudeText(this.currentClaudeConfig);
|
|
228
261
|
if (current && !configs[current]) {
|
|
229
262
|
this.currentClaudeConfig = firstCachedName || Object.keys(configs)[0] || '';
|
|
230
|
-
try { localStorage.setItem('currentClaudeConfig', this.currentClaudeConfig); } catch (_) {}
|
|
231
263
|
changed = true;
|
|
232
264
|
} else if (firstCachedName) {
|
|
233
|
-
let savedCurrent = '';
|
|
234
|
-
try { savedCurrent = localStorage.getItem('currentClaudeConfig') || ''; } catch (_) {}
|
|
235
265
|
const currentConfig = current && configs[current] ? configs[current] : null;
|
|
236
|
-
|
|
266
|
+
const currentApplyable = currentConfig && (
|
|
267
|
+
currentConfig.hasKey === true
|
|
268
|
+
|| !!normalizeClaudeText(currentConfig.apiKey)
|
|
269
|
+
|| !!normalizeClaudeText(currentConfig.providerCacheRef)
|
|
270
|
+
|| !!normalizeClaudeText(currentConfig.externalCredentialType)
|
|
271
|
+
|| normalizeClaudeText(currentConfig.targetApi) === 'ollama'
|
|
272
|
+
);
|
|
273
|
+
if (!currentApplyable) {
|
|
237
274
|
this.currentClaudeConfig = firstCachedName;
|
|
238
|
-
try { localStorage.setItem('currentClaudeConfig', firstCachedName); } catch (_) {}
|
|
239
275
|
changed = true;
|
|
240
276
|
}
|
|
241
277
|
}
|
|
@@ -458,7 +494,9 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
458
494
|
const silentSuccess = silent || !!(options && options.silentSuccess);
|
|
459
495
|
const silentError = silent || !!(options && options.silentError);
|
|
460
496
|
this.currentClaudeConfig = name;
|
|
461
|
-
|
|
497
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
498
|
+
this.persistWebUiPreferences({ currentClaudeConfig: name || '' });
|
|
499
|
+
}
|
|
462
500
|
this.refreshClaudeModelContext();
|
|
463
501
|
const config = this.claudeConfigs[name];
|
|
464
502
|
|
|
@@ -572,7 +610,9 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
572
610
|
|
|
573
611
|
async applyClaudeLocalBridge() {
|
|
574
612
|
this.currentClaudeConfig = 'claude-local';
|
|
575
|
-
|
|
613
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
614
|
+
this.persistWebUiPreferences({ currentClaudeConfig: 'claude-local' });
|
|
615
|
+
}
|
|
576
616
|
this.refreshClaudeModelContext();
|
|
577
617
|
|
|
578
618
|
const candidates = this.claudeLocalBridgeCandidateProviders();
|