codexmate 0.0.38 → 0.0.40
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/builtin-proxy.js +626 -207
- package/cli/config-bootstrap.js +6 -1
- package/cli/openai-bridge.js +541 -210
- package/cli.js +189 -4
- package/package.json +1 -1
- package/plugins/prompt-templates/computed.mjs +61 -3
- package/plugins/prompt-templates/manifest.mjs +3 -0
- package/web-ui/app.js +14 -3
- package/web-ui/modules/app.computed.main-tabs.mjs +39 -30
- package/web-ui/modules/app.methods.claude-config.mjs +111 -9
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.openclaw-editing.mjs +48 -0
- package/web-ui/modules/app.methods.openclaw-persist.mjs +13 -7
- package/web-ui/modules/app.methods.providers.mjs +36 -10
- package/web-ui/modules/app.methods.runtime.mjs +76 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +7 -0
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +87 -0
- package/web-ui/modules/config-mode.computed.mjs +3 -3
- package/web-ui/modules/i18n/locales/en.mjs +1140 -0
- package/web-ui/modules/i18n/locales/ja.mjs +1130 -0
- package/web-ui/modules/i18n/locales/vi.mjs +239 -0
- package/web-ui/modules/i18n/locales/zh.mjs +1143 -0
- package/web-ui/modules/i18n.dict.mjs +9 -3195
- package/web-ui/modules/i18n.mjs +65 -16
- package/web-ui/partials/index/layout-header.html +16 -46
- package/web-ui/partials/index/modal-openclaw-config.html +135 -71
- package/web-ui/partials/index/modal-webhook.html +8 -8
- package/web-ui/partials/index/modals-basic.html +56 -16
- package/web-ui/partials/index/panel-config-claude.html +51 -21
- package/web-ui/partials/index/panel-config-codex.html +34 -5
- package/web-ui/partials/index/panel-config-openclaw.html +70 -64
- package/web-ui/partials/index/panel-dashboard.html +62 -77
- package/web-ui/partials/index/panel-settings.html +28 -7
- package/web-ui/partials/index/panel-trash.html +14 -14
- package/web-ui/res/web-ui-render.precompiled.js +1783 -1386
- package/web-ui/styles/controls-forms.css +99 -0
- package/web-ui/styles/dashboard.css +46 -14
- package/web-ui/styles/layout-shell.css +45 -0
- package/web-ui/styles/navigation-panels.css +3 -3
- package/web-ui/styles/openclaw-structured.css +383 -33
- package/web-ui/styles/responsive.css +68 -0
- package/web-ui/styles/sessions-usage.css +105 -9
- package/web-ui/styles/settings-panel.css +4 -0
|
@@ -43,93 +43,99 @@ function readTaskOrchestrationDraftMetrics(taskOrchestration) {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
function
|
|
46
|
+
function translateTaskText(t, key, fallback, params = null) {
|
|
47
|
+
if (typeof t !== 'function') return fallback;
|
|
48
|
+
const translated = t(key, params);
|
|
49
|
+
return translated === key ? fallback : translated;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createTaskDraftChecklist(metrics, t = null) {
|
|
47
53
|
const workflowReady = metrics.engine !== 'workflow' || metrics.workflowCount > 0;
|
|
48
54
|
const scopeReady = metrics.hasNotes || !metrics.allowWrite;
|
|
49
55
|
const previewReady = metrics.hasPlan && metrics.planIssues.length === 0;
|
|
50
56
|
return [
|
|
51
57
|
{
|
|
52
58
|
key: 'target',
|
|
53
|
-
label: '目标',
|
|
59
|
+
label: translateTaskText(t, 'orchestration.readiness.target.label', '目标'),
|
|
54
60
|
done: metrics.hasTarget,
|
|
55
|
-
detail: metrics.hasTarget ? '已写目标' : '还没写目标'
|
|
61
|
+
detail: metrics.hasTarget ? translateTaskText(t, 'orchestration.readiness.target.done', '已写目标') : translateTaskText(t, 'orchestration.readiness.target.missing', '还没写目标')
|
|
56
62
|
},
|
|
57
63
|
{
|
|
58
64
|
key: 'engine',
|
|
59
|
-
label: metrics.engine === 'workflow' ? 'Workflow' : '执行策略',
|
|
65
|
+
label: metrics.engine === 'workflow' ? 'Workflow' : translateTaskText(t, 'orchestration.readiness.engine.label', '执行策略'),
|
|
60
66
|
done: workflowReady,
|
|
61
67
|
detail: metrics.engine === 'workflow'
|
|
62
|
-
? (metrics.workflowCount > 0 ? `已选 ${metrics.workflowCount} 个 Workflow
|
|
63
|
-
: '使用 Codex 规划节点'
|
|
68
|
+
? (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.codex', '使用 Codex 规划节点')
|
|
64
70
|
},
|
|
65
71
|
{
|
|
66
72
|
key: 'scope',
|
|
67
|
-
label: '边界',
|
|
73
|
+
label: translateTaskText(t, 'orchestration.readiness.scope.label', '边界'),
|
|
68
74
|
done: scopeReady,
|
|
69
75
|
detail: metrics.hasNotes
|
|
70
|
-
? '已补充说明'
|
|
71
|
-
: (metrics.allowWrite ? '建议补说明后再写入' : '当前是只读,可直接试')
|
|
76
|
+
? translateTaskText(t, 'orchestration.readiness.scope.done', '已补充说明')
|
|
77
|
+
: (metrics.allowWrite ? translateTaskText(t, 'orchestration.readiness.scope.writeHint', '建议补说明后再写入') : translateTaskText(t, 'orchestration.readiness.scope.readonlyHint', '当前是只读,可直接试'))
|
|
72
78
|
},
|
|
73
79
|
{
|
|
74
80
|
key: 'preview',
|
|
75
|
-
label: '预览',
|
|
81
|
+
label: translateTaskText(t, 'orchestration.readiness.preview.label', '预览'),
|
|
76
82
|
done: previewReady,
|
|
77
83
|
detail: !metrics.hasPlan
|
|
78
|
-
? '还没生成计划'
|
|
79
|
-
: (metrics.planIssues.length > 0 ? `有 ${metrics.planIssues.length}
|
|
84
|
+
? translateTaskText(t, 'orchestration.readiness.preview.missing', '还没生成计划')
|
|
85
|
+
: (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 }))
|
|
80
86
|
}
|
|
81
87
|
];
|
|
82
88
|
}
|
|
83
89
|
|
|
84
|
-
function createTaskDraftReadiness(metrics) {
|
|
90
|
+
function createTaskDraftReadiness(metrics, t = null) {
|
|
85
91
|
if (!metrics.hasTarget) {
|
|
86
92
|
return {
|
|
87
93
|
tone: 'neutral',
|
|
88
|
-
title: '先写目标',
|
|
89
|
-
summary: '先把想完成的结果写清楚,再让编排器拆节点。'
|
|
94
|
+
title: translateTaskText(t, 'orchestration.readiness.empty.title', '先写目标'),
|
|
95
|
+
summary: translateTaskText(t, 'orchestration.readiness.empty.summary', '先把想完成的结果写清楚,再让编排器拆节点。')
|
|
90
96
|
};
|
|
91
97
|
}
|
|
92
98
|
if (metrics.engine === 'workflow' && metrics.workflowCount === 0) {
|
|
93
99
|
return {
|
|
94
100
|
tone: 'warn',
|
|
95
|
-
title: '缺少 Workflow',
|
|
96
|
-
summary: '你已经选了 Workflow 模式,但还没指定可复用流程。'
|
|
101
|
+
title: translateTaskText(t, 'orchestration.readiness.workflow.title', '缺少 Workflow'),
|
|
102
|
+
summary: translateTaskText(t, 'orchestration.readiness.workflow.summary', '你已经选了 Workflow 模式,但还没指定可复用流程。')
|
|
97
103
|
};
|
|
98
104
|
}
|
|
99
105
|
if (!metrics.hasPlan) {
|
|
100
106
|
return {
|
|
101
107
|
tone: 'warn',
|
|
102
|
-
title: '建议先预览',
|
|
103
|
-
summary: '草稿已成形,先生成一次计划,确认节点和依赖再执行。'
|
|
108
|
+
title: translateTaskText(t, 'orchestration.readiness.preview.title', '建议先预览'),
|
|
109
|
+
summary: translateTaskText(t, 'orchestration.readiness.preview.summary', '草稿已成形,先生成一次计划,确认节点和依赖再执行。')
|
|
104
110
|
};
|
|
105
111
|
}
|
|
106
112
|
if (metrics.planIssues.length > 0) {
|
|
107
113
|
return {
|
|
108
114
|
tone: 'error',
|
|
109
|
-
title: '预览有阻塞',
|
|
110
|
-
summary: `当前计划里还有 ${metrics.planIssues.length}
|
|
115
|
+
title: translateTaskText(t, 'orchestration.readiness.blocked.title', '预览有阻塞'),
|
|
116
|
+
summary: translateTaskText(t, 'orchestration.readiness.blocked.summary', `当前计划里还有 ${metrics.planIssues.length} 个阻塞项,先处理它们。`, { count: metrics.planIssues.length })
|
|
111
117
|
};
|
|
112
118
|
}
|
|
113
119
|
if (metrics.planWarnings.length > 0) {
|
|
114
120
|
return {
|
|
115
121
|
tone: 'warn',
|
|
116
|
-
title: '可以执行,但有提醒',
|
|
117
|
-
summary: `计划已生成,但还有 ${metrics.planWarnings.length}
|
|
122
|
+
title: translateTaskText(t, 'orchestration.readiness.warn.title', '可以执行,但有提醒'),
|
|
123
|
+
summary: translateTaskText(t, 'orchestration.readiness.warn.summary', `计划已生成,但还有 ${metrics.planWarnings.length} 条提醒值得先看一眼。`, { count: metrics.planWarnings.length })
|
|
118
124
|
};
|
|
119
125
|
}
|
|
120
126
|
if (metrics.dryRun) {
|
|
121
127
|
return {
|
|
122
128
|
tone: 'success',
|
|
123
|
-
title: '适合先预演',
|
|
124
|
-
summary: '现在可以安全地跑一次仅预演,先看结果再决定是否真实执行。'
|
|
129
|
+
title: translateTaskText(t, 'orchestration.readiness.dryRun.title', '适合先预演'),
|
|
130
|
+
summary: translateTaskText(t, 'orchestration.readiness.dryRun.summary', '现在可以安全地跑一次仅预演,先看结果再决定是否真实执行。')
|
|
125
131
|
};
|
|
126
132
|
}
|
|
127
133
|
return {
|
|
128
134
|
tone: 'success',
|
|
129
|
-
title: '可以执行',
|
|
135
|
+
title: translateTaskText(t, 'orchestration.readiness.ready.title', '可以执行'),
|
|
130
136
|
summary: metrics.followUpCount > 0
|
|
131
|
-
? `主目标和收尾动作都已具备,可以直接执行或入队。`
|
|
132
|
-
: '主目标已经够清楚了,可以直接执行或入队。'
|
|
137
|
+
? translateTaskText(t, 'orchestration.readiness.ready.withFollowUps', `主目标和收尾动作都已具备,可以直接执行或入队。`)
|
|
138
|
+
: translateTaskText(t, 'orchestration.readiness.ready.summary', '主目标已经够清楚了,可以直接执行或入队。')
|
|
133
139
|
};
|
|
134
140
|
}
|
|
135
141
|
|
|
@@ -144,6 +150,7 @@ export function createMainTabsComputed() {
|
|
|
144
150
|
if (this.mainTab === 'market') return this.t('kicker.market');
|
|
145
151
|
if (this.mainTab === 'plugins') return this.t('kicker.plugins');
|
|
146
152
|
if (this.mainTab === 'docs') return this.t('kicker.docs');
|
|
153
|
+
if (this.mainTab === 'trash') return this.t('kicker.trash');
|
|
147
154
|
return this.t('kicker.settings');
|
|
148
155
|
},
|
|
149
156
|
mainTabTitle() {
|
|
@@ -155,6 +162,7 @@ export function createMainTabsComputed() {
|
|
|
155
162
|
if (this.mainTab === 'market') return this.t('title.market');
|
|
156
163
|
if (this.mainTab === 'plugins') return this.t('title.plugins');
|
|
157
164
|
if (this.mainTab === 'docs') return this.t('title.docs');
|
|
165
|
+
if (this.mainTab === 'trash') return this.t('settings.trash.title');
|
|
158
166
|
return this.t('title.settings');
|
|
159
167
|
},
|
|
160
168
|
mainTabSubtitle() {
|
|
@@ -166,6 +174,7 @@ export function createMainTabsComputed() {
|
|
|
166
174
|
if (this.mainTab === 'market') return this.t('subtitle.market');
|
|
167
175
|
if (this.mainTab === 'plugins') return this.t('subtitle.plugins');
|
|
168
176
|
if (this.mainTab === 'docs') return this.t('subtitle.docs');
|
|
177
|
+
if (this.mainTab === 'trash') return this.t('settings.trash.meta');
|
|
169
178
|
return this.t('subtitle.settings');
|
|
170
179
|
},
|
|
171
180
|
taskOrchestrationSelectedRun() {
|
|
@@ -196,10 +205,10 @@ export function createMainTabsComputed() {
|
|
|
196
205
|
return readTaskOrchestrationDraftMetrics(this.taskOrchestration);
|
|
197
206
|
},
|
|
198
207
|
taskOrchestrationDraftChecklist() {
|
|
199
|
-
return createTaskDraftChecklist(this.taskOrchestrationDraftMetrics);
|
|
208
|
+
return createTaskDraftChecklist(this.taskOrchestrationDraftMetrics, this.t && this.t.bind(this));
|
|
200
209
|
},
|
|
201
210
|
taskOrchestrationDraftReadiness() {
|
|
202
|
-
return createTaskDraftReadiness(this.taskOrchestrationDraftMetrics);
|
|
211
|
+
return createTaskDraftReadiness(this.taskOrchestrationDraftMetrics, this.t && this.t.bind(this));
|
|
203
212
|
}
|
|
204
213
|
};
|
|
205
214
|
}
|
|
@@ -1,3 +1,67 @@
|
|
|
1
|
+
function normalizeClaudeText(value) {
|
|
2
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function normalizeClaudeBaseUrl(value) {
|
|
6
|
+
return normalizeClaudeText(value).replace(/\/+$/g, '');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isValidClaudeHttpUrl(value) {
|
|
10
|
+
if (!value) return false;
|
|
11
|
+
try {
|
|
12
|
+
const parsed = new URL(value);
|
|
13
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
14
|
+
} catch (_) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getClaudeConfigValidationForContext(vm, mode = 'add') {
|
|
20
|
+
const draft = mode === 'edit' ? vm.editingConfig : vm.newClaudeConfig;
|
|
21
|
+
const name = normalizeClaudeText(draft && draft.name);
|
|
22
|
+
const apiKey = normalizeClaudeText(draft && draft.apiKey);
|
|
23
|
+
const externalCredentialType = normalizeClaudeText(draft && draft.externalCredentialType);
|
|
24
|
+
const baseUrl = normalizeClaudeBaseUrl(draft && draft.baseUrl);
|
|
25
|
+
const model = normalizeClaudeText(draft && draft.model);
|
|
26
|
+
const errors = {
|
|
27
|
+
name: '',
|
|
28
|
+
apiKey: '',
|
|
29
|
+
baseUrl: '',
|
|
30
|
+
model: ''
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (!name) {
|
|
34
|
+
errors.name = '配置名称不能为空';
|
|
35
|
+
} else if (mode === 'add' && vm.claudeConfigs && vm.claudeConfigs[name]) {
|
|
36
|
+
errors.name = '名称已存在';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!apiKey && !externalCredentialType) {
|
|
40
|
+
errors.apiKey = 'API Key 必填';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!baseUrl) {
|
|
44
|
+
errors.baseUrl = 'Base URL 必填';
|
|
45
|
+
} else if (!isValidClaudeHttpUrl(baseUrl)) {
|
|
46
|
+
errors.baseUrl = 'Base URL 仅支持 http/https';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!model) {
|
|
50
|
+
errors.model = '模型名称必填';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
mode,
|
|
55
|
+
name,
|
|
56
|
+
apiKey,
|
|
57
|
+
externalCredentialType,
|
|
58
|
+
baseUrl,
|
|
59
|
+
model,
|
|
60
|
+
errors,
|
|
61
|
+
ok: !errors.name && !errors.apiKey && !errors.baseUrl && !errors.model
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
1
65
|
export function createClaudeConfigMethods(options = {}) {
|
|
2
66
|
const { api } = options;
|
|
3
67
|
|
|
@@ -54,14 +118,31 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
54
118
|
baseUrl: config.baseUrl || '',
|
|
55
119
|
model: config.model || ''
|
|
56
120
|
};
|
|
121
|
+
this.showAddClaudeConfigKey = false;
|
|
57
122
|
this.showClaudeConfigModal = true;
|
|
58
123
|
},
|
|
59
124
|
|
|
125
|
+
getClaudeConfigValidation(mode = 'add') {
|
|
126
|
+
return getClaudeConfigValidationForContext(this, mode);
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
claudeConfigFieldError(mode, fieldName) {
|
|
130
|
+
const validation = getClaudeConfigValidationForContext(this, mode);
|
|
131
|
+
return validation && validation.errors && typeof validation.errors[fieldName] === 'string'
|
|
132
|
+
? validation.errors[fieldName]
|
|
133
|
+
: '';
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
canSubmitClaudeConfig(mode = 'add') {
|
|
137
|
+
return getClaudeConfigValidationForContext(this, mode).ok;
|
|
138
|
+
},
|
|
139
|
+
|
|
60
140
|
openEditConfigModal(name) {
|
|
61
141
|
const config = this.claudeConfigs[name];
|
|
62
142
|
this.editingConfig = {
|
|
63
143
|
name: name,
|
|
64
144
|
apiKey: config.apiKey || '',
|
|
145
|
+
externalCredentialType: config.externalCredentialType || '',
|
|
65
146
|
baseUrl: config.baseUrl || '',
|
|
66
147
|
model: config.model || ''
|
|
67
148
|
};
|
|
@@ -70,7 +151,14 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
70
151
|
},
|
|
71
152
|
|
|
72
153
|
updateConfig() {
|
|
73
|
-
const
|
|
154
|
+
const validation = getClaudeConfigValidationForContext(this, 'edit');
|
|
155
|
+
if (!validation.ok) {
|
|
156
|
+
return this.showMessage(validation.errors.name || validation.errors.apiKey || validation.errors.baseUrl || validation.errors.model || '请检查 Claude 配置', 'error');
|
|
157
|
+
}
|
|
158
|
+
const name = validation.name;
|
|
159
|
+
this.editingConfig.apiKey = validation.apiKey;
|
|
160
|
+
this.editingConfig.baseUrl = validation.baseUrl;
|
|
161
|
+
this.editingConfig.model = validation.model;
|
|
74
162
|
this.claudeConfigs[name] = this.mergeClaudeConfig(this.claudeConfigs[name], this.editingConfig);
|
|
75
163
|
this.saveClaudeConfigs();
|
|
76
164
|
this.showMessage('操作成功', 'success');
|
|
@@ -83,7 +171,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
83
171
|
closeEditConfigModal() {
|
|
84
172
|
this.showEditConfigModal = false;
|
|
85
173
|
this.showEditClaudeConfigKey = false;
|
|
86
|
-
this.editingConfig = { name: '', apiKey: '', baseUrl: '', model: '' };
|
|
174
|
+
this.editingConfig = { name: '', apiKey: '', externalCredentialType: '', baseUrl: '', model: '' };
|
|
87
175
|
},
|
|
88
176
|
|
|
89
177
|
toggleEditClaudeConfigKey() {
|
|
@@ -91,7 +179,14 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
91
179
|
},
|
|
92
180
|
|
|
93
181
|
async saveAndApplyConfig() {
|
|
94
|
-
const
|
|
182
|
+
const validation = getClaudeConfigValidationForContext(this, 'edit');
|
|
183
|
+
if (!validation.ok) {
|
|
184
|
+
return this.showMessage(validation.errors.name || validation.errors.apiKey || validation.errors.baseUrl || validation.errors.model || '请检查 Claude 配置', 'error');
|
|
185
|
+
}
|
|
186
|
+
const name = validation.name;
|
|
187
|
+
this.editingConfig.apiKey = validation.apiKey;
|
|
188
|
+
this.editingConfig.baseUrl = validation.baseUrl;
|
|
189
|
+
this.editingConfig.model = validation.model;
|
|
95
190
|
this.claudeConfigs[name] = this.mergeClaudeConfig(this.claudeConfigs[name], this.editingConfig);
|
|
96
191
|
this.saveClaudeConfigs();
|
|
97
192
|
|
|
@@ -125,13 +220,15 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
125
220
|
},
|
|
126
221
|
|
|
127
222
|
addClaudeConfig() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const name = this.newClaudeConfig.name.trim();
|
|
132
|
-
if (this.claudeConfigs[name]) {
|
|
133
|
-
return this.showMessage('名称已存在', 'error');
|
|
223
|
+
const validation = getClaudeConfigValidationForContext(this, 'add');
|
|
224
|
+
if (!validation.ok) {
|
|
225
|
+
return this.showMessage(validation.errors.name || validation.errors.apiKey || validation.errors.baseUrl || validation.errors.model || '请检查 Claude 配置', 'error');
|
|
134
226
|
}
|
|
227
|
+
this.newClaudeConfig.name = validation.name;
|
|
228
|
+
this.newClaudeConfig.apiKey = validation.apiKey;
|
|
229
|
+
this.newClaudeConfig.baseUrl = validation.baseUrl;
|
|
230
|
+
this.newClaudeConfig.model = validation.model;
|
|
231
|
+
const name = validation.name;
|
|
135
232
|
const duplicateName = this.findDuplicateClaudeConfigName(this.newClaudeConfig);
|
|
136
233
|
if (duplicateName) {
|
|
137
234
|
return this.showMessage('配置已存在', 'info');
|
|
@@ -199,6 +296,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
199
296
|
|
|
200
297
|
closeClaudeConfigModal() {
|
|
201
298
|
this.showClaudeConfigModal = false;
|
|
299
|
+
this.showAddClaudeConfigKey = false;
|
|
202
300
|
this.newClaudeConfig = {
|
|
203
301
|
name: '',
|
|
204
302
|
apiKey: '',
|
|
@@ -207,6 +305,10 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
207
305
|
};
|
|
208
306
|
},
|
|
209
307
|
|
|
308
|
+
toggleAddClaudeConfigKey() {
|
|
309
|
+
this.showAddClaudeConfigKey = !this.showAddClaudeConfigKey;
|
|
310
|
+
},
|
|
311
|
+
|
|
210
312
|
async loadClaudeLocalBridgeStatus() {
|
|
211
313
|
try {
|
|
212
314
|
const res = await api('claude-local-bridge-status');
|
|
@@ -20,6 +20,7 @@ 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 { createToolConfigPermissionMethods } from './app.methods.tool-config-permissions.mjs';
|
|
23
24
|
import { createTaskOrchestrationMethods } from './app.methods.task-orchestration.mjs';
|
|
24
25
|
import { createSessionActionMethods } from './app.methods.session-actions.mjs';
|
|
25
26
|
import { createSessionBrowserMethods } from './app.methods.session-browser.mjs';
|
|
@@ -81,6 +82,7 @@ export function createAppMethods() {
|
|
|
81
82
|
...createAgentsMethods({ api, apiWithMeta }),
|
|
82
83
|
...createProvidersMethods({ api }),
|
|
83
84
|
...createClaudeConfigMethods({ api }),
|
|
85
|
+
...createToolConfigPermissionMethods({ api }),
|
|
84
86
|
...createOpenclawCoreMethods(),
|
|
85
87
|
...createOpenclawEditingMethods(),
|
|
86
88
|
...createOpenclawPersistMethods({
|
|
@@ -367,6 +367,54 @@ export function createOpenclawEditingMethods() {
|
|
|
367
367
|
this.showMessage('保存本地 OpenClaw 配置失败', 'error');
|
|
368
368
|
return false;
|
|
369
369
|
}
|
|
370
|
+
},
|
|
371
|
+
|
|
372
|
+
// Accordion stepper methods
|
|
373
|
+
toggleAccordionStep(step) {
|
|
374
|
+
if (this.openclawAccordionStep === step) {
|
|
375
|
+
// Don't allow collapsing the current step
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
this.openclawAccordionStep = step;
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
nextAccordionStep() {
|
|
382
|
+
if (this.openclawAccordionStep < 3) {
|
|
383
|
+
this.openclawAccordionStep++;
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
prevAccordionStep() {
|
|
388
|
+
if (this.openclawAccordionStep > 1) {
|
|
389
|
+
this.openclawAccordionStep--;
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
finishAccordionStep() {
|
|
394
|
+
this.openclawAccordionStep = 4; // Mark as complete
|
|
395
|
+
this.applyOpenclawQuickToText();
|
|
396
|
+
},
|
|
397
|
+
|
|
398
|
+
validateProviderName() {
|
|
399
|
+
const name = (this.openclawQuick.providerName || '').trim();
|
|
400
|
+
if (!name) {
|
|
401
|
+
this.openclawValidation.providerName = { valid: false, message: '必填' };
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (name.includes('/')) {
|
|
405
|
+
this.openclawValidation.providerName = { valid: false, message: '不能包含 "/"' };
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
this.openclawValidation.providerName = { valid: true, message: '' };
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
validateModelId() {
|
|
412
|
+
const id = (this.openclawQuick.modelId || '').trim();
|
|
413
|
+
if (!id) {
|
|
414
|
+
this.openclawValidation.modelId = { valid: false, message: '必填' };
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
this.openclawValidation.modelId = { valid: true, message: '' };
|
|
370
418
|
}
|
|
371
419
|
};
|
|
372
420
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const DEFAULT_OPENCLAW_CONFIG_NAME = '默认配置';
|
|
1
|
+
export const DEFAULT_OPENCLAW_CONFIG_NAME = '默认配置';
|
|
2
2
|
|
|
3
3
|
function buildNormalizedOpenclawConfigs(configs, defaultContent = '') {
|
|
4
4
|
const source = configs && typeof configs === 'object' && !Array.isArray(configs)
|
|
@@ -11,7 +11,8 @@ function buildNormalizedOpenclawConfigs(configs, defaultContent = '') {
|
|
|
11
11
|
: { content: defaultContent };
|
|
12
12
|
const normalized = {
|
|
13
13
|
[DEFAULT_OPENCLAW_CONFIG_NAME]: {
|
|
14
|
-
content: typeof defaultEntry.content === 'string' ? defaultEntry.content : defaultContent
|
|
14
|
+
content: typeof defaultEntry.content === 'string' ? defaultEntry.content : defaultContent,
|
|
15
|
+
isDefault: true
|
|
15
16
|
}
|
|
16
17
|
};
|
|
17
18
|
for (const [name, value] of Object.entries(source)) {
|
|
@@ -25,7 +26,8 @@ function syncDefaultOpenclawConfigState(vm, content, options = {}) {
|
|
|
25
26
|
const nextContent = typeof content === 'string' ? content : '';
|
|
26
27
|
vm.openclawConfigs = buildNormalizedOpenclawConfigs(vm.openclawConfigs, nextContent);
|
|
27
28
|
vm.openclawConfigs[DEFAULT_OPENCLAW_CONFIG_NAME] = {
|
|
28
|
-
content: nextContent
|
|
29
|
+
content: nextContent,
|
|
30
|
+
isDefault: true
|
|
29
31
|
};
|
|
30
32
|
if (typeof options.path === 'string') {
|
|
31
33
|
vm.openclawConfigPath = options.path;
|
|
@@ -51,6 +53,10 @@ export function createOpenclawPersistMethods(options = {}) {
|
|
|
51
53
|
} = options;
|
|
52
54
|
|
|
53
55
|
return {
|
|
56
|
+
isDefaultOpenclawConfig(name, config = null) {
|
|
57
|
+
return !!(config && config.isDefault === true) || name === DEFAULT_OPENCLAW_CONFIG_NAME;
|
|
58
|
+
},
|
|
59
|
+
|
|
54
60
|
syncDefaultOpenclawConfigEntry(options = {}) {
|
|
55
61
|
const silent = !!options.silent;
|
|
56
62
|
return api('get-openclaw-config')
|
|
@@ -104,7 +110,7 @@ export function createOpenclawPersistMethods(options = {}) {
|
|
|
104
110
|
|
|
105
111
|
openOpenclawEditModal(name) {
|
|
106
112
|
const existing = this.openclawConfigs[name];
|
|
107
|
-
const isDefaultConfig = name
|
|
113
|
+
const isDefaultConfig = this.isDefaultOpenclawConfig(name, existing);
|
|
108
114
|
const modalToken = (Number(this.openclawModalLoadToken || 0) + 1);
|
|
109
115
|
this.openclawModalLoadToken = modalToken;
|
|
110
116
|
this.openclawEditorTitle = `编辑 OpenClaw 配置: ${name}`;
|
|
@@ -146,7 +152,7 @@ export function createOpenclawPersistMethods(options = {}) {
|
|
|
146
152
|
const force = !!options.force;
|
|
147
153
|
const fallbackToTemplate = options.fallbackToTemplate !== false;
|
|
148
154
|
const syncDefaultEntry = options.syncDefaultEntry === true
|
|
149
|
-
|| (this.openclawEditing && this.openclawEditing.lockName && this.openclawEditing.name
|
|
155
|
+
|| (this.openclawEditing && this.openclawEditing.lockName && this.isDefaultOpenclawConfig(this.openclawEditing.name));
|
|
150
156
|
const modalToken = Number(options.modalToken || this.openclawModalLoadToken || 0);
|
|
151
157
|
const expectedEditorContent = typeof options.expectedEditorContent === 'string'
|
|
152
158
|
? options.expectedEditorContent
|
|
@@ -260,7 +266,7 @@ export function createOpenclawPersistMethods(options = {}) {
|
|
|
260
266
|
if (this.openclawSaving || this.openclawApplying) {
|
|
261
267
|
return;
|
|
262
268
|
}
|
|
263
|
-
if (this.openclawEditing && this.openclawEditing.lockName && this.openclawEditing.name
|
|
269
|
+
if (this.openclawEditing && this.openclawEditing.lockName && this.isDefaultOpenclawConfig(this.openclawEditing.name)) {
|
|
264
270
|
this.showMessage('默认配置代表当前系统配置,请使用“保存并应用”', 'info');
|
|
265
271
|
return;
|
|
266
272
|
}
|
|
@@ -312,7 +318,7 @@ export function createOpenclawPersistMethods(options = {}) {
|
|
|
312
318
|
},
|
|
313
319
|
|
|
314
320
|
async deleteOpenclawConfig(name) {
|
|
315
|
-
if (name
|
|
321
|
+
if (this.isDefaultOpenclawConfig(name, this.openclawConfigs && this.openclawConfigs[name])) {
|
|
316
322
|
return this.showMessage('默认配置始终映射当前系统配置,不可删除', 'info');
|
|
317
323
|
}
|
|
318
324
|
if (Object.keys(this.openclawConfigs).length <= 1) {
|
|
@@ -47,6 +47,12 @@ function normalizeProviderDraftState(target) {
|
|
|
47
47
|
if (typeof target.url === 'string') {
|
|
48
48
|
target.url = normalizeProviderUrl(target.url);
|
|
49
49
|
}
|
|
50
|
+
if (typeof target.model === 'string') {
|
|
51
|
+
target.model = target.model.trim();
|
|
52
|
+
}
|
|
53
|
+
if (typeof target.key === 'string') {
|
|
54
|
+
target.key = target.key.trim();
|
|
55
|
+
}
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
function maskKeyLocal(key) {
|
|
@@ -66,9 +72,13 @@ function getProviderValidationForContext(vm, mode = 'add') {
|
|
|
66
72
|
const editingName = mode === 'edit' ? normalizeText(draft && draft.name) : '';
|
|
67
73
|
const name = normalizeText(draft && draft.name);
|
|
68
74
|
const url = normalizeProviderUrl(draft && draft.url);
|
|
75
|
+
const model = normalizeText(draft && draft.model);
|
|
76
|
+
const key = normalizeText(draft && draft.key);
|
|
69
77
|
const errors = {
|
|
70
78
|
name: '',
|
|
71
|
-
url: ''
|
|
79
|
+
url: '',
|
|
80
|
+
key: '',
|
|
81
|
+
model: ''
|
|
72
82
|
};
|
|
73
83
|
|
|
74
84
|
if (mode === 'add') {
|
|
@@ -91,12 +101,22 @@ function getProviderValidationForContext(vm, mode = 'add') {
|
|
|
91
101
|
errors.url = 'URL 仅支持 http/https';
|
|
92
102
|
}
|
|
93
103
|
|
|
104
|
+
if (mode === 'add' && !key) {
|
|
105
|
+
errors.key = 'API Key 必填';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (mode === 'add' && !model) {
|
|
109
|
+
errors.model = '模型名称必填';
|
|
110
|
+
}
|
|
111
|
+
|
|
94
112
|
return {
|
|
95
113
|
mode,
|
|
96
114
|
name,
|
|
97
115
|
url,
|
|
116
|
+
key,
|
|
117
|
+
model,
|
|
98
118
|
errors,
|
|
99
|
-
ok: !errors.name && !errors.url
|
|
119
|
+
ok: !errors.name && !errors.url && !errors.key && !errors.model
|
|
100
120
|
};
|
|
101
121
|
}
|
|
102
122
|
|
|
@@ -150,21 +170,20 @@ export function createProvidersMethods(options = {}) {
|
|
|
150
170
|
normalizeProviderDraftState(this.newProvider);
|
|
151
171
|
const validation = getProviderValidationForContext(this, 'add');
|
|
152
172
|
if (!validation.ok) {
|
|
153
|
-
return this.showMessage(validation.errors.name || validation.errors.url || '
|
|
173
|
+
return this.showMessage(validation.errors.name || validation.errors.url || validation.errors.key || validation.errors.model || '名称、URL、API Key 和模型名称必填', 'error');
|
|
154
174
|
}
|
|
155
175
|
|
|
156
176
|
try {
|
|
157
177
|
const payload = {
|
|
158
178
|
name: validation.name,
|
|
159
179
|
url: validation.url,
|
|
160
|
-
key:
|
|
180
|
+
key: validation.key,
|
|
181
|
+
model: validation.model
|
|
161
182
|
};
|
|
162
183
|
if (this.newProvider && this.newProvider.useTransform) {
|
|
163
184
|
payload.useTransform = true;
|
|
164
185
|
}
|
|
165
|
-
const suggestedModel =
|
|
166
|
-
? this.newProvider._suggestedModel.trim()
|
|
167
|
-
: '';
|
|
186
|
+
const suggestedModel = validation.model;
|
|
168
187
|
const res = await api('add-provider', payload);
|
|
169
188
|
if (res.error) {
|
|
170
189
|
this.showMessage(res.error, 'error');
|
|
@@ -179,7 +198,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
179
198
|
codexmate_bridge: payload.useTransform ? 'openai' : '',
|
|
180
199
|
key: maskKeyLocal(payload.key),
|
|
181
200
|
hasKey: !!payload.key,
|
|
182
|
-
models: [],
|
|
201
|
+
models: suggestedModel ? [{ id: suggestedModel, name: suggestedModel, cost: null, contextWindow: undefined, maxTokens: undefined }] : [],
|
|
183
202
|
current: false,
|
|
184
203
|
readOnly: false,
|
|
185
204
|
nonDeletable: false,
|
|
@@ -208,7 +227,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
208
227
|
const configured = !!(provider && provider.hasKey);
|
|
209
228
|
return {
|
|
210
229
|
configured,
|
|
211
|
-
text: configured ? '
|
|
230
|
+
text: configured ? this.t('common.configured') : this.t('common.notConfigured')
|
|
212
231
|
};
|
|
213
232
|
},
|
|
214
233
|
|
|
@@ -300,8 +319,10 @@ export function createProvidersMethods(options = {}) {
|
|
|
300
319
|
name: '',
|
|
301
320
|
url: cloneUrl,
|
|
302
321
|
key: '',
|
|
322
|
+
model: '',
|
|
303
323
|
useTransform: isTransform
|
|
304
324
|
};
|
|
325
|
+
this.showAddProviderKey = false;
|
|
305
326
|
this.showAddModal = true;
|
|
306
327
|
},
|
|
307
328
|
|
|
@@ -513,7 +534,12 @@ export function createProvidersMethods(options = {}) {
|
|
|
513
534
|
|
|
514
535
|
closeAddModal() {
|
|
515
536
|
this.showAddModal = false;
|
|
516
|
-
this.
|
|
537
|
+
this.showAddProviderKey = false;
|
|
538
|
+
this.newProvider = { name: '', url: '', key: '', model: '', useTransform: false };
|
|
539
|
+
},
|
|
540
|
+
|
|
541
|
+
toggleAddProviderKey() {
|
|
542
|
+
this.showAddProviderKey = !this.showAddProviderKey;
|
|
517
543
|
},
|
|
518
544
|
|
|
519
545
|
closeModelModal() {
|