codexmate 0.1.1 → 0.1.3
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 +6 -3
- package/cli/openai-bridge-retry.js +17 -9
- package/cli/openai-bridge-runtime.js +14 -1
- package/cli/openai-bridge.js +20 -10
- package/cli.js +375 -4
- package/package.json +1 -1
- package/web-ui/app.js +18 -4
- package/web-ui/index.html +1 -0
- package/web-ui/modules/app.computed.dashboard.mjs +14 -0
- package/web-ui/modules/app.methods.agents.mjs +2 -2
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.install.mjs +14 -0
- package/web-ui/modules/app.methods.kilocode-config.mjs +164 -0
- package/web-ui/modules/app.methods.navigation.mjs +12 -0
- package/web-ui/modules/app.methods.openclaw-core.mjs +87 -0
- package/web-ui/modules/app.methods.providers.mjs +5 -32
- package/web-ui/modules/app.methods.startup-claude.mjs +2 -1
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +3 -2
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +2 -1
- package/web-ui/modules/config-mode.computed.mjs +11 -1
- package/web-ui/modules/i18n/locales/en.mjs +31 -4
- package/web-ui/modules/i18n/locales/ja.mjs +34 -8
- package/web-ui/modules/i18n/locales/vi.mjs +35 -9
- package/web-ui/modules/i18n/locales/zh-tw.mjs +36 -9
- package/web-ui/modules/i18n/locales/zh.mjs +40 -13
- package/web-ui/partials/index/layout-header.html +32 -21
- package/web-ui/partials/index/modal-config-template-agents.html +8 -4
- package/web-ui/partials/index/modals-basic.html +0 -26
- package/web-ui/partials/index/panel-config-kilocode.html +101 -0
- package/web-ui/partials/index/panel-config-openclaw.html +42 -14
- package/web-ui/partials/index/panel-config-opencode.html +1 -0
- package/web-ui/partials/index/panel-docs.html +17 -1
- package/web-ui/res/web-ui-render.precompiled.js +400 -123
- package/web-ui/styles/controls-forms.css +10 -0
- package/web-ui/styles/docs-panel.css +51 -0
- package/web-ui/styles/layout-shell.css +12 -0
- package/web-ui/styles/modals-core.css +79 -0
- package/web-ui/styles/openclaw-structured.css +117 -71
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
function normalizeKilocodeProviderName(value) {
|
|
2
|
+
const name = typeof value === 'string' ? value.trim() : '';
|
|
3
|
+
return /^[a-zA-Z0-9_.-]+$/.test(name) ? name : '';
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const DEFAULT_KILOCODE_PROVIDER = 'codexmate';
|
|
7
|
+
const DEFAULT_KILOCODE_MODEL = 'gpt-5.3';
|
|
8
|
+
|
|
9
|
+
export function createKilocodeConfigMethods(options = {}) {
|
|
10
|
+
const { api } = options;
|
|
11
|
+
return {
|
|
12
|
+
normalizeKilocodeProviderNameForUi(value) {
|
|
13
|
+
return normalizeKilocodeProviderName(value);
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
refreshKilocodeSelectionFromSummary(res = {}) {
|
|
17
|
+
const providers = Array.isArray(res.providers) ? res.providers : [];
|
|
18
|
+
this.kilocodeProviders = providers;
|
|
19
|
+
this.kilocodeConfigPath = typeof res.targetPath === 'string' ? res.targetPath : '';
|
|
20
|
+
this.kilocodeConfigExists = res.exists === true;
|
|
21
|
+
this.kilocodeContent = typeof res.content === 'string' ? res.content : '{}\n';
|
|
22
|
+
const firstProvider = providers.find(item => item && item.name);
|
|
23
|
+
this.kilocodeProvider = normalizeKilocodeProviderName(res.currentProvider)
|
|
24
|
+
|| normalizeKilocodeProviderName(firstProvider && firstProvider.name)
|
|
25
|
+
|| DEFAULT_KILOCODE_PROVIDER;
|
|
26
|
+
const selected = providers.find(item => normalizeKilocodeProviderName(item && item.name) === this.kilocodeProvider);
|
|
27
|
+
this.kilocodeBaseUrl = typeof (selected && (selected.baseURL || selected.api)) === 'string'
|
|
28
|
+
? (selected.baseURL || selected.api)
|
|
29
|
+
: this.kilocodeBaseUrl;
|
|
30
|
+
const model = typeof res.currentModel === 'string' ? res.currentModel.trim() : '';
|
|
31
|
+
this.kilocodeModel = model || (selected && Array.isArray(selected.models) ? (selected.models[0] || '') : this.kilocodeModel) || DEFAULT_KILOCODE_MODEL;
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async loadKilocodeConfig(options = {}) {
|
|
35
|
+
if (this.kilocodeLoading) return;
|
|
36
|
+
this.kilocodeLoading = true;
|
|
37
|
+
this.kilocodeError = '';
|
|
38
|
+
try {
|
|
39
|
+
const res = await api('get-kilocode-config');
|
|
40
|
+
if (res && res.error) {
|
|
41
|
+
this.kilocodeError = res.error;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.refreshKilocodeSelectionFromSummary(res || {});
|
|
45
|
+
if (options.toast === true) this.showMessage('KiloCode 配置已刷新', 'success');
|
|
46
|
+
} catch (e) {
|
|
47
|
+
this.kilocodeError = e && e.message ? e.message : '读取 KiloCode 配置失败';
|
|
48
|
+
} finally {
|
|
49
|
+
this.kilocodeLoading = false;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
hasKilocodeStoredKey(provider) {
|
|
54
|
+
const normalizedProvider = normalizeKilocodeProviderName(provider || this.kilocodeProvider);
|
|
55
|
+
return (this.kilocodeProviders || []).some(item => normalizeKilocodeProviderName(item && item.name) === normalizedProvider && item.hasKey === true);
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
kilocodeConfigSignature(provider, url, model, apiKey) {
|
|
59
|
+
const keyMarker = apiKey ? `key:${apiKey}` : (this.hasKilocodeStoredKey(provider) ? 'key:<stored>' : 'key:<empty>');
|
|
60
|
+
return [provider, url, model, keyMarker].join('\u0000');
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async selectKilocodeProvider(provider = {}) {
|
|
64
|
+
const name = normalizeKilocodeProviderName(provider && provider.name);
|
|
65
|
+
if (!name) return false;
|
|
66
|
+
const url = typeof (provider.baseURL || provider.api) === 'string' ? (provider.baseURL || provider.api).trim() : '';
|
|
67
|
+
const model = Array.isArray(provider.models) && provider.models.length
|
|
68
|
+
? String(provider.models[0] || '').trim()
|
|
69
|
+
: DEFAULT_KILOCODE_MODEL;
|
|
70
|
+
this.kilocodeProvider = name;
|
|
71
|
+
if (url) this.kilocodeBaseUrl = url;
|
|
72
|
+
this.kilocodeModel = model || DEFAULT_KILOCODE_MODEL;
|
|
73
|
+
this.kilocodeApiKey = '';
|
|
74
|
+
this.kilocodeAutoSaveSignature = '';
|
|
75
|
+
if (!this.isToolConfigWriteAllowed('kilocode') || !url || !this.hasKilocodeStoredKey(name)) return true;
|
|
76
|
+
return this.saveKilocodeConfig({ auto: true });
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
async autoSaveKilocodeConfig() {
|
|
80
|
+
if (!this.isToolConfigWriteAllowed('kilocode') || this.kilocodeSaving || this.kilocodeLoading) return false;
|
|
81
|
+
const provider = normalizeKilocodeProviderName(this.kilocodeProvider) || DEFAULT_KILOCODE_PROVIDER;
|
|
82
|
+
const url = typeof this.kilocodeBaseUrl === 'string' ? this.kilocodeBaseUrl.trim() : '';
|
|
83
|
+
const model = (typeof this.kilocodeModel === 'string' ? this.kilocodeModel.trim() : '') || DEFAULT_KILOCODE_MODEL;
|
|
84
|
+
const apiKey = typeof this.kilocodeApiKey === 'string' ? this.kilocodeApiKey.trim() : '';
|
|
85
|
+
if (!url || (!apiKey && !this.hasKilocodeStoredKey(provider))) return false;
|
|
86
|
+
const signature = this.kilocodeConfigSignature(provider, url, model, apiKey);
|
|
87
|
+
if (signature === this.kilocodeAutoSaveSignature) return false;
|
|
88
|
+
return this.saveKilocodeConfig({ auto: true, signature });
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
async saveKilocodeConfig(options = {}) {
|
|
92
|
+
if (this.kilocodeSaving) return false;
|
|
93
|
+
const auto = options && options.auto === true;
|
|
94
|
+
if (!this.isToolConfigWriteAllowed('kilocode')) {
|
|
95
|
+
if (!auto) this.showMessage(this.t ? this.t('kilocode.writeRequired') : '请先打开 KiloCode 写入开关', 'error');
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const provider = normalizeKilocodeProviderName(this.kilocodeProvider) || DEFAULT_KILOCODE_PROVIDER;
|
|
99
|
+
const url = typeof this.kilocodeBaseUrl === 'string' ? this.kilocodeBaseUrl.trim() : '';
|
|
100
|
+
const model = (typeof this.kilocodeModel === 'string' ? this.kilocodeModel.trim() : '') || DEFAULT_KILOCODE_MODEL;
|
|
101
|
+
const apiKey = typeof this.kilocodeApiKey === 'string' ? this.kilocodeApiKey.trim() : '';
|
|
102
|
+
if (!url || (!apiKey && !this.hasKilocodeStoredKey(provider))) {
|
|
103
|
+
if (!auto) this.showMessage(this.t ? this.t('kilocode.fillRequired') : '请填写 KiloCode URL 和 API Key', 'error');
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
this.kilocodeProvider = provider;
|
|
107
|
+
this.kilocodeModel = model;
|
|
108
|
+
this.kilocodeSaving = true;
|
|
109
|
+
this.kilocodeError = '';
|
|
110
|
+
try {
|
|
111
|
+
const res = await api('apply-kilocode-config', { provider, url, model, apiKey });
|
|
112
|
+
if (res && res.error) {
|
|
113
|
+
this.kilocodeError = res.error;
|
|
114
|
+
this.showMessage(res.error, 'error');
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
this.kilocodeApiKey = '';
|
|
118
|
+
this.refreshKilocodeSelectionFromSummary(res || {});
|
|
119
|
+
this.kilocodeAutoSaveSignature = options && options.signature ? options.signature : this.kilocodeConfigSignature(provider, url, model, '');
|
|
120
|
+
this.showMessage(this.t ? this.t(auto ? 'kilocode.autoSaved' : 'kilocode.saved') : 'KiloCode 配置已保存', 'success');
|
|
121
|
+
return true;
|
|
122
|
+
} catch (e) {
|
|
123
|
+
this.kilocodeError = e && e.message ? e.message : (this.t ? this.t('kilocode.saveFailed') : '保存 KiloCode 配置失败');
|
|
124
|
+
this.showMessage(this.kilocodeError, 'error');
|
|
125
|
+
return false;
|
|
126
|
+
} finally {
|
|
127
|
+
this.kilocodeSaving = false;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
async startKilocode(configure = false) {
|
|
132
|
+
if (this.kilocodeStarting) return;
|
|
133
|
+
if (!this.isToolConfigWriteAllowed('kilocode')) {
|
|
134
|
+
this.showMessage('请先打开 KiloCode 写入开关', 'error');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.kilocodeStarting = true;
|
|
138
|
+
this.kilocodeError = '';
|
|
139
|
+
try {
|
|
140
|
+
const payload = configure ? {
|
|
141
|
+
configure: true,
|
|
142
|
+
provider: this.kilocodeProvider,
|
|
143
|
+
url: this.kilocodeBaseUrl,
|
|
144
|
+
model: this.kilocodeModel,
|
|
145
|
+
apiKey: this.kilocodeApiKey
|
|
146
|
+
} : {};
|
|
147
|
+
const res = await api('start-kilocode', payload);
|
|
148
|
+
if (res && res.error) {
|
|
149
|
+
this.kilocodeError = res.error;
|
|
150
|
+
this.showMessage(res.error, 'error');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.kilocodeApiKey = '';
|
|
154
|
+
this.showMessage(`KiloCode 已启动${res && res.pid ? `(PID ${res.pid})` : ''}`, 'success');
|
|
155
|
+
await this.loadKilocodeConfig();
|
|
156
|
+
} catch (e) {
|
|
157
|
+
this.kilocodeError = e && e.message ? e.message : '启动 KiloCode 失败';
|
|
158
|
+
this.showMessage(this.kilocodeError, 'error');
|
|
159
|
+
} finally {
|
|
160
|
+
this.kilocodeStarting = false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
@@ -148,6 +148,9 @@
|
|
|
148
148
|
}
|
|
149
149
|
this.configMode = configModeSet.has(normalizedMode) ? normalizedMode : 'codex';
|
|
150
150
|
if (this.mainTab === 'config') {
|
|
151
|
+
if (this.configMode === 'kilocode' && typeof this.loadKilocodeConfig === 'function') {
|
|
152
|
+
this.loadKilocodeConfig();
|
|
153
|
+
}
|
|
151
154
|
if (this.configMode === 'claude') {
|
|
152
155
|
const expectedMainTab = 'config';
|
|
153
156
|
const expectedConfigMode = 'claude';
|
|
@@ -458,6 +461,9 @@
|
|
|
458
461
|
if (targetTab === previousTab) {
|
|
459
462
|
switchState.ticket += 1;
|
|
460
463
|
switchState.pendingTarget = '';
|
|
464
|
+
if (targetTab === 'config' && this.configMode === 'kilocode' && typeof this.loadKilocodeConfig === 'function') {
|
|
465
|
+
void this.loadKilocodeConfig();
|
|
466
|
+
}
|
|
461
467
|
if (targetTab === 'dashboard' && !this.__doctorLoadedOnce) {
|
|
462
468
|
void loadDoctorOverview(this);
|
|
463
469
|
}
|
|
@@ -490,6 +496,9 @@
|
|
|
490
496
|
switchState.pendingTarget = '';
|
|
491
497
|
const result = switchMainTabHelper.call(this, targetTab);
|
|
492
498
|
persistNavState(this);
|
|
499
|
+
if (targetTab === 'config' && this.configMode === 'kilocode' && typeof this.loadKilocodeConfig === 'function') {
|
|
500
|
+
void this.loadKilocodeConfig();
|
|
501
|
+
}
|
|
493
502
|
if (targetTab === 'dashboard') {
|
|
494
503
|
void loadDoctorOverview(this);
|
|
495
504
|
}
|
|
@@ -508,6 +517,9 @@
|
|
|
508
517
|
liveState.pendingTarget = '';
|
|
509
518
|
switchMainTabHelper.call(this, pendingTarget);
|
|
510
519
|
persistNavState(this);
|
|
520
|
+
if (pendingTarget === 'config' && this.configMode === 'kilocode' && typeof this.loadKilocodeConfig === 'function') {
|
|
521
|
+
void this.loadKilocodeConfig();
|
|
522
|
+
}
|
|
511
523
|
if (pendingTarget === 'dashboard') {
|
|
512
524
|
void loadDoctorOverview(this);
|
|
513
525
|
}
|
|
@@ -224,6 +224,31 @@ function readPreferredProviderModels(records) {
|
|
|
224
224
|
return [];
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
function readNestedValue(record, path) {
|
|
228
|
+
if (!isPlainRecord(record)) return undefined;
|
|
229
|
+
let cursor = record;
|
|
230
|
+
for (const key of path) {
|
|
231
|
+
if (!isPlainRecord(cursor) || !Object.prototype.hasOwnProperty.call(cursor, key)) {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
cursor = cursor[key];
|
|
235
|
+
}
|
|
236
|
+
return cursor;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function formatOpenclawSummaryValue(value, fallback = '未配置') {
|
|
240
|
+
if (Array.isArray(value)) {
|
|
241
|
+
const list = value
|
|
242
|
+
.map(item => (typeof item === 'string' ? item.trim() : String(item || '').trim()))
|
|
243
|
+
.filter(Boolean);
|
|
244
|
+
return list.length ? list.join(' / ') : fallback;
|
|
245
|
+
}
|
|
246
|
+
if (value === true) return '启用';
|
|
247
|
+
if (value === false) return '关闭';
|
|
248
|
+
const text = typeof value === 'string' ? value.trim() : String(value ?? '').trim();
|
|
249
|
+
return text || fallback;
|
|
250
|
+
}
|
|
251
|
+
|
|
227
252
|
export function createOpenclawCoreMethods() {
|
|
228
253
|
return {
|
|
229
254
|
getOpenclawParser() {
|
|
@@ -472,6 +497,68 @@ export function createOpenclawCoreMethods() {
|
|
|
472
497
|
};
|
|
473
498
|
},
|
|
474
499
|
|
|
500
|
+
getOpenclawConfigSummary(config) {
|
|
501
|
+
const content = config && typeof config.content === 'string' ? config.content : '';
|
|
502
|
+
if (!content.trim()) {
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
const parsed = this.parseOpenclawContent(content, { allowEmpty: true });
|
|
506
|
+
if (!parsed.ok || !isPlainRecord(parsed.data)) {
|
|
507
|
+
return [
|
|
508
|
+
{ key: 'parse-error', label: '配置状态', value: '解析失败', tone: 'warning' }
|
|
509
|
+
];
|
|
510
|
+
}
|
|
511
|
+
const data = parsed.data;
|
|
512
|
+
const agentDefaults = readNestedValue(data, ['agents', 'defaults']);
|
|
513
|
+
const modelConfig = isPlainRecord(agentDefaults) ? agentDefaults.model : undefined;
|
|
514
|
+
const primaryModel = isPlainRecord(modelConfig)
|
|
515
|
+
? modelConfig.primary
|
|
516
|
+
: (typeof modelConfig === 'string' ? modelConfig : readNestedValue(data, ['agent', 'model']));
|
|
517
|
+
const fallbacks = isPlainRecord(modelConfig) && Array.isArray(modelConfig.fallbacks)
|
|
518
|
+
? modelConfig.fallbacks
|
|
519
|
+
: [];
|
|
520
|
+
const workspace = isPlainRecord(agentDefaults) ? agentDefaults.workspace : '';
|
|
521
|
+
const browser = isPlainRecord(data.browser) ? data.browser : {};
|
|
522
|
+
const browserMode = browser.enabled === false
|
|
523
|
+
? '关闭'
|
|
524
|
+
: (browser.headless === true ? 'Headless' : (browser.headless === false ? '可见窗口' : '未声明'));
|
|
525
|
+
const executablePath = typeof browser.executablePath === 'string' ? browser.executablePath.trim() : '';
|
|
526
|
+
const providerNames = collectDistinctProviderKeys(
|
|
527
|
+
readNestedValue(data, ['models', 'providers']),
|
|
528
|
+
data.providers
|
|
529
|
+
);
|
|
530
|
+
return [
|
|
531
|
+
{ key: 'primary', label: '默认模型', value: formatOpenclawSummaryValue(primaryModel) },
|
|
532
|
+
{ key: 'fallbacks', label: 'Fallback', value: formatOpenclawSummaryValue(fallbacks) },
|
|
533
|
+
{ key: 'workspace', label: 'Workspace', value: formatOpenclawSummaryValue(workspace) },
|
|
534
|
+
{ key: 'browser', label: 'Browser', value: browserMode },
|
|
535
|
+
{ key: 'browser-path', label: 'Chrome', value: formatOpenclawSummaryValue(executablePath) },
|
|
536
|
+
{ key: 'providers', label: 'Providers', value: providerNames.length ? providerNames.join(' / ') : '未配置' }
|
|
537
|
+
];
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
getOpenclawStatusSummaryItems() {
|
|
541
|
+
const current = this.openclawConfigs && this.currentOpenclawConfig
|
|
542
|
+
? this.openclawConfigs[this.currentOpenclawConfig]
|
|
543
|
+
: null;
|
|
544
|
+
const configPath = this.openclawConfigPath || '~/.openclaw/openclaw.json';
|
|
545
|
+
return [
|
|
546
|
+
{ key: 'config-path', label: 'Config', value: configPath, tone: this.openclawConfigExists ? 'ok' : 'warning' },
|
|
547
|
+
...this.getOpenclawConfigSummary(current || {}).slice(0, 4)
|
|
548
|
+
];
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
getOpenclawQuickWorkspaceFiles() {
|
|
552
|
+
return ['SOUL.md', 'USER.md', 'TOOLS.md', 'HEARTBEAT.md'];
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
openOpenclawQuickWorkspaceFile(fileName) {
|
|
556
|
+
const normalized = typeof fileName === 'string' ? fileName.trim() : '';
|
|
557
|
+
if (!normalized) return;
|
|
558
|
+
this.openclawWorkspaceFileName = normalized;
|
|
559
|
+
this.openOpenclawWorkspaceEditor();
|
|
560
|
+
},
|
|
561
|
+
|
|
475
562
|
syncOpenclawQuickFromText(options = {}) {
|
|
476
563
|
const silent = !!options.silent;
|
|
477
564
|
const parsed = this.parseOpenclawContent(this.openclawEditing.content, { allowEmpty: true });
|
|
@@ -41,13 +41,6 @@ function findProviderByName(list, name) {
|
|
|
41
41
|
return (Array.isArray(list) ? list : []).find((item) => item && normalizeText(item.name) === target) || null;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
function normalizeBridgeMaxRetries(value, fallback = 2) {
|
|
45
|
-
const raw = Number(value);
|
|
46
|
-
const fallbackRaw = Number(fallback);
|
|
47
|
-
const base = Number.isFinite(raw) ? raw : (Number.isFinite(fallbackRaw) ? fallbackRaw : 2);
|
|
48
|
-
return Math.min(10, Math.max(2, Math.floor(base)));
|
|
49
|
-
}
|
|
50
|
-
|
|
51
44
|
function normalizeProviderDraftState(target) {
|
|
52
45
|
if (!target || typeof target !== 'object') return;
|
|
53
46
|
if (typeof target.name === 'string') {
|
|
@@ -62,9 +55,6 @@ function normalizeProviderDraftState(target) {
|
|
|
62
55
|
if (typeof target.key === 'string') {
|
|
63
56
|
target.key = target.key.trim();
|
|
64
57
|
}
|
|
65
|
-
if (target.useTransform || target.openaiBridgeMaxRetries !== undefined) {
|
|
66
|
-
target.openaiBridgeMaxRetries = normalizeBridgeMaxRetries(target.openaiBridgeMaxRetries);
|
|
67
|
-
}
|
|
68
58
|
}
|
|
69
59
|
|
|
70
60
|
function maskKeyLocal(key) {
|
|
@@ -87,13 +77,11 @@ function getProviderValidationForContext(vm, mode = 'add') {
|
|
|
87
77
|
const model = normalizeText(draft && draft.model);
|
|
88
78
|
const key = normalizeText(draft && draft.key);
|
|
89
79
|
const useTransform = !!(draft && draft.useTransform);
|
|
90
|
-
const openaiBridgeMaxRetries = normalizeBridgeMaxRetries(draft && draft.openaiBridgeMaxRetries);
|
|
91
80
|
const errors = {
|
|
92
81
|
name: '',
|
|
93
82
|
url: '',
|
|
94
83
|
key: '',
|
|
95
84
|
model: '',
|
|
96
|
-
openaiBridgeMaxRetries: ''
|
|
97
85
|
};
|
|
98
86
|
|
|
99
87
|
if (mode === 'add') {
|
|
@@ -124,10 +112,6 @@ function getProviderValidationForContext(vm, mode = 'add') {
|
|
|
124
112
|
errors.model = '模型名称必填';
|
|
125
113
|
}
|
|
126
114
|
|
|
127
|
-
if (useTransform && openaiBridgeMaxRetries < 2) {
|
|
128
|
-
errors.openaiBridgeMaxRetries = '重试次数最小为 2';
|
|
129
|
-
}
|
|
130
|
-
|
|
131
115
|
return {
|
|
132
116
|
mode,
|
|
133
117
|
name,
|
|
@@ -135,9 +119,8 @@ function getProviderValidationForContext(vm, mode = 'add') {
|
|
|
135
119
|
key,
|
|
136
120
|
model,
|
|
137
121
|
useTransform,
|
|
138
|
-
openaiBridgeMaxRetries,
|
|
139
122
|
errors,
|
|
140
|
-
ok: !errors.name && !errors.url && !errors.key && !errors.model
|
|
123
|
+
ok: !errors.name && !errors.url && !errors.key && !errors.model
|
|
141
124
|
};
|
|
142
125
|
}
|
|
143
126
|
|
|
@@ -191,7 +174,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
191
174
|
normalizeProviderDraftState(this.newProvider);
|
|
192
175
|
const validation = getProviderValidationForContext(this, 'add');
|
|
193
176
|
if (!validation.ok) {
|
|
194
|
-
return this.showMessage(validation.errors.name || validation.errors.url || validation.errors.key || validation.errors.model ||
|
|
177
|
+
return this.showMessage(validation.errors.name || validation.errors.url || validation.errors.key || validation.errors.model || this.t('toast.provider.fieldsRequired'), 'error');
|
|
195
178
|
}
|
|
196
179
|
|
|
197
180
|
try {
|
|
@@ -203,7 +186,6 @@ export function createProvidersMethods(options = {}) {
|
|
|
203
186
|
};
|
|
204
187
|
if (this.newProvider && this.newProvider.useTransform) {
|
|
205
188
|
payload.useTransform = true;
|
|
206
|
-
payload.openaiBridgeMaxRetries = validation.openaiBridgeMaxRetries;
|
|
207
189
|
}
|
|
208
190
|
const suggestedModel = validation.model;
|
|
209
191
|
const res = await api('add-provider', payload);
|
|
@@ -218,7 +200,6 @@ export function createProvidersMethods(options = {}) {
|
|
|
218
200
|
url: validation.url,
|
|
219
201
|
upstreamUrl: '',
|
|
220
202
|
codexmate_bridge: payload.useTransform ? 'openai' : '',
|
|
221
|
-
openaiBridgeMaxRetries: payload.useTransform ? validation.openaiBridgeMaxRetries : undefined,
|
|
222
203
|
key: maskKeyLocal(payload.key),
|
|
223
204
|
hasKey: !!payload.key,
|
|
224
205
|
models: suggestedModel ? [{ id: suggestedModel, name: suggestedModel, cost: null, contextWindow: undefined, maxTokens: undefined }] : [],
|
|
@@ -345,7 +326,6 @@ export function createProvidersMethods(options = {}) {
|
|
|
345
326
|
key: '',
|
|
346
327
|
model: '',
|
|
347
328
|
useTransform: isTransform,
|
|
348
|
-
openaiBridgeMaxRetries: normalizeBridgeMaxRetries(provider.openaiBridgeMaxRetries)
|
|
349
329
|
};
|
|
350
330
|
this.showAddProviderKey = false;
|
|
351
331
|
this.showAddModal = true;
|
|
@@ -358,7 +338,6 @@ export function createProvidersMethods(options = {}) {
|
|
|
358
338
|
key: '',
|
|
359
339
|
model: '',
|
|
360
340
|
useTransform: false,
|
|
361
|
-
openaiBridgeMaxRetries: 2
|
|
362
341
|
};
|
|
363
342
|
this.showAddProviderKey = false;
|
|
364
343
|
this.showAddModal = true;
|
|
@@ -387,7 +366,6 @@ export function createProvidersMethods(options = {}) {
|
|
|
387
366
|
? provider.nonEditable
|
|
388
367
|
: this.isNonDeletableProvider(provider),
|
|
389
368
|
useTransform: isTransformProvider,
|
|
390
|
-
openaiBridgeMaxRetries: normalizeBridgeMaxRetries(provider.openaiBridgeMaxRetries)
|
|
391
369
|
};
|
|
392
370
|
this._editProviderOriginalKey = '';
|
|
393
371
|
this._editProviderRealKeyLoaded = false;
|
|
@@ -428,9 +406,6 @@ export function createProvidersMethods(options = {}) {
|
|
|
428
406
|
&& res.baseUrl.trim()
|
|
429
407
|
) {
|
|
430
408
|
this.editingProvider.url = normalizeProviderUrl(res.baseUrl);
|
|
431
|
-
if (res.maxRetries !== undefined) {
|
|
432
|
-
this.editingProvider.openaiBridgeMaxRetries = normalizeBridgeMaxRetries(res.maxRetries);
|
|
433
|
-
}
|
|
434
409
|
}
|
|
435
410
|
} catch (_) {
|
|
436
411
|
// ignore
|
|
@@ -447,13 +422,12 @@ export function createProvidersMethods(options = {}) {
|
|
|
447
422
|
normalizeProviderDraftState(this.editingProvider);
|
|
448
423
|
const validation = getProviderValidationForContext(this, 'edit');
|
|
449
424
|
if (!validation.ok) {
|
|
450
|
-
return this.showMessage(validation.errors.name || validation.errors.url ||
|
|
425
|
+
return this.showMessage(validation.errors.name || validation.errors.url || this.t('toast.provider.urlRequired'), 'error');
|
|
451
426
|
}
|
|
452
427
|
|
|
453
428
|
const params = { name: validation.name, url: validation.url };
|
|
454
429
|
if (this.editingProvider && this.editingProvider.useTransform) {
|
|
455
430
|
params.useTransform = true;
|
|
456
|
-
params.openaiBridgeMaxRetries = validation.openaiBridgeMaxRetries;
|
|
457
431
|
}
|
|
458
432
|
if (this._editProviderRealKeyLoaded) {
|
|
459
433
|
const currentKey = typeof this.editingProvider.key === 'string' ? this.editingProvider.key : '';
|
|
@@ -479,7 +453,6 @@ export function createProvidersMethods(options = {}) {
|
|
|
479
453
|
key: keyUpdated ? maskKeyLocal(params.key) : p.key,
|
|
480
454
|
hasKey: keyUpdated ? !!params.key : p.hasKey,
|
|
481
455
|
codexmate_bridge: params.useTransform ? 'openai' : p.codexmate_bridge,
|
|
482
|
-
openaiBridgeMaxRetries: params.useTransform ? validation.openaiBridgeMaxRetries : p.openaiBridgeMaxRetries
|
|
483
456
|
};
|
|
484
457
|
}
|
|
485
458
|
return p;
|
|
@@ -497,7 +470,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
497
470
|
this.showEditProviderKey = false;
|
|
498
471
|
this._editProviderOriginalKey = '';
|
|
499
472
|
this._editProviderRealKeyLoaded = false;
|
|
500
|
-
this.editingProvider = { name: '', url: '', key: '', readOnly: false, nonEditable: false, useTransform: false
|
|
473
|
+
this.editingProvider = { name: '', url: '', key: '', readOnly: false, nonEditable: false, useTransform: false };
|
|
501
474
|
},
|
|
502
475
|
|
|
503
476
|
toggleEditProviderKey() {
|
|
@@ -580,7 +553,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
580
553
|
closeAddModal() {
|
|
581
554
|
this.showAddModal = false;
|
|
582
555
|
this.showAddProviderKey = false;
|
|
583
|
-
this.newProvider = { name: nextCodexProviderName(this.providersList), url: '', key: '', model: '', useTransform: false
|
|
556
|
+
this.newProvider = { name: nextCodexProviderName(this.providersList), url: '', key: '', model: '', useTransform: false };
|
|
584
557
|
},
|
|
585
558
|
|
|
586
559
|
toggleAddProviderKey() {
|
|
@@ -143,7 +143,8 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
143
143
|
this.toolConfigPermissions = {
|
|
144
144
|
codex: statusRes.toolConfigPermissions.codex === true,
|
|
145
145
|
claude: statusRes.toolConfigPermissions.claude === true,
|
|
146
|
-
opencode: statusRes.toolConfigPermissions.opencode === true
|
|
146
|
+
opencode: statusRes.toolConfigPermissions.opencode === true,
|
|
147
|
+
kilocode: statusRes.toolConfigPermissions.kilocode === true
|
|
147
148
|
};
|
|
148
149
|
}
|
|
149
150
|
this.providersList = listRes.providers;
|
|
@@ -3,7 +3,7 @@ export function createToolConfigPermissionMethods(options = {}) {
|
|
|
3
3
|
|
|
4
4
|
function normalizeTarget(value) {
|
|
5
5
|
const target = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
6
|
-
return target === 'codex' || target === 'claude' || target === 'opencode' ? target : '';
|
|
6
|
+
return target === 'codex' || target === 'claude' || target === 'opencode' || target === 'kilocode' ? target : '';
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
function normalizePermissions(value) {
|
|
@@ -11,7 +11,8 @@ export function createToolConfigPermissionMethods(options = {}) {
|
|
|
11
11
|
return {
|
|
12
12
|
codex: source.codex === true,
|
|
13
13
|
claude: source.claude === true,
|
|
14
|
-
opencode: source.opencode === true
|
|
14
|
+
opencode: source.opencode === true,
|
|
15
|
+
kilocode: source.kilocode === true
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -438,7 +438,8 @@ export function createWebUiPreferencesMethods(options = {}) {
|
|
|
438
438
|
this.toolConfigPermissions = {
|
|
439
439
|
codex: source.toolConfigPermissions.codex === true,
|
|
440
440
|
claude: source.toolConfigPermissions.claude === true,
|
|
441
|
-
opencode: source.toolConfigPermissions.opencode === true
|
|
441
|
+
opencode: source.toolConfigPermissions.opencode === true,
|
|
442
|
+
kilocode: source.toolConfigPermissions.kilocode === true
|
|
442
443
|
};
|
|
443
444
|
}
|
|
444
445
|
if (Array.isArray(source.deletedClaudeSettingsImports)) {
|
|
@@ -11,7 +11,8 @@ export const CONFIG_MODE_SET = new Set([
|
|
|
11
11
|
...Object.keys(PROVIDER_CONFIG_MODE_META),
|
|
12
12
|
'claude',
|
|
13
13
|
'openclaw',
|
|
14
|
-
'opencode'
|
|
14
|
+
'opencode',
|
|
15
|
+
'kilocode'
|
|
15
16
|
]);
|
|
16
17
|
|
|
17
18
|
export function getProviderConfigModeMeta(mode) {
|
|
@@ -63,6 +64,7 @@ export function createConfigModeComputed() {
|
|
|
63
64
|
if (this.configMode === 'claude') return 'Claude Code';
|
|
64
65
|
if (this.configMode === 'openclaw') return 'OpenClaw';
|
|
65
66
|
if (this.configMode === 'opencode') return 'OpenCode';
|
|
67
|
+
if (this.configMode === 'kilocode') return 'KiloCode';
|
|
66
68
|
return '未选择';
|
|
67
69
|
},
|
|
68
70
|
inspectorCurrentConfigLabel() {
|
|
@@ -82,6 +84,10 @@ export function createConfigModeComputed() {
|
|
|
82
84
|
const provider = typeof this.opencodeProvider === 'string' ? this.opencodeProvider.trim() : '';
|
|
83
85
|
return provider || '未选择';
|
|
84
86
|
}
|
|
87
|
+
if (this.configMode === 'kilocode') {
|
|
88
|
+
const provider = typeof this.kilocodeProvider === 'string' ? this.kilocodeProvider.trim() : '';
|
|
89
|
+
return provider || '未选择';
|
|
90
|
+
}
|
|
85
91
|
return '未选择';
|
|
86
92
|
},
|
|
87
93
|
inspectorCurrentModelLabel() {
|
|
@@ -103,6 +109,10 @@ export function createConfigModeComputed() {
|
|
|
103
109
|
const model = typeof this.opencodeModel === 'string' ? this.opencodeModel.trim() : '';
|
|
104
110
|
return model || '未选择';
|
|
105
111
|
}
|
|
112
|
+
if (this.configMode === 'kilocode') {
|
|
113
|
+
const model = typeof this.kilocodeModel === 'string' ? this.kilocodeModel.trim() : '';
|
|
114
|
+
return model || '未选择';
|
|
115
|
+
}
|
|
106
116
|
return '未选择';
|
|
107
117
|
},
|
|
108
118
|
inspectorTemplateStatus() {
|
|
@@ -142,6 +142,7 @@ const en = Object.freeze({
|
|
|
142
142
|
'side.docs': 'Docs',
|
|
143
143
|
'side.config': 'Config',
|
|
144
144
|
'side.sessions': 'Sessions',
|
|
145
|
+
'side.workspace': 'Workspace',
|
|
145
146
|
'side.prompts': 'Prompts',
|
|
146
147
|
'side.prompts.meta': 'Prompt files and presets',
|
|
147
148
|
'side.plugins': 'Plugins',
|
|
@@ -172,7 +173,7 @@ const en = Object.freeze({
|
|
|
172
173
|
'side.config.claude': 'Claude Code',
|
|
173
174
|
'side.config.claude.meta': 'Settings',
|
|
174
175
|
'side.config.openclaw': 'OpenClaw',
|
|
175
|
-
'side.config.openclaw.meta': '
|
|
176
|
+
'side.config.openclaw.meta': 'Config',
|
|
176
177
|
'side.config.opencode': 'OpenCode',
|
|
177
178
|
'side.config.opencode.meta': 'Provider',
|
|
178
179
|
'side.sessions.browser': 'Session Browser',
|
|
@@ -517,8 +518,6 @@ const en = Object.freeze({
|
|
|
517
518
|
'modal.claudeConfigEdit.title': 'Edit Claude Code config',
|
|
518
519
|
'field.useBuiltinTransform': 'Use built-in transform (OpenAI compatible)',
|
|
519
520
|
'hint.useBuiltinTransform': 'When enabled, base_url points to codexmate built-in transform service; Codex token is fixed to codexmate.',
|
|
520
|
-
'field.transformMaxRetries': 'Built-in transform retries',
|
|
521
|
-
'hint.transformMaxRetries': 'Default 2, minimum 2; number of retries after the first failed attempt.',
|
|
522
521
|
|
|
523
522
|
// Config template / agents modals
|
|
524
523
|
'modal.configTemplate.label': 'config.toml template',
|
|
@@ -620,9 +619,12 @@ const en = Object.freeze({
|
|
|
620
619
|
|
|
621
620
|
// Docs panel
|
|
622
621
|
'docs.title': 'CLI Install',
|
|
623
|
-
'docs.subtitle': 'Install commands for Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI.',
|
|
622
|
+
'docs.subtitle': 'Install commands for Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI / KiloCode CLI.',
|
|
624
623
|
'docs.section.commands': 'Commands',
|
|
625
624
|
'docs.section.commandsNote': 'Copy and run directly.',
|
|
625
|
+
'docs.installed.title': 'Currently installed commands',
|
|
626
|
+
'docs.installed.count': '{count} installed',
|
|
627
|
+
'docs.installed.empty': 'No installed commands detected',
|
|
626
628
|
'docs.section.faq': 'FAQ',
|
|
627
629
|
'docs.section.faqNote': 'Common issues and tips.',
|
|
628
630
|
'docs.command.aria': '{name} command',
|
|
@@ -1532,6 +1534,31 @@ const en = Object.freeze({
|
|
|
1532
1534
|
'openclaw.action.applyAria': 'Apply OpenClaw config: {name}',
|
|
1533
1535
|
'openclaw.action.editAria': 'Edit OpenClaw config: {name}',
|
|
1534
1536
|
'openclaw.action.deleteAria': 'Delete OpenClaw config: {name}'
|
|
1537
|
+
,
|
|
1538
|
+
|
|
1539
|
+
'tab.config.kilocode': 'KiloCode',
|
|
1540
|
+
'side.config.kilocode': 'KiloCode',
|
|
1541
|
+
'side.config.kilocode.meta': 'Provider',
|
|
1542
|
+
'toolConfig.kilocode.title': 'KiloCode config writes and launch',
|
|
1543
|
+
'toolConfig.kilocode.desc': 'Read-only by default; enable writes to sync KiloCode config and launch it from the Web UI.',
|
|
1544
|
+
'toolConfig.kilocode.lockedTitle': 'Read-only mode',
|
|
1545
|
+
'toolConfig.kilocode.lockedDesc': 'Enable write access to auto-sync config or launch KiloCode.',
|
|
1546
|
+
'toolConfig.kilocode.confirmMessage': 'When enabled, actions in the KiloCode tab can write ~/.config/kilo/kilo.jsonc or an existing kilo.json, and can launch local KiloCode.',
|
|
1547
|
+
'kilocode.providerModel.title': 'KiloCode provider / model',
|
|
1548
|
+
'kilocode.connection.title': 'KiloCode connection',
|
|
1549
|
+
'kilocode.targetFile': 'Active KiloCode config: {path} · {status}',
|
|
1550
|
+
'kilocode.saveConfig': 'Save config',
|
|
1551
|
+
'kilocode.start': 'Launch KiloCode',
|
|
1552
|
+
'kilocode.starting': 'Launching',
|
|
1553
|
+
'kilocode.saveAndStart': 'Save and launch',
|
|
1554
|
+
'kilocode.summary.title': 'Current KiloCode provider',
|
|
1555
|
+
'kilocode.configPreview.title': 'KiloCode config preview',
|
|
1556
|
+
'kilocode.configPreview.hint': 'API Key is redacted; auto-sync writes the real key.',
|
|
1557
|
+
'kilocode.saveFailed': 'Failed to save KiloCode config',
|
|
1558
|
+
'kilocode.fillRequired': 'Fill KiloCode URL and API Key',
|
|
1559
|
+
'kilocode.writeRequired': 'Enable KiloCode write access first',
|
|
1560
|
+
'kilocode.saved': 'KiloCode config saved',
|
|
1561
|
+
'kilocode.autoSaved': 'KiloCode config synced',
|
|
1535
1562
|
});
|
|
1536
1563
|
|
|
1537
1564
|
export { en };
|