codexmate 0.0.20 → 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.
- package/README.md +289 -152
- package/README.zh.md +321 -0
- package/cli/agents-files.js +224 -0
- package/cli/archive-helpers.js +446 -0
- package/cli/auth-profiles.js +359 -0
- package/cli/builtin-proxy.js +1044 -0
- package/cli/claude-proxy.js +998 -0
- package/cli/config-bootstrap.js +384 -0
- package/cli/openai-bridge.js +950 -0
- package/cli/openclaw-config.js +629 -0
- package/cli/session-usage.concurrent.js +28 -0
- package/cli/session-usage.js +112 -0
- package/cli/session-usage.models.js +176 -0
- package/cli/skills.js +1141 -0
- package/cli/zip-commands.js +510 -0
- package/cli.js +9408 -9719
- package/lib/cli-models-utils.js +109 -1
- package/lib/cli-path-utils.js +69 -0
- package/lib/cli-sessions.js +386 -0
- package/lib/download-artifacts.js +77 -0
- package/lib/task-orchestrator.js +869 -0
- package/package.json +14 -10
- package/res/logo.png +0 -0
- package/res/vue.global.prod.js +13 -0
- package/web-ui/app.js +193 -15
- package/web-ui/index.html +5 -1
- package/web-ui/logic.agents-diff.mjs +1 -1
- package/web-ui/logic.claude.mjs +60 -0
- package/web-ui/logic.runtime.mjs +11 -7
- package/web-ui/logic.sessions.mjs +372 -21
- package/web-ui/modules/api.mjs +22 -1
- package/web-ui/modules/app.computed.dashboard.mjs +23 -10
- package/web-ui/modules/app.computed.index.mjs +4 -0
- package/web-ui/modules/app.computed.main-tabs.mjs +198 -0
- package/web-ui/modules/app.computed.session.mjs +521 -9
- package/web-ui/modules/app.methods.agents.mjs +62 -11
- package/web-ui/modules/app.methods.codex-config.mjs +189 -34
- package/web-ui/modules/app.methods.index.mjs +7 -1
- package/web-ui/modules/app.methods.install.mjs +24 -20
- package/web-ui/modules/app.methods.navigation.mjs +142 -1
- package/web-ui/modules/app.methods.openclaw-core.mjs +339 -39
- package/web-ui/modules/app.methods.openclaw-editing.mjs +39 -4
- package/web-ui/modules/app.methods.openclaw-persist.mjs +122 -4
- package/web-ui/modules/app.methods.providers.mjs +192 -53
- package/web-ui/modules/app.methods.session-actions.mjs +99 -19
- package/web-ui/modules/app.methods.session-browser.mjs +196 -5
- package/web-ui/modules/app.methods.session-timeline.mjs +22 -15
- package/web-ui/modules/app.methods.session-trash.mjs +3 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +70 -71
- package/web-ui/modules/app.methods.task-orchestration.mjs +471 -0
- package/web-ui/modules/config-mode.computed.mjs +2 -0
- package/web-ui/modules/config-template-confirm-pref.mjs +33 -0
- package/web-ui/modules/i18n.mjs +1609 -0
- package/web-ui/modules/plugins.computed.mjs +220 -0
- package/web-ui/modules/plugins.methods.mjs +620 -0
- package/web-ui/modules/plugins.storage.mjs +37 -0
- package/web-ui/partials/index/layout-footer.html +1 -57
- package/web-ui/partials/index/layout-header.html +299 -175
- package/web-ui/partials/index/modal-config-template-agents.html +79 -29
- package/web-ui/partials/index/modal-confirm-toast.html +1 -1
- package/web-ui/partials/index/modal-health-check.html +14 -14
- package/web-ui/partials/index/modal-openclaw-config.html +47 -42
- package/web-ui/partials/index/modal-skills.html +130 -114
- package/web-ui/partials/index/modals-basic.html +71 -102
- package/web-ui/partials/index/panel-config-claude.html +50 -12
- package/web-ui/partials/index/panel-config-codex.html +34 -37
- package/web-ui/partials/index/panel-config-openclaw.html +10 -16
- package/web-ui/partials/index/panel-docs.html +147 -0
- package/web-ui/partials/index/panel-market.html +38 -38
- package/web-ui/partials/index/panel-orchestration.html +397 -0
- package/web-ui/partials/index/panel-plugins.html +243 -0
- package/web-ui/partials/index/panel-sessions.html +51 -146
- package/web-ui/partials/index/panel-settings.html +188 -96
- package/web-ui/partials/index/panel-usage.html +353 -0
- package/web-ui/session-helpers.mjs +221 -10
- package/web-ui/styles/base-theme.css +120 -229
- package/web-ui/styles/controls-forms.css +59 -51
- package/web-ui/styles/docs-panel.css +247 -0
- package/web-ui/styles/layout-shell.css +394 -128
- package/web-ui/styles/modals-core.css +18 -3
- package/web-ui/styles/navigation-panels.css +184 -183
- package/web-ui/styles/plugins-panel.css +518 -0
- package/web-ui/styles/responsive.css +102 -62
- package/web-ui/styles/sessions-list.css +13 -27
- package/web-ui/styles/sessions-preview.css +13 -7
- package/web-ui/styles/sessions-toolbar-trash.css +25 -0
- package/web-ui/styles/sessions-usage.css +581 -6
- package/web-ui/styles/settings-panel.css +166 -0
- package/web-ui/styles/skills-list.css +16 -11
- package/web-ui/styles/skills-market.css +63 -2
- package/web-ui/styles/task-orchestration.css +776 -0
- package/web-ui/styles/titles-cards.css +67 -66
- package/web-ui/styles.css +4 -0
- package/README.en.md +0 -259
- package/res/screenshot.png +0 -0
- package/res/vue.global.js +0 -18552
|
@@ -36,6 +36,38 @@ export function createAgentsMethods(options = {}) {
|
|
|
36
36
|
} = options;
|
|
37
37
|
|
|
38
38
|
return {
|
|
39
|
+
async openClaudeMdEditor() {
|
|
40
|
+
this.setAgentsModalContext('claude-md');
|
|
41
|
+
const requestToken = issueLatestRequestToken(this, '_agentsOpenRequestToken');
|
|
42
|
+
this.agentsLoading = true;
|
|
43
|
+
try {
|
|
44
|
+
const res = await api('get-claude-md-file');
|
|
45
|
+
if (!isLatestRequestToken(this, '_agentsOpenRequestToken', requestToken)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (res.error) {
|
|
49
|
+
this.showMessage(res.error, 'error');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.agentsContent = res.content || '';
|
|
53
|
+
this.agentsOriginalContent = this.agentsContent;
|
|
54
|
+
this.agentsPath = res.path || '';
|
|
55
|
+
this.agentsExists = !!res.exists;
|
|
56
|
+
this.agentsLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
|
|
57
|
+
this.resetAgentsDiffState();
|
|
58
|
+
this.showAgentsModal = true;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
if (!isLatestRequestToken(this, '_agentsOpenRequestToken', requestToken)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
this.showMessage('加载文件失败', 'error');
|
|
64
|
+
} finally {
|
|
65
|
+
if (isLatestRequestToken(this, '_agentsOpenRequestToken', requestToken)) {
|
|
66
|
+
this.agentsLoading = false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
|
|
39
71
|
async openAgentsEditor() {
|
|
40
72
|
this.setAgentsModalContext('codex');
|
|
41
73
|
const requestToken = issueLatestRequestToken(this, '_agentsOpenRequestToken');
|
|
@@ -148,6 +180,15 @@ export function createAgentsMethods(options = {}) {
|
|
|
148
180
|
},
|
|
149
181
|
|
|
150
182
|
setAgentsModalContext(context, options = {}) {
|
|
183
|
+
const t = typeof this.t === 'function' ? this.t : null;
|
|
184
|
+
const tr = (key, fallback, params = null) => (t ? t(key, params) : fallback);
|
|
185
|
+
if (context === 'claude-md') {
|
|
186
|
+
this.agentsContext = 'claude-md';
|
|
187
|
+
this.agentsWorkspaceFileName = '';
|
|
188
|
+
this.agentsModalTitle = tr('modal.agents.title.claudeMd', 'CLAUDE.md 编辑器');
|
|
189
|
+
this.agentsModalHint = tr('modal.agents.hint.claudeMd', '保存后会写入 ~/.claude/CLAUDE.md。');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
151
192
|
if (context === 'openclaw-workspace') {
|
|
152
193
|
const fileName = (options.fileName || this.openclawWorkspaceFileName || 'AGENTS.md').trim();
|
|
153
194
|
this.agentsContext = 'openclaw-workspace';
|
|
@@ -158,11 +199,11 @@ export function createAgentsMethods(options = {}) {
|
|
|
158
199
|
}
|
|
159
200
|
this.agentsContext = context === 'openclaw' ? 'openclaw' : 'codex';
|
|
160
201
|
if (this.agentsContext === 'openclaw') {
|
|
161
|
-
this.agentsModalTitle = 'OpenClaw AGENTS.md 编辑器';
|
|
162
|
-
this.agentsModalHint = '保存后会写入 OpenClaw Workspace 下的 AGENTS.md。';
|
|
202
|
+
this.agentsModalTitle = tr('modal.agents.title.openclaw', 'OpenClaw AGENTS.md 编辑器');
|
|
203
|
+
this.agentsModalHint = tr('modal.agents.hint.openclaw', '保存后会写入 OpenClaw Workspace 下的 AGENTS.md。');
|
|
163
204
|
} else {
|
|
164
|
-
this.agentsModalTitle = 'AGENTS.md 编辑器';
|
|
165
|
-
this.agentsModalHint = '保存后会写入目标 AGENTS.md(与 config.toml 同级)。';
|
|
205
|
+
this.agentsModalTitle = tr('modal.agents.title.default', 'AGENTS.md 编辑器');
|
|
206
|
+
this.agentsModalHint = tr('modal.agents.hint.default', '保存后会写入目标 AGENTS.md(与 config.toml 同级)。');
|
|
166
207
|
}
|
|
167
208
|
this.agentsWorkspaceFileName = '';
|
|
168
209
|
},
|
|
@@ -232,16 +273,17 @@ export function createAgentsMethods(options = {}) {
|
|
|
232
273
|
this.confirmDialogResolver(false);
|
|
233
274
|
}
|
|
234
275
|
const confirmDisabled = options.confirmDisabled;
|
|
276
|
+
const t = typeof this.t === 'function' ? this.t : null;
|
|
235
277
|
this.confirmDialogTitle = typeof options.title === 'string' && options.title.trim()
|
|
236
278
|
? options.title.trim()
|
|
237
|
-
: '请确认操作';
|
|
279
|
+
: (t ? t('confirm.title.default') : '请确认操作');
|
|
238
280
|
this.confirmDialogMessage = typeof options.message === 'string' ? options.message : '';
|
|
239
281
|
this.confirmDialogConfirmText = typeof options.confirmText === 'string' && options.confirmText.trim()
|
|
240
282
|
? options.confirmText.trim()
|
|
241
|
-
: '确认';
|
|
283
|
+
: (t ? t('confirm.ok') : '确认');
|
|
242
284
|
this.confirmDialogCancelText = typeof options.cancelText === 'string' && options.cancelText.trim()
|
|
243
285
|
? options.cancelText.trim()
|
|
244
|
-
: '取消';
|
|
286
|
+
: (t ? t('confirm.cancel') : '取消');
|
|
245
287
|
this.confirmDialogDanger = !!options.danger;
|
|
246
288
|
this.confirmDialogConfirmDisabled = typeof confirmDisabled === 'function' ? false : !!confirmDisabled;
|
|
247
289
|
this.confirmDialogDisableWhen = typeof confirmDisabled === 'function' ? confirmDisabled : null;
|
|
@@ -267,8 +309,13 @@ export function createAgentsMethods(options = {}) {
|
|
|
267
309
|
this.showConfirmDialog = false;
|
|
268
310
|
this.confirmDialogTitle = '';
|
|
269
311
|
this.confirmDialogMessage = '';
|
|
270
|
-
this.
|
|
271
|
-
|
|
312
|
+
if (typeof this.t === 'function') {
|
|
313
|
+
this.confirmDialogConfirmText = this.t('confirm.ok');
|
|
314
|
+
this.confirmDialogCancelText = this.t('confirm.cancel');
|
|
315
|
+
} else {
|
|
316
|
+
this.confirmDialogConfirmText = '确认';
|
|
317
|
+
this.confirmDialogCancelText = '取消';
|
|
318
|
+
}
|
|
272
319
|
this.confirmDialogDanger = false;
|
|
273
320
|
this.confirmDialogConfirmDisabled = false;
|
|
274
321
|
this.confirmDialogDisableWhen = null;
|
|
@@ -467,7 +514,9 @@ export function createAgentsMethods(options = {}) {
|
|
|
467
514
|
content: this.agentsContent,
|
|
468
515
|
lineEnding: this.agentsLineEnding
|
|
469
516
|
};
|
|
470
|
-
if (this.agentsContext === '
|
|
517
|
+
if (this.agentsContext === 'claude-md') {
|
|
518
|
+
action = 'apply-claude-md-file';
|
|
519
|
+
} else if (this.agentsContext === 'openclaw') {
|
|
471
520
|
action = 'apply-openclaw-agents-file';
|
|
472
521
|
} else if (this.agentsContext === 'openclaw-workspace') {
|
|
473
522
|
action = 'apply-openclaw-workspace-file';
|
|
@@ -480,7 +529,9 @@ export function createAgentsMethods(options = {}) {
|
|
|
480
529
|
}
|
|
481
530
|
const successLabel = this.agentsContext === 'openclaw-workspace'
|
|
482
531
|
? `工作区文件已保存${this.agentsWorkspaceFileName ? `: ${this.agentsWorkspaceFileName}` : ''}`
|
|
483
|
-
: (this.agentsContext === '
|
|
532
|
+
: (this.agentsContext === 'claude-md'
|
|
533
|
+
? 'CLAUDE.md 已保存'
|
|
534
|
+
: (this.agentsContext === 'openclaw' ? 'OpenClaw AGENTS.md 已保存' : 'AGENTS.md 已保存'));
|
|
484
535
|
this.showMessage(successLabel, 'success');
|
|
485
536
|
this.closeAgentsModal({ force: true });
|
|
486
537
|
} catch (e) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runLatestOnlyQueue } from '../logic.mjs';
|
|
2
|
+
import { normalizeConfigTemplateDiffConfirmEnabled } from './config-template-confirm-pref.mjs';
|
|
2
3
|
|
|
3
4
|
function hasResponseError(response) {
|
|
4
5
|
if (!response || typeof response !== 'object') {
|
|
@@ -104,23 +105,42 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
104
105
|
const previousModelsSource = this.modelsSource;
|
|
105
106
|
const previousModelsHasCurrent = this.modelsHasCurrent;
|
|
106
107
|
this.currentProvider = name;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
108
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
109
|
+
|
|
110
|
+
// 不要把“切换提供商”强绑定到 /models 成功与否:
|
|
111
|
+
// 部分 OpenAI 兼容服务 /models 不可用或很慢,但用户仍希望一次点击即可完成切换。
|
|
112
|
+
// 这里做“短等待 + 后台补齐”:
|
|
113
|
+
// 1) 先启动 models 拉取(静默)
|
|
114
|
+
// 2) 给一个很短的窗口等待它完成,以便能立即选到第一个模型
|
|
115
|
+
// 3) 无论 models 是否成功,先应用 provider 切换
|
|
116
|
+
// 4) models 后续若补齐并发现当前 model 不在列表,则自动切到首个 model 并再应用一次
|
|
117
|
+
const modelsTask = this.loadModelsForProvider(name, { silentError: true })
|
|
118
|
+
.catch(() => {});
|
|
119
|
+
|
|
120
|
+
await Promise.race([modelsTask, delay(250)]);
|
|
121
|
+
|
|
116
122
|
if (this.modelsSource === 'remote' && this.models.length > 0 && !this.models.includes(this.currentModel)) {
|
|
117
123
|
this.currentModel = this.models[0];
|
|
118
124
|
this.modelsHasCurrent = true;
|
|
119
125
|
}
|
|
126
|
+
|
|
120
127
|
if (getProviderConfigModeMeta(this.configMode)) {
|
|
121
128
|
await this.waitForCodexApplyIdle();
|
|
122
129
|
await this.applyCodexConfigDirect({ silent: true });
|
|
123
130
|
}
|
|
131
|
+
|
|
132
|
+
await modelsTask;
|
|
133
|
+
|
|
134
|
+
if (this.currentProvider === name) {
|
|
135
|
+
if (this.modelsSource === 'remote' && this.models.length > 0 && !this.models.includes(this.currentModel)) {
|
|
136
|
+
this.currentModel = this.models[0];
|
|
137
|
+
this.modelsHasCurrent = true;
|
|
138
|
+
if (getProviderConfigModeMeta(this.configMode)) {
|
|
139
|
+
await this.waitForCodexApplyIdle();
|
|
140
|
+
await this.applyCodexConfigDirect({ silent: true });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
124
144
|
},
|
|
125
145
|
|
|
126
146
|
async switchProvider(name) {
|
|
@@ -191,7 +211,7 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
191
211
|
const fallbackText = fallback === '' ? '' : String(fallback).trim();
|
|
192
212
|
const raw = typeof value === 'string'
|
|
193
213
|
? value.trim()
|
|
194
|
-
: String(value
|
|
214
|
+
: String(value == null ? '' : value).trim();
|
|
195
215
|
const text = raw || fallbackText;
|
|
196
216
|
if (!text) {
|
|
197
217
|
return { ok: true, value: null, text: '' };
|
|
@@ -329,7 +349,7 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
329
349
|
},
|
|
330
350
|
|
|
331
351
|
buildDefaultHealthCheckPrompt() {
|
|
332
|
-
return '
|
|
352
|
+
return '请简短回复:连接正常。';
|
|
333
353
|
},
|
|
334
354
|
|
|
335
355
|
openHealthCheckDialog(options = {}) {
|
|
@@ -337,6 +357,12 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
337
357
|
? options.providerName.trim()
|
|
338
358
|
: '';
|
|
339
359
|
const locked = !!options.locked && !!providerName;
|
|
360
|
+
if (locked && providerName && providerName !== String(this.currentProvider || '').trim()) {
|
|
361
|
+
if (typeof this.showMessage === 'function') {
|
|
362
|
+
this.showMessage('请先切换到该提供商再进行健康聊天测试', 'info');
|
|
363
|
+
}
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
340
366
|
const nextProvider = providerName
|
|
341
367
|
|| String(this.healthCheckDialogSelectedProvider || '').trim()
|
|
342
368
|
|| String(this.currentProvider || '').trim()
|
|
@@ -376,7 +402,7 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
376
402
|
return;
|
|
377
403
|
}
|
|
378
404
|
if (!prompt) {
|
|
379
|
-
this.showMessage('
|
|
405
|
+
this.showMessage('请输入消息内容', 'error');
|
|
380
406
|
return;
|
|
381
407
|
}
|
|
382
408
|
|
|
@@ -396,7 +422,7 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
396
422
|
this.healthCheckDialogLastResult = res;
|
|
397
423
|
|
|
398
424
|
if (hasResponseError(res) || res.ok === false) {
|
|
399
|
-
const message = getResponseMessage(res, '
|
|
425
|
+
const message = getResponseMessage(res, '健康聊天测试失败');
|
|
400
426
|
this.healthCheckDialogMessages.push({
|
|
401
427
|
id: `assistant-${Date.now()}`,
|
|
402
428
|
role: 'assistant',
|
|
@@ -413,7 +439,7 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
413
439
|
|
|
414
440
|
const reply = typeof res.reply === 'string' && res.reply.trim()
|
|
415
441
|
? res.reply.trim()
|
|
416
|
-
: '
|
|
442
|
+
: '已收到回复,但未解析到可展示文本。';
|
|
417
443
|
this.healthCheckDialogMessages.push({
|
|
418
444
|
id: `assistant-${Date.now()}`,
|
|
419
445
|
role: 'assistant',
|
|
@@ -426,7 +452,7 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
426
452
|
});
|
|
427
453
|
this.healthCheckDialogPrompt = '';
|
|
428
454
|
} catch (e) {
|
|
429
|
-
const message = e && e.message ? e.message : '
|
|
455
|
+
const message = e && e.message ? e.message : '健康聊天测试失败';
|
|
430
456
|
this.healthCheckDialogMessages.push({
|
|
431
457
|
id: `assistant-${Date.now()}`,
|
|
432
458
|
role: 'assistant',
|
|
@@ -451,6 +477,7 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
451
477
|
},
|
|
452
478
|
|
|
453
479
|
async openConfigTemplateEditor(options = {}) {
|
|
480
|
+
this.resetConfigTemplateDiffState();
|
|
454
481
|
const modelContextWindow = this.normalizePositiveIntegerInput(
|
|
455
482
|
this.modelContextWindowInput,
|
|
456
483
|
'model_context_window',
|
|
@@ -598,43 +625,171 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
598
625
|
|
|
599
626
|
closeConfigTemplateModal(options = {}) {
|
|
600
627
|
const force = !!options.force;
|
|
601
|
-
if (!force && this.configTemplateApplying) {
|
|
628
|
+
if (!force && (this.configTemplateApplying || this.configTemplateDiffLoading)) {
|
|
602
629
|
return;
|
|
603
630
|
}
|
|
604
631
|
this.showConfigTemplateModal = false;
|
|
605
632
|
this.configTemplateContent = '';
|
|
633
|
+
this.resetConfigTemplateDiffState();
|
|
606
634
|
},
|
|
607
635
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
636
|
+
resetConfigTemplateDiffState() {
|
|
637
|
+
this.configTemplateDiffVisible = false;
|
|
638
|
+
this.configTemplateDiffLoading = false;
|
|
639
|
+
this.configTemplateDiffError = '';
|
|
640
|
+
this.configTemplateDiffLines = [];
|
|
641
|
+
this.configTemplateDiffStats = { added: 0, removed: 0, unchanged: 0 };
|
|
642
|
+
this.configTemplateDiffHasChangesValue = false;
|
|
643
|
+
this.configTemplateDiffFingerprint = '';
|
|
644
|
+
this._configTemplateDiffPreviewRequestToken = null;
|
|
645
|
+
},
|
|
646
|
+
|
|
647
|
+
onConfigTemplateContentInput() {
|
|
648
|
+
if (this.configTemplateDiffVisible || (this.configTemplateDiffLines && this.configTemplateDiffLines.length)) {
|
|
649
|
+
this.resetConfigTemplateDiffState();
|
|
611
650
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
651
|
+
},
|
|
652
|
+
|
|
653
|
+
buildConfigTemplateDiffFingerprint() {
|
|
654
|
+
const content = typeof this.configTemplateContent === 'string' ? this.configTemplateContent : '';
|
|
655
|
+
return `${content.length}::${content}`;
|
|
656
|
+
},
|
|
657
|
+
|
|
658
|
+
hasConfigTemplateDiffChanges() {
|
|
659
|
+
if (this.configTemplateDiffHasChangesValue !== undefined && this.configTemplateDiffHasChangesValue !== null) {
|
|
660
|
+
return !!this.configTemplateDiffHasChangesValue;
|
|
615
661
|
}
|
|
662
|
+
const stats = this.configTemplateDiffStats && typeof this.configTemplateDiffStats === 'object'
|
|
663
|
+
? this.configTemplateDiffStats
|
|
664
|
+
: {};
|
|
665
|
+
const added = Number(stats.added || 0);
|
|
666
|
+
const removed = Number(stats.removed || 0);
|
|
667
|
+
return added > 0 || removed > 0;
|
|
668
|
+
},
|
|
616
669
|
|
|
617
|
-
|
|
670
|
+
async prepareConfigTemplateDiff() {
|
|
671
|
+
const requestFingerprint = this.buildConfigTemplateDiffFingerprint();
|
|
672
|
+
const requestToken = Symbol('config-template-diff-preview');
|
|
673
|
+
this._configTemplateDiffPreviewRequestToken = requestToken;
|
|
674
|
+
this.configTemplateDiffVisible = true;
|
|
675
|
+
this.configTemplateDiffLoading = true;
|
|
676
|
+
this.configTemplateDiffError = '';
|
|
677
|
+
this.configTemplateDiffLines = [];
|
|
678
|
+
this.configTemplateDiffStats = { added: 0, removed: 0, unchanged: 0 };
|
|
679
|
+
this.configTemplateDiffHasChangesValue = false;
|
|
618
680
|
try {
|
|
619
|
-
const
|
|
681
|
+
const shouldApply = () => (
|
|
682
|
+
this.configTemplateDiffVisible
|
|
683
|
+
&& this._configTemplateDiffPreviewRequestToken === requestToken
|
|
684
|
+
&& this.buildConfigTemplateDiffFingerprint() === requestFingerprint
|
|
685
|
+
);
|
|
686
|
+
const res = await api('preview-config-template-diff', {
|
|
620
687
|
template: this.configTemplateContent
|
|
621
688
|
});
|
|
689
|
+
if (!shouldApply()) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
622
692
|
if (res.error) {
|
|
623
|
-
this.
|
|
693
|
+
this.configTemplateDiffError = res.error;
|
|
624
694
|
return;
|
|
625
695
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
this.
|
|
696
|
+
const diff = res.diff && typeof res.diff === 'object' ? res.diff : {};
|
|
697
|
+
const lines = Array.isArray(diff.lines) ? diff.lines : [];
|
|
698
|
+
this.configTemplateDiffLines = lines.filter(line => line && line.type);
|
|
699
|
+
const stats = diff.stats && typeof diff.stats === 'object' ? diff.stats : null;
|
|
700
|
+
if (stats) {
|
|
701
|
+
this.configTemplateDiffStats = {
|
|
702
|
+
added: Number(stats.added || 0),
|
|
703
|
+
removed: Number(stats.removed || 0),
|
|
704
|
+
unchanged: Number(stats.unchanged || 0)
|
|
705
|
+
};
|
|
706
|
+
} else {
|
|
707
|
+
const nextStats = { added: 0, removed: 0, unchanged: 0 };
|
|
708
|
+
for (const line of this.configTemplateDiffLines) {
|
|
709
|
+
if (line && line.type === 'add') nextStats.added += 1;
|
|
710
|
+
else if (line && line.type === 'del') nextStats.removed += 1;
|
|
711
|
+
else nextStats.unchanged += 1;
|
|
712
|
+
}
|
|
713
|
+
this.configTemplateDiffStats = nextStats;
|
|
714
|
+
}
|
|
715
|
+
this.configTemplateDiffHasChangesValue = !!diff.hasChanges;
|
|
716
|
+
this.configTemplateDiffFingerprint = requestFingerprint;
|
|
717
|
+
} catch (_) {
|
|
718
|
+
if (this._configTemplateDiffPreviewRequestToken === requestToken) {
|
|
719
|
+
this.configTemplateDiffError = '生成差异失败';
|
|
632
720
|
}
|
|
633
|
-
} catch (e) {
|
|
634
|
-
this.showMessage('应用模板失败', 'error');
|
|
635
721
|
} finally {
|
|
636
|
-
this.
|
|
722
|
+
if (this._configTemplateDiffPreviewRequestToken === requestToken) {
|
|
723
|
+
this.configTemplateDiffLoading = false;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
|
|
728
|
+
async applyConfigTemplate() {
|
|
729
|
+
if (this.configTemplateApplying) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
if (!this.configTemplateContent || !this.configTemplateContent.trim()) {
|
|
733
|
+
this.showMessage('模板不能为空', 'error');
|
|
734
|
+
return;
|
|
637
735
|
}
|
|
736
|
+
|
|
737
|
+
// Default to two-step confirmation when the setting is unset.
|
|
738
|
+
// (The normalize helper lives in session-actions; keep a safe fallback here.)
|
|
739
|
+
const shouldUseTwoStepConfirm = normalizeConfigTemplateDiffConfirmEnabled(this.configTemplateDiffConfirmEnabled);
|
|
740
|
+
|
|
741
|
+
const performApply = async () => {
|
|
742
|
+
this.configTemplateApplying = true;
|
|
743
|
+
try {
|
|
744
|
+
const res = await api('apply-config-template', {
|
|
745
|
+
template: this.configTemplateContent
|
|
746
|
+
});
|
|
747
|
+
if (res.error) {
|
|
748
|
+
this.showMessage(res.error, 'error');
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
this.showMessage('模板已应用', 'success');
|
|
752
|
+
this.closeConfigTemplateModal({ force: true });
|
|
753
|
+
try {
|
|
754
|
+
await this.loadAll();
|
|
755
|
+
} catch (_) {
|
|
756
|
+
this.showMessage('模板已应用,但界面刷新失败,请手动刷新', 'error');
|
|
757
|
+
}
|
|
758
|
+
} catch (e) {
|
|
759
|
+
this.showMessage('应用模板失败', 'error');
|
|
760
|
+
} finally {
|
|
761
|
+
this.configTemplateApplying = false;
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
// One-step mode: apply immediately unless user explicitly entered the diff preview state.
|
|
766
|
+
if (!shouldUseTwoStepConfirm && !this.configTemplateDiffVisible) {
|
|
767
|
+
await performApply();
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
if (!this.configTemplateDiffVisible) {
|
|
772
|
+
await this.prepareConfigTemplateDiff();
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (this.configTemplateDiffLoading) {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
if (this.configTemplateDiffError) {
|
|
779
|
+
this.showMessage(this.configTemplateDiffError, 'error');
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
const fingerprint = this.buildConfigTemplateDiffFingerprint();
|
|
783
|
+
if (this.configTemplateDiffFingerprint !== fingerprint) {
|
|
784
|
+
await this.prepareConfigTemplateDiff();
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (!this.hasConfigTemplateDiffChanges()) {
|
|
788
|
+
this.showMessage('未检测到改动', 'info');
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
await performApply();
|
|
638
793
|
}
|
|
639
794
|
};
|
|
640
795
|
}
|
|
@@ -20,12 +20,15 @@ import { createOpenclawEditingMethods } from './app.methods.openclaw-editing.mjs
|
|
|
20
20
|
import { createOpenclawPersistMethods } from './app.methods.openclaw-persist.mjs';
|
|
21
21
|
import { createProvidersMethods } from './app.methods.providers.mjs';
|
|
22
22
|
import { createRuntimeMethods } from './app.methods.runtime.mjs';
|
|
23
|
+
import { createTaskOrchestrationMethods } from './app.methods.task-orchestration.mjs';
|
|
23
24
|
import { createSessionActionMethods } from './app.methods.session-actions.mjs';
|
|
24
25
|
import { createSessionBrowserMethods } from './app.methods.session-browser.mjs';
|
|
25
26
|
import { createSessionTimelineMethods } from './app.methods.session-timeline.mjs';
|
|
26
27
|
import { createSessionTrashMethods } from './app.methods.session-trash.mjs';
|
|
27
28
|
import { createStartupClaudeMethods } from './app.methods.startup-claude.mjs';
|
|
28
29
|
import { createSkillsMethods } from './skills.methods.mjs';
|
|
30
|
+
import { createPluginsMethods } from './plugins.methods.mjs';
|
|
31
|
+
import { createI18nMethods } from './i18n.mjs';
|
|
29
32
|
import {
|
|
30
33
|
CONFIG_MODE_SET,
|
|
31
34
|
getProviderConfigModeMeta
|
|
@@ -39,6 +42,7 @@ import {
|
|
|
39
42
|
|
|
40
43
|
export function createAppMethods() {
|
|
41
44
|
return {
|
|
45
|
+
...createI18nMethods(),
|
|
42
46
|
...createStartupClaudeMethods({
|
|
43
47
|
api,
|
|
44
48
|
defaultModelContextWindow: DEFAULT_MODEL_CONTEXT_WINDOW,
|
|
@@ -71,6 +75,7 @@ export function createAppMethods() {
|
|
|
71
75
|
getProviderConfigModeMeta
|
|
72
76
|
}),
|
|
73
77
|
...createSkillsMethods({ api }),
|
|
78
|
+
...createPluginsMethods(),
|
|
74
79
|
...createAgentsMethods({ api, apiWithMeta }),
|
|
75
80
|
...createProvidersMethods({ api }),
|
|
76
81
|
...createClaudeConfigMethods({ api }),
|
|
@@ -81,6 +86,7 @@ export function createAppMethods() {
|
|
|
81
86
|
defaultOpenclawTemplate: DEFAULT_OPENCLAW_TEMPLATE
|
|
82
87
|
}),
|
|
83
88
|
...createInstallMethods(),
|
|
84
|
-
...createRuntimeMethods({ api })
|
|
89
|
+
...createRuntimeMethods({ api }),
|
|
90
|
+
...createTaskOrchestrationMethods({ api })
|
|
85
91
|
};
|
|
86
92
|
}
|
|
@@ -78,6 +78,13 @@ export function createInstallMethods() {
|
|
|
78
78
|
},
|
|
79
79
|
|
|
80
80
|
resolveInstallPlatform() {
|
|
81
|
+
const navUserAgent = typeof navigator !== 'undefined' && typeof navigator.userAgent === 'string'
|
|
82
|
+
? navigator.userAgent.trim().toLowerCase()
|
|
83
|
+
: '';
|
|
84
|
+
// Termux runs on Android; Codex CLI needs a Termux-friendly build.
|
|
85
|
+
if (navUserAgent.includes('termux') || navUserAgent.includes('android')) {
|
|
86
|
+
return 'termux';
|
|
87
|
+
}
|
|
81
88
|
const navPlatform = typeof navigator !== 'undefined' && typeof navigator.platform === 'string'
|
|
82
89
|
? navigator.platform.trim().toLowerCase()
|
|
83
90
|
: '';
|
|
@@ -86,8 +93,11 @@ export function createInstallMethods() {
|
|
|
86
93
|
return 'linux';
|
|
87
94
|
},
|
|
88
95
|
|
|
89
|
-
buildInstallCommandMatrix(packageManager) {
|
|
96
|
+
buildInstallCommandMatrix(packageManager, platformOverride = '') {
|
|
90
97
|
const manager = this.normalizeInstallPackageManager(packageManager);
|
|
98
|
+
const platform = platformOverride ? String(platformOverride).trim().toLowerCase() : this.resolveInstallPlatform();
|
|
99
|
+
const codexPackage = platform === 'termux' ? '@mmmbuto/codex-cli-termux' : '@openai/codex';
|
|
100
|
+
const codexInstallPackage = platform === 'termux' ? '@mmmbuto/codex-cli-termux@latest' : '@openai/codex';
|
|
91
101
|
const matrix = {
|
|
92
102
|
claude: {
|
|
93
103
|
install: '',
|
|
@@ -104,34 +114,36 @@ export function createInstallMethods() {
|
|
|
104
114
|
matrix.claude.install = 'pnpm add -g @anthropic-ai/claude-code';
|
|
105
115
|
matrix.claude.update = 'pnpm up -g @anthropic-ai/claude-code';
|
|
106
116
|
matrix.claude.uninstall = 'pnpm remove -g @anthropic-ai/claude-code';
|
|
107
|
-
matrix.codex.install =
|
|
108
|
-
matrix.codex.update =
|
|
109
|
-
matrix.codex.uninstall =
|
|
117
|
+
matrix.codex.install = `pnpm add -g ${codexInstallPackage}`;
|
|
118
|
+
matrix.codex.update = `pnpm up -g ${codexPackage}`;
|
|
119
|
+
matrix.codex.uninstall = `pnpm remove -g ${codexPackage}`;
|
|
110
120
|
return matrix;
|
|
111
121
|
}
|
|
112
122
|
if (manager === 'bun') {
|
|
113
123
|
matrix.claude.install = 'bun add -g @anthropic-ai/claude-code';
|
|
114
124
|
matrix.claude.update = 'bun update -g @anthropic-ai/claude-code';
|
|
115
125
|
matrix.claude.uninstall = 'bun remove -g @anthropic-ai/claude-code';
|
|
116
|
-
matrix.codex.install =
|
|
117
|
-
matrix.codex.update =
|
|
118
|
-
matrix.codex.uninstall =
|
|
126
|
+
matrix.codex.install = `bun add -g ${codexInstallPackage}`;
|
|
127
|
+
matrix.codex.update = `bun update -g ${codexPackage}`;
|
|
128
|
+
matrix.codex.uninstall = `bun remove -g ${codexPackage}`;
|
|
119
129
|
return matrix;
|
|
120
130
|
}
|
|
121
131
|
matrix.claude.install = 'npm install -g @anthropic-ai/claude-code';
|
|
122
132
|
matrix.claude.update = 'npm update -g @anthropic-ai/claude-code';
|
|
123
133
|
matrix.claude.uninstall = 'npm uninstall -g @anthropic-ai/claude-code';
|
|
124
|
-
matrix.codex.install =
|
|
125
|
-
matrix.codex.update =
|
|
126
|
-
|
|
134
|
+
matrix.codex.install = `npm install -g ${codexInstallPackage}`;
|
|
135
|
+
matrix.codex.update = platform === 'termux'
|
|
136
|
+
? `npm install -g ${codexInstallPackage}`
|
|
137
|
+
: `npm update -g ${codexPackage}`;
|
|
138
|
+
matrix.codex.uninstall = `npm uninstall -g ${codexPackage}`;
|
|
127
139
|
return matrix;
|
|
128
140
|
},
|
|
129
141
|
|
|
130
|
-
getInstallCommand(targetId, actionName) {
|
|
142
|
+
getInstallCommand(targetId, actionName, platformOverride = '') {
|
|
131
143
|
const targetKey = typeof targetId === 'string' ? targetId.trim() : '';
|
|
132
144
|
if (!targetKey) return '';
|
|
133
145
|
const action = this.normalizeInstallAction(actionName);
|
|
134
|
-
const currentMap = this.buildInstallCommandMatrix(this.installPackageManager);
|
|
146
|
+
const currentMap = this.buildInstallCommandMatrix(this.installPackageManager, platformOverride);
|
|
135
147
|
const current = currentMap[targetKey] && typeof currentMap[targetKey][action] === 'string'
|
|
136
148
|
? currentMap[targetKey][action]
|
|
137
149
|
: '';
|
|
@@ -144,14 +156,6 @@ export function createInstallMethods() {
|
|
|
144
156
|
|
|
145
157
|
setInstallRegistryPreset(presetName) {
|
|
146
158
|
this.installRegistryPreset = this.normalizeInstallRegistryPreset(presetName);
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
openInstallModal() {
|
|
150
|
-
this.showInstallModal = true;
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
closeInstallModal() {
|
|
154
|
-
this.showInstallModal = false;
|
|
155
159
|
}
|
|
156
160
|
};
|
|
157
161
|
}
|