codexmate 0.0.20 → 0.0.21
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.en.md +349 -259
- package/README.md +284 -252
- package/cli/agents-files.js +162 -0
- package/cli/archive-helpers.js +446 -0
- package/cli/auth-profiles.js +359 -0
- package/cli/builtin-proxy.js +580 -0
- package/cli/claude-proxy.js +998 -0
- package/cli/config-bootstrap.js +384 -0
- package/cli/config-health.js +338 -338
- package/cli/openclaw-config.js +629 -0
- package/cli/skills.js +1141 -0
- package/cli/zip-commands.js +510 -0
- package/cli.js +13101 -13497
- package/lib/cli-file-utils.js +151 -151
- package/lib/cli-models-utils.js +419 -311
- package/lib/cli-network-utils.js +164 -164
- package/lib/cli-path-utils.js +69 -0
- package/lib/cli-session-utils.js +121 -121
- package/lib/cli-sessions.js +386 -0
- package/lib/cli-utils.js +155 -155
- package/lib/download-artifacts.js +77 -0
- package/lib/mcp-stdio.js +440 -440
- package/lib/task-orchestrator.js +869 -0
- package/lib/text-diff.js +303 -303
- package/lib/workflow-engine.js +340 -340
- package/package.json +74 -70
- package/res/json5.min.js +1 -1
- package/res/vue.global.prod.js +13 -0
- package/web-ui/app.js +530 -397
- package/web-ui/index.html +33 -30
- package/web-ui/logic.agents-diff.mjs +386 -386
- package/web-ui/logic.claude.mjs +168 -108
- package/web-ui/logic.mjs +5 -5
- package/web-ui/logic.runtime.mjs +124 -124
- package/web-ui/logic.sessions.mjs +581 -263
- package/web-ui/modules/api.mjs +90 -69
- package/web-ui/modules/app.computed.dashboard.mjs +113 -113
- package/web-ui/modules/app.computed.index.mjs +15 -13
- package/web-ui/modules/app.computed.main-tabs.mjs +195 -0
- package/web-ui/modules/app.computed.session.mjs +507 -141
- package/web-ui/modules/app.constants.mjs +15 -15
- package/web-ui/modules/app.methods.agents.mjs +493 -493
- package/web-ui/modules/app.methods.claude-config.mjs +174 -174
- package/web-ui/modules/app.methods.codex-config.mjs +640 -640
- package/web-ui/modules/app.methods.index.mjs +88 -86
- package/web-ui/modules/app.methods.install.mjs +149 -157
- package/web-ui/modules/app.methods.navigation.mjs +619 -478
- package/web-ui/modules/app.methods.openclaw-core.mjs +814 -514
- package/web-ui/modules/app.methods.openclaw-editing.mjs +372 -337
- package/web-ui/modules/app.methods.openclaw-persist.mjs +369 -251
- package/web-ui/modules/app.methods.providers.mjs +363 -265
- package/web-ui/modules/app.methods.runtime.mjs +323 -323
- package/web-ui/modules/app.methods.session-actions.mjs +520 -457
- package/web-ui/modules/app.methods.session-browser.mjs +626 -435
- package/web-ui/modules/app.methods.session-timeline.mjs +448 -441
- package/web-ui/modules/app.methods.session-trash.mjs +422 -419
- package/web-ui/modules/app.methods.startup-claude.mjs +412 -406
- package/web-ui/modules/app.methods.task-orchestration.mjs +471 -0
- package/web-ui/modules/config-mode.computed.mjs +126 -124
- package/web-ui/modules/skills.computed.mjs +107 -107
- package/web-ui/modules/skills.methods.mjs +481 -481
- package/web-ui/partials/index/layout-footer.html +13 -69
- package/web-ui/partials/index/layout-header.html +402 -337
- package/web-ui/partials/index/modal-config-template-agents.html +125 -125
- package/web-ui/partials/index/modal-confirm-toast.html +32 -32
- package/web-ui/partials/index/modal-health-check.html +72 -72
- package/web-ui/partials/index/modal-openclaw-config.html +280 -275
- package/web-ui/partials/index/modal-skills.html +184 -184
- package/web-ui/partials/index/modals-basic.html +156 -196
- package/web-ui/partials/index/panel-config-claude.html +126 -100
- package/web-ui/partials/index/panel-config-codex.html +237 -237
- package/web-ui/partials/index/panel-config-openclaw.html +78 -84
- package/web-ui/partials/index/panel-docs.html +130 -0
- package/web-ui/partials/index/panel-market.html +174 -174
- package/web-ui/partials/index/panel-orchestration.html +397 -0
- package/web-ui/partials/index/panel-sessions.html +292 -387
- package/web-ui/partials/index/panel-settings.html +190 -166
- package/web-ui/partials/index/panel-usage.html +213 -0
- package/web-ui/session-helpers.mjs +559 -362
- package/web-ui/source-bundle.cjs +233 -233
- package/web-ui/styles/base-theme.css +271 -373
- package/web-ui/styles/controls-forms.css +360 -354
- package/web-ui/styles/docs-panel.css +182 -0
- package/web-ui/styles/feedback.css +108 -108
- package/web-ui/styles/health-check-dialog.css +144 -144
- package/web-ui/styles/layout-shell.css +376 -330
- package/web-ui/styles/modals-core.css +464 -449
- package/web-ui/styles/navigation-panels.css +348 -381
- package/web-ui/styles/openclaw-structured.css +266 -266
- package/web-ui/styles/responsive.css +450 -416
- package/web-ui/styles/sessions-list.css +400 -414
- package/web-ui/styles/sessions-preview.css +411 -405
- package/web-ui/styles/sessions-toolbar-trash.css +243 -243
- package/web-ui/styles/sessions-usage.css +628 -276
- package/web-ui/styles/skills-list.css +296 -298
- package/web-ui/styles/skills-market.css +335 -335
- package/web-ui/styles/task-orchestration.css +776 -0
- package/web-ui/styles/titles-cards.css +408 -407
- package/web-ui/styles.css +18 -16
- package/web-ui.html +17 -17
- package/res/screenshot.png +0 -0
- package/res/vue.global.js +0 -18552
package/web-ui/modules/api.mjs
CHANGED
|
@@ -1,69 +1,90 @@
|
|
|
1
|
-
const browserLocation = typeof location !== 'undefined' ? location : null;
|
|
2
|
-
|
|
3
|
-
export const API_BASE = (browserLocation && browserLocation.origin && browserLocation.origin !== 'null')
|
|
4
|
-
? browserLocation.origin
|
|
5
|
-
: 'http://localhost:3737';
|
|
6
|
-
|
|
7
|
-
async function postApi(action, params = {}) {
|
|
8
|
-
return await fetch(`${API_BASE}/api`, {
|
|
9
|
-
method: 'POST',
|
|
10
|
-
headers: { 'Content-Type': 'application/json' },
|
|
11
|
-
body: JSON.stringify({ action, params })
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function buildApiResponseContext(action, res, contentType) {
|
|
16
|
-
return `${action} (${res.status} ${res.statusText}, content-type: ${contentType || 'unknown'})`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
1
|
+
const browserLocation = typeof location !== 'undefined' ? location : null;
|
|
2
|
+
|
|
3
|
+
export const API_BASE = (browserLocation && browserLocation.origin && browserLocation.origin !== 'null')
|
|
4
|
+
? browserLocation.origin
|
|
5
|
+
: 'http://localhost:3737';
|
|
6
|
+
|
|
7
|
+
async function postApi(action, params = {}) {
|
|
8
|
+
return await fetch(`${API_BASE}/api`, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: { 'Content-Type': 'application/json' },
|
|
11
|
+
body: JSON.stringify({ action, params })
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function buildApiResponseContext(action, res, contentType) {
|
|
16
|
+
return `${action} (${res.status} ${res.statusText}, content-type: ${contentType || 'unknown'})`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function formatUnexpectedApiBodySnippet(body, contentType) {
|
|
20
|
+
const raw = typeof body === 'string' ? body.trim() : '';
|
|
21
|
+
if (!raw) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
const normalizedContentType = String(contentType || '').toLowerCase();
|
|
25
|
+
const looksLikeHtml = normalizedContentType.includes('text/html')
|
|
26
|
+
|| /<!doctype\s+html|<html[\s>]|<head[\s>]|<body[\s>]/i.test(raw);
|
|
27
|
+
if (looksLikeHtml) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
const singleLine = raw.replace(/\s+/g, ' ').trim();
|
|
31
|
+
if (!singleLine) {
|
|
32
|
+
return '';
|
|
33
|
+
}
|
|
34
|
+
return singleLine.length > 200
|
|
35
|
+
? `${singleLine.slice(0, 197)}...`
|
|
36
|
+
: singleLine;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function withPayloadTooLargeErrorCode(res, payload) {
|
|
40
|
+
if (res.status !== 413 || (payload && typeof payload === 'object' && payload.errorCode)) {
|
|
41
|
+
return payload;
|
|
42
|
+
}
|
|
43
|
+
return { ...payload, errorCode: 'payload-too-large' };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function api(action, params = {}) {
|
|
47
|
+
const res = await postApi(action, params);
|
|
48
|
+
const contentType = String(res.headers.get('content-type') || '').toLowerCase();
|
|
49
|
+
if (contentType && !contentType.includes('application/json')) {
|
|
50
|
+
const body = await res.text();
|
|
51
|
+
const errorDetails = buildApiResponseContext(action, res, contentType);
|
|
52
|
+
const bodySnippet = formatUnexpectedApiBodySnippet(body, contentType);
|
|
53
|
+
const bodyDetails = bodySnippet ? `: ${bodySnippet}` : '';
|
|
54
|
+
throw new Error(`Unexpected non-JSON API response for ${errorDetails}${bodyDetails}`);
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
return await res.json();
|
|
58
|
+
} catch (error) {
|
|
59
|
+
const errorDetails = buildApiResponseContext(action, res, contentType);
|
|
60
|
+
throw new Error(`Failed to parse API response for ${errorDetails}: ${error.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function apiWithMeta(action, params = {}) {
|
|
65
|
+
const res = await postApi(action, params);
|
|
66
|
+
const contentType = String(res.headers.get('content-type') || '').toLowerCase();
|
|
67
|
+
if (contentType.includes('application/json')) {
|
|
68
|
+
try {
|
|
69
|
+
const payload = await res.json();
|
|
70
|
+
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
|
71
|
+
return { ...withPayloadTooLargeErrorCode(res, payload), ok: res.ok, status: res.status };
|
|
72
|
+
}
|
|
73
|
+
return res.status === 413
|
|
74
|
+
? { ok: res.ok, status: res.status, data: payload, errorCode: 'payload-too-large' }
|
|
75
|
+
: { ok: res.ok, status: res.status, data: payload };
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (res.status === 413) {
|
|
78
|
+
return { ok: false, status: 413, errorCode: 'payload-too-large' };
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const error = await res.text();
|
|
84
|
+
return {
|
|
85
|
+
ok: res.ok,
|
|
86
|
+
status: res.status,
|
|
87
|
+
error,
|
|
88
|
+
errorCode: res.status === 413 ? 'payload-too-large' : ''
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
export function createDashboardComputed() {
|
|
2
|
-
return {
|
|
3
|
-
agentsDiffHasChanges() {
|
|
4
|
-
if (this.agentsDiffTruncated) {
|
|
5
|
-
return !!this.agentsDiffHasChangesValue;
|
|
6
|
-
}
|
|
7
|
-
const stats = this.agentsDiffStats || {};
|
|
8
|
-
const added = Number(stats.added || 0);
|
|
9
|
-
const removed = Number(stats.removed || 0);
|
|
10
|
-
return added > 0 || removed > 0;
|
|
11
|
-
},
|
|
12
|
-
claudeModelHasList() {
|
|
13
|
-
return this.claudeModelOptions.length > 0;
|
|
14
|
-
},
|
|
15
|
-
claudeModelOptions() {
|
|
16
|
-
const list = Array.isArray(this.claudeModels) ? [...this.claudeModels] : [];
|
|
17
|
-
const current = (this.currentClaudeModel || '').trim();
|
|
18
|
-
if (current && !list.includes(current)) {
|
|
19
|
-
list.unshift(current);
|
|
20
|
-
}
|
|
21
|
-
return list;
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (this.
|
|
53
|
-
if (this.
|
|
54
|
-
if (this.
|
|
55
|
-
if (this.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return tasks.length ? tasks.join(' / ') : '空闲';
|
|
59
|
-
},
|
|
60
|
-
inspectorMessageSummary() {
|
|
61
|
-
const value = typeof this.message === 'string' ? this.message.trim() : '';
|
|
62
|
-
return value || '暂无提示';
|
|
63
|
-
},
|
|
64
|
-
inspectorSessionSourceLabel() {
|
|
65
|
-
if (this.sessionFilterSource === 'codex') return 'Codex';
|
|
66
|
-
if (this.sessionFilterSource === 'claude') return 'Claude Code';
|
|
67
|
-
return '全部';
|
|
68
|
-
},
|
|
69
|
-
inspectorSessionPathLabel() {
|
|
70
|
-
const value = typeof this.sessionPathFilter === 'string' ? this.sessionPathFilter.trim() : '';
|
|
71
|
-
return value || '全部路径';
|
|
72
|
-
},
|
|
73
|
-
inspectorSessionQueryLabel() {
|
|
74
|
-
if (!this.isSessionQueryEnabled) return '当前来源不支持';
|
|
75
|
-
const value = typeof this.sessionQuery === 'string' ? this.sessionQuery.trim() : '';
|
|
76
|
-
return value || '未设置';
|
|
77
|
-
},
|
|
78
|
-
inspectorHealthStatus() {
|
|
79
|
-
if (this.initError) return '读取失败';
|
|
80
|
-
if (this.loading) return '初始化中';
|
|
81
|
-
return '正常';
|
|
82
|
-
},
|
|
83
|
-
inspectorHealthTone() {
|
|
84
|
-
if (this.initError) return 'error';
|
|
85
|
-
if (this.loading) return 'warn';
|
|
86
|
-
return 'ok';
|
|
87
|
-
},
|
|
88
|
-
inspectorModelLoadStatus() {
|
|
89
|
-
if (this.codexModelsLoading || this.claudeModelsLoading) {
|
|
90
|
-
return '加载中';
|
|
91
|
-
}
|
|
92
|
-
if (this.modelsSource === 'error' || this.claudeModelsSource === 'error') {
|
|
93
|
-
return '加载异常';
|
|
94
|
-
}
|
|
95
|
-
return '正常';
|
|
96
|
-
},
|
|
97
|
-
installTroubleshootingTips() {
|
|
98
|
-
const platform = this.resolveInstallPlatform();
|
|
99
|
-
if (platform === 'win32') {
|
|
100
|
-
return [
|
|
101
|
-
'PowerShell 报权限不足(EACCES/EPERM)时,请以管理员身份执行安装命令。',
|
|
102
|
-
'安装后若仍提示找不到命令,重开终端并执行:where codex / where claude。',
|
|
103
|
-
'公司网络受限时,可先切换镜像源快捷项(npmmirror / 腾讯云 / 自定义)。'
|
|
104
|
-
];
|
|
105
|
-
}
|
|
106
|
-
return [
|
|
107
|
-
'出现 EACCES 权限错误时,优先修复 Node 全局目录权限,不建议直接 sudo npm。',
|
|
108
|
-
'安装后若命令未生效,重开终端并执行:which codex / which claude。',
|
|
109
|
-
'公司网络受限时,可先切换镜像源快捷项(npmmirror / 腾讯云 / 自定义)。'
|
|
110
|
-
];
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
}
|
|
1
|
+
export function createDashboardComputed() {
|
|
2
|
+
return {
|
|
3
|
+
agentsDiffHasChanges() {
|
|
4
|
+
if (this.agentsDiffTruncated) {
|
|
5
|
+
return !!this.agentsDiffHasChangesValue;
|
|
6
|
+
}
|
|
7
|
+
const stats = this.agentsDiffStats || {};
|
|
8
|
+
const added = Number(stats.added || 0);
|
|
9
|
+
const removed = Number(stats.removed || 0);
|
|
10
|
+
return added > 0 || removed > 0;
|
|
11
|
+
},
|
|
12
|
+
claudeModelHasList() {
|
|
13
|
+
return this.claudeModelOptions.length > 0;
|
|
14
|
+
},
|
|
15
|
+
claudeModelOptions() {
|
|
16
|
+
const list = Array.isArray(this.claudeModels) ? [...this.claudeModels] : [];
|
|
17
|
+
const current = (this.currentClaudeModel || '').trim();
|
|
18
|
+
if (current && !list.includes(current)) {
|
|
19
|
+
list.unshift(current);
|
|
20
|
+
}
|
|
21
|
+
return list;
|
|
22
|
+
},
|
|
23
|
+
displayCurrentProvider() {
|
|
24
|
+
const switching = String(this.providerSwitchDisplayTarget || '').trim();
|
|
25
|
+
if (switching) return switching;
|
|
26
|
+
const current = String(this.currentProvider || '').trim();
|
|
27
|
+
return current;
|
|
28
|
+
},
|
|
29
|
+
displayProvidersList() {
|
|
30
|
+
const list = Array.isArray(this.providersList) ? this.providersList : [];
|
|
31
|
+
return list.filter((item) => String(item && item.name ? item.name : '').trim().toLowerCase() !== 'codexmate-proxy');
|
|
32
|
+
},
|
|
33
|
+
installTargetCards() {
|
|
34
|
+
const targets = Array.isArray(this.installStatusTargets) ? this.installStatusTargets : [];
|
|
35
|
+
const action = this.normalizeInstallAction(this.installCommandAction);
|
|
36
|
+
return targets.map((target) => {
|
|
37
|
+
const id = target && typeof target.id === 'string' ? target.id : '';
|
|
38
|
+
return {
|
|
39
|
+
...target,
|
|
40
|
+
command: this.getInstallCommand(id, action)
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
installRegistryPreview() {
|
|
45
|
+
return this.resolveInstallRegistryUrl(this.installRegistryPreset, this.installRegistryCustom);
|
|
46
|
+
},
|
|
47
|
+
inspectorBusyStatus() {
|
|
48
|
+
const tasks = [];
|
|
49
|
+
if (this.loading) tasks.push('初始化');
|
|
50
|
+
if (this.sessionsLoading) tasks.push('会话加载');
|
|
51
|
+
if (this.codexModelsLoading || this.claudeModelsLoading) tasks.push('模型加载');
|
|
52
|
+
if (this.codexApplying || this.configTemplateApplying || this.openclawApplying) tasks.push('配置应用');
|
|
53
|
+
if (this.agentsSaving) tasks.push('AGENTS 保存');
|
|
54
|
+
if (this.skillsLoading || this.skillsDeleting || this.skillsScanningImports || this.skillsImporting || this.skillsZipImporting || this.skillsExporting) tasks.push('Skills 管理');
|
|
55
|
+
if (this.taskOrchestration && (this.taskOrchestration.loading || this.taskOrchestration.planning || this.taskOrchestration.running || this.taskOrchestration.queueAdding || this.taskOrchestration.queueStarting || this.taskOrchestration.retrying || this.taskOrchestration.selectedRunLoading)) {
|
|
56
|
+
tasks.push('任务编排');
|
|
57
|
+
}
|
|
58
|
+
return tasks.length ? tasks.join(' / ') : '空闲';
|
|
59
|
+
},
|
|
60
|
+
inspectorMessageSummary() {
|
|
61
|
+
const value = typeof this.message === 'string' ? this.message.trim() : '';
|
|
62
|
+
return value || '暂无提示';
|
|
63
|
+
},
|
|
64
|
+
inspectorSessionSourceLabel() {
|
|
65
|
+
if (this.sessionFilterSource === 'codex') return 'Codex';
|
|
66
|
+
if (this.sessionFilterSource === 'claude') return 'Claude Code';
|
|
67
|
+
return '全部';
|
|
68
|
+
},
|
|
69
|
+
inspectorSessionPathLabel() {
|
|
70
|
+
const value = typeof this.sessionPathFilter === 'string' ? this.sessionPathFilter.trim() : '';
|
|
71
|
+
return value || '全部路径';
|
|
72
|
+
},
|
|
73
|
+
inspectorSessionQueryLabel() {
|
|
74
|
+
if (!this.isSessionQueryEnabled) return '当前来源不支持';
|
|
75
|
+
const value = typeof this.sessionQuery === 'string' ? this.sessionQuery.trim() : '';
|
|
76
|
+
return value || '未设置';
|
|
77
|
+
},
|
|
78
|
+
inspectorHealthStatus() {
|
|
79
|
+
if (this.initError) return '读取失败';
|
|
80
|
+
if (this.loading) return '初始化中';
|
|
81
|
+
return '正常';
|
|
82
|
+
},
|
|
83
|
+
inspectorHealthTone() {
|
|
84
|
+
if (this.initError) return 'error';
|
|
85
|
+
if (this.loading) return 'warn';
|
|
86
|
+
return 'ok';
|
|
87
|
+
},
|
|
88
|
+
inspectorModelLoadStatus() {
|
|
89
|
+
if (this.codexModelsLoading || this.claudeModelsLoading) {
|
|
90
|
+
return '加载中';
|
|
91
|
+
}
|
|
92
|
+
if (this.modelsSource === 'error' || this.claudeModelsSource === 'error') {
|
|
93
|
+
return '加载异常';
|
|
94
|
+
}
|
|
95
|
+
return '正常';
|
|
96
|
+
},
|
|
97
|
+
installTroubleshootingTips() {
|
|
98
|
+
const platform = this.resolveInstallPlatform();
|
|
99
|
+
if (platform === 'win32') {
|
|
100
|
+
return [
|
|
101
|
+
'PowerShell 报权限不足(EACCES/EPERM)时,请以管理员身份执行安装命令。',
|
|
102
|
+
'安装后若仍提示找不到命令,重开终端并执行:where codex / where claude。',
|
|
103
|
+
'公司网络受限时,可先切换镜像源快捷项(npmmirror / 腾讯云 / 自定义)。'
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
return [
|
|
107
|
+
'出现 EACCES 权限错误时,优先修复 Node 全局目录权限,不建议直接 sudo npm。',
|
|
108
|
+
'安装后若命令未生效,重开终端并执行:which codex / which claude。',
|
|
109
|
+
'公司网络受限时,可先切换镜像源快捷项(npmmirror / 腾讯云 / 自定义)。'
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { createDashboardComputed } from './app.computed.dashboard.mjs';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
...
|
|
10
|
-
...
|
|
11
|
-
...
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import { createDashboardComputed } from './app.computed.dashboard.mjs';
|
|
2
|
+
import { createMainTabsComputed } from './app.computed.main-tabs.mjs';
|
|
3
|
+
import { createSessionComputed } from './app.computed.session.mjs';
|
|
4
|
+
import { createConfigModeComputed } from './config-mode.computed.mjs';
|
|
5
|
+
import { createSkillsComputed } from './skills.computed.mjs';
|
|
6
|
+
|
|
7
|
+
export function createAppComputed() {
|
|
8
|
+
return {
|
|
9
|
+
...createSessionComputed(),
|
|
10
|
+
...createDashboardComputed(),
|
|
11
|
+
...createMainTabsComputed(),
|
|
12
|
+
...createSkillsComputed(),
|
|
13
|
+
...createConfigModeComputed()
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
function normalizeTaskDraftLines(text) {
|
|
2
|
+
return String(text || '')
|
|
3
|
+
.split(/\r?\n/g)
|
|
4
|
+
.map((item) => item.trim())
|
|
5
|
+
.filter(Boolean);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function readTaskOrchestrationDraftMetrics(taskOrchestration) {
|
|
9
|
+
const state = taskOrchestration && typeof taskOrchestration === 'object' ? taskOrchestration : {};
|
|
10
|
+
const target = String(state.target || '').trim();
|
|
11
|
+
const notes = String(state.notes || '').trim();
|
|
12
|
+
const title = String(state.title || '').trim();
|
|
13
|
+
const workflowIds = normalizeTaskDraftLines(state.workflowIdsText);
|
|
14
|
+
const followUps = normalizeTaskDraftLines(state.followUpsText);
|
|
15
|
+
const engine = String(state.selectedEngine || 'codex').trim().toLowerCase() === 'workflow' ? 'workflow' : 'codex';
|
|
16
|
+
const plan = state.plan && typeof state.plan === 'object' ? state.plan : null;
|
|
17
|
+
const planNodes = Array.isArray(plan && plan.nodes) ? plan.nodes : [];
|
|
18
|
+
const planIssues = Array.isArray(state.planIssues) ? state.planIssues : [];
|
|
19
|
+
const planWarnings = Array.isArray(state.planWarnings) ? state.planWarnings : [];
|
|
20
|
+
return {
|
|
21
|
+
engine,
|
|
22
|
+
title,
|
|
23
|
+
target,
|
|
24
|
+
notes,
|
|
25
|
+
workflowIds,
|
|
26
|
+
followUps,
|
|
27
|
+
hasTarget: target.length > 0,
|
|
28
|
+
hasNotes: notes.length > 0,
|
|
29
|
+
hasTitle: title.length > 0,
|
|
30
|
+
hasPlan: !!plan,
|
|
31
|
+
planNodes,
|
|
32
|
+
planIssues,
|
|
33
|
+
planWarnings,
|
|
34
|
+
workflowCount: workflowIds.length,
|
|
35
|
+
followUpCount: followUps.length,
|
|
36
|
+
planNodeCount: planNodes.length,
|
|
37
|
+
allowWrite: state.allowWrite === true,
|
|
38
|
+
dryRun: state.dryRun === true
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function createTaskDraftChecklist(metrics) {
|
|
43
|
+
const workflowReady = metrics.engine !== 'workflow' || metrics.workflowCount > 0;
|
|
44
|
+
const scopeReady = metrics.hasNotes || !metrics.allowWrite;
|
|
45
|
+
const previewReady = metrics.hasPlan && metrics.planIssues.length === 0;
|
|
46
|
+
return [
|
|
47
|
+
{
|
|
48
|
+
key: 'target',
|
|
49
|
+
label: '目标',
|
|
50
|
+
done: metrics.hasTarget,
|
|
51
|
+
detail: metrics.hasTarget ? '已写目标' : '还没写目标'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: 'engine',
|
|
55
|
+
label: metrics.engine === 'workflow' ? 'Workflow' : '执行策略',
|
|
56
|
+
done: workflowReady,
|
|
57
|
+
detail: metrics.engine === 'workflow'
|
|
58
|
+
? (metrics.workflowCount > 0 ? `已选 ${metrics.workflowCount} 个 Workflow` : '还没选 Workflow ID')
|
|
59
|
+
: '使用 Codex 规划节点'
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
key: 'scope',
|
|
63
|
+
label: '边界',
|
|
64
|
+
done: scopeReady,
|
|
65
|
+
detail: metrics.hasNotes
|
|
66
|
+
? '已补充说明'
|
|
67
|
+
: (metrics.allowWrite ? '建议补说明后再写入' : '当前是只读,可直接试')
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: 'preview',
|
|
71
|
+
label: '预览',
|
|
72
|
+
done: previewReady,
|
|
73
|
+
detail: !metrics.hasPlan
|
|
74
|
+
? '还没生成计划'
|
|
75
|
+
: (metrics.planIssues.length > 0 ? `有 ${metrics.planIssues.length} 个阻塞项` : `计划可用,${metrics.planNodeCount} 个节点`)
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function createTaskDraftReadiness(metrics) {
|
|
81
|
+
if (!metrics.hasTarget) {
|
|
82
|
+
return {
|
|
83
|
+
tone: 'neutral',
|
|
84
|
+
title: '先写目标',
|
|
85
|
+
summary: '先把想完成的结果写清楚,再让编排器拆节点。'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (metrics.engine === 'workflow' && metrics.workflowCount === 0) {
|
|
89
|
+
return {
|
|
90
|
+
tone: 'warn',
|
|
91
|
+
title: '缺少 Workflow',
|
|
92
|
+
summary: '你已经选了 Workflow 模式,但还没指定可复用流程。'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (!metrics.hasPlan) {
|
|
96
|
+
return {
|
|
97
|
+
tone: 'warn',
|
|
98
|
+
title: '建议先预览',
|
|
99
|
+
summary: '草稿已成形,先生成一次计划,确认节点和依赖再执行。'
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (metrics.planIssues.length > 0) {
|
|
103
|
+
return {
|
|
104
|
+
tone: 'error',
|
|
105
|
+
title: '预览有阻塞',
|
|
106
|
+
summary: `当前计划里还有 ${metrics.planIssues.length} 个阻塞项,先处理它们。`
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (metrics.planWarnings.length > 0) {
|
|
110
|
+
return {
|
|
111
|
+
tone: 'warn',
|
|
112
|
+
title: '可以执行,但有提醒',
|
|
113
|
+
summary: `计划已生成,但还有 ${metrics.planWarnings.length} 条提醒值得先看一眼。`
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (metrics.dryRun) {
|
|
117
|
+
return {
|
|
118
|
+
tone: 'success',
|
|
119
|
+
title: '适合先预演',
|
|
120
|
+
summary: '现在可以安全地跑一次仅预演,先看结果再决定是否真实执行。'
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
tone: 'success',
|
|
125
|
+
title: '可以执行',
|
|
126
|
+
summary: metrics.followUpCount > 0
|
|
127
|
+
? `主目标和收尾动作都已具备,可以直接执行或入队。`
|
|
128
|
+
: '主目标已经够清楚了,可以直接执行或入队。'
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function createMainTabsComputed() {
|
|
133
|
+
return {
|
|
134
|
+
mainTabKicker() {
|
|
135
|
+
if (this.mainTab === 'config') return 'Configuration';
|
|
136
|
+
if (this.mainTab === 'sessions') return 'Sessions';
|
|
137
|
+
if (this.mainTab === 'usage') return 'Usage';
|
|
138
|
+
if (this.mainTab === 'orchestration') return 'Tasks';
|
|
139
|
+
if (this.mainTab === 'market') return 'Skills';
|
|
140
|
+
if (this.mainTab === 'docs') return 'Docs';
|
|
141
|
+
return 'Settings';
|
|
142
|
+
},
|
|
143
|
+
mainTabTitle() {
|
|
144
|
+
if (this.mainTab === 'config') return '本地配置控制台';
|
|
145
|
+
if (this.mainTab === 'sessions') return '会话与导出';
|
|
146
|
+
if (this.mainTab === 'usage') return '本地用量与趋势';
|
|
147
|
+
if (this.mainTab === 'orchestration') return '任务编排';
|
|
148
|
+
if (this.mainTab === 'market') return 'Skills 安装与同步';
|
|
149
|
+
if (this.mainTab === 'docs') return 'CLI 安装与文档';
|
|
150
|
+
return '系统与数据设置';
|
|
151
|
+
},
|
|
152
|
+
mainTabSubtitle() {
|
|
153
|
+
if (this.mainTab === 'config') return '管理本地配置与模型。';
|
|
154
|
+
if (this.mainTab === 'sessions') return '浏览与导出会话。';
|
|
155
|
+
if (this.mainTab === 'usage') return '查看近 7 / 30 天用量。';
|
|
156
|
+
if (this.mainTab === 'orchestration') return '规划、排队、执行与回看本地任务。';
|
|
157
|
+
if (this.mainTab === 'market') return '管理本地 Skills。';
|
|
158
|
+
if (this.mainTab === 'docs') return '查看 CLI 安装命令与排障。';
|
|
159
|
+
return '管理下载、目录与回收站。';
|
|
160
|
+
},
|
|
161
|
+
taskOrchestrationSelectedRun() {
|
|
162
|
+
return this.taskOrchestration && this.taskOrchestration.selectedRunDetail
|
|
163
|
+
? this.taskOrchestration.selectedRunDetail
|
|
164
|
+
: null;
|
|
165
|
+
},
|
|
166
|
+
taskOrchestrationSelectedRunNodes() {
|
|
167
|
+
const detail = this.taskOrchestrationSelectedRun;
|
|
168
|
+
const run = detail && detail.run && typeof detail.run === 'object' ? detail.run : {};
|
|
169
|
+
if (detail && Array.isArray(detail.nodes)) return detail.nodes;
|
|
170
|
+
return Array.isArray(run.nodes) ? run.nodes : [];
|
|
171
|
+
},
|
|
172
|
+
taskOrchestrationQueueStats() {
|
|
173
|
+
const queue = this.taskOrchestration && Array.isArray(this.taskOrchestration.queue)
|
|
174
|
+
? this.taskOrchestration.queue
|
|
175
|
+
: [];
|
|
176
|
+
const stats = { queued: 0, running: 0, failed: 0 };
|
|
177
|
+
for (const item of queue) {
|
|
178
|
+
const status = String(item && item.status || '').trim().toLowerCase();
|
|
179
|
+
if (status === 'queued') stats.queued += 1;
|
|
180
|
+
else if (status === 'running') stats.running += 1;
|
|
181
|
+
else if (status === 'failed') stats.failed += 1;
|
|
182
|
+
}
|
|
183
|
+
return stats;
|
|
184
|
+
},
|
|
185
|
+
taskOrchestrationDraftMetrics() {
|
|
186
|
+
return readTaskOrchestrationDraftMetrics(this.taskOrchestration);
|
|
187
|
+
},
|
|
188
|
+
taskOrchestrationDraftChecklist() {
|
|
189
|
+
return createTaskDraftChecklist(this.taskOrchestrationDraftMetrics);
|
|
190
|
+
},
|
|
191
|
+
taskOrchestrationDraftReadiness() {
|
|
192
|
+
return createTaskDraftReadiness(this.taskOrchestrationDraftMetrics);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|