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
package/cli.js
CHANGED
|
@@ -193,6 +193,9 @@ const CONFIG_DIR = path.join(os.homedir(), '.codex');
|
|
|
193
193
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.toml');
|
|
194
194
|
const AUTH_FILE = path.join(CONFIG_DIR, 'auth.json');
|
|
195
195
|
const OPENCODE_CONFIG_DIR = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'opencode');
|
|
196
|
+
const KILOCODE_CONFIG_DIR = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'kilo');
|
|
197
|
+
const KILOCODE_GLOBAL_JSONC_CONFIG_FILE = path.join(KILOCODE_CONFIG_DIR, 'kilo.jsonc');
|
|
198
|
+
const KILOCODE_GLOBAL_JSON_CONFIG_FILE = path.join(KILOCODE_CONFIG_DIR, 'kilo.json');
|
|
196
199
|
const OPENCODE_CONFIG_ENV_FILE = process.env.OPENCODE_CONFIG ? path.resolve(process.env.OPENCODE_CONFIG) : '';
|
|
197
200
|
const OPENCODE_GLOBAL_JSONC_CONFIG_FILE = path.join(OPENCODE_CONFIG_DIR, 'opencode.jsonc');
|
|
198
201
|
const OPENCODE_GLOBAL_JSON_CONFIG_FILE = path.join(OPENCODE_CONFIG_DIR, 'opencode.json');
|
|
@@ -362,6 +365,12 @@ const CLI_INSTALL_TARGETS = Object.freeze([
|
|
|
362
365
|
name: 'Codex CLI',
|
|
363
366
|
packageName: '@openai/codex',
|
|
364
367
|
bins: ['codex']
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
id: 'kilocode',
|
|
371
|
+
name: 'KiloCode CLI',
|
|
372
|
+
packageName: '@kilocode/cli',
|
|
373
|
+
bins: ['kilo', 'kilocode']
|
|
365
374
|
}
|
|
366
375
|
]);
|
|
367
376
|
|
|
@@ -900,8 +909,8 @@ function isPlainObject(value) {
|
|
|
900
909
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
901
910
|
}
|
|
902
911
|
|
|
903
|
-
const TOOL_CONFIG_PERMISSION_TARGETS = new Set(['codex', 'claude', 'opencode']);
|
|
904
|
-
const TOOL_CONFIG_PERMISSION_DEFAULTS = Object.freeze({ codex: false, claude: false, opencode: false });
|
|
912
|
+
const TOOL_CONFIG_PERMISSION_TARGETS = new Set(['codex', 'claude', 'opencode', 'kilocode']);
|
|
913
|
+
const TOOL_CONFIG_PERMISSION_DEFAULTS = Object.freeze({ codex: false, claude: false, opencode: false, kilocode: false });
|
|
905
914
|
let toolConfigWriteGuardDepth = 0;
|
|
906
915
|
|
|
907
916
|
function enterToolConfigWriteGuard() {
|
|
@@ -928,7 +937,8 @@ function normalizeToolConfigPermissions(value) {
|
|
|
928
937
|
return {
|
|
929
938
|
codex: source.codex === true,
|
|
930
939
|
claude: source.claude === true,
|
|
931
|
-
opencode: source.opencode === true
|
|
940
|
+
opencode: source.opencode === true,
|
|
941
|
+
kilocode: source.kilocode === true
|
|
932
942
|
};
|
|
933
943
|
}
|
|
934
944
|
|
|
@@ -986,7 +996,7 @@ function normalizeMainTabPreference(value) {
|
|
|
986
996
|
|
|
987
997
|
function normalizeConfigModePreference(value) {
|
|
988
998
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
989
|
-
return ['codex', 'claude', 'openclaw', 'opencode'].includes(normalized) ? normalized : 'codex';
|
|
999
|
+
return ['codex', 'claude', 'openclaw', 'opencode', 'kilocode'].includes(normalized) ? normalized : 'codex';
|
|
990
1000
|
}
|
|
991
1001
|
|
|
992
1002
|
function normalizeUsageTimeRangePreference(value) {
|
|
@@ -1183,9 +1193,14 @@ function getApiToolConfigWriteTarget(action) {
|
|
|
1183
1193
|
'apply-opencode-config',
|
|
1184
1194
|
'update-opencode-selection'
|
|
1185
1195
|
]);
|
|
1196
|
+
const kilocodeWriteActions = new Set([
|
|
1197
|
+
'apply-kilocode-config',
|
|
1198
|
+
'start-kilocode'
|
|
1199
|
+
]);
|
|
1186
1200
|
if (codexWriteActions.has(name)) return 'codex';
|
|
1187
1201
|
if (claudeWriteActions.has(name)) return 'claude';
|
|
1188
1202
|
if (opencodeWriteActions.has(name)) return 'opencode';
|
|
1203
|
+
if (kilocodeWriteActions.has(name)) return 'kilocode';
|
|
1189
1204
|
return '';
|
|
1190
1205
|
}
|
|
1191
1206
|
|
|
@@ -10169,6 +10184,347 @@ function cmdUpdate(name, baseUrl, apiKey, silent = false, options = {}) {
|
|
|
10169
10184
|
}
|
|
10170
10185
|
}
|
|
10171
10186
|
|
|
10187
|
+
function stripJsoncComments(input) {
|
|
10188
|
+
const text = String(input || '');
|
|
10189
|
+
let output = '';
|
|
10190
|
+
let inString = false;
|
|
10191
|
+
let quote = '';
|
|
10192
|
+
let escaped = false;
|
|
10193
|
+
for (let i = 0; i < text.length; i++) {
|
|
10194
|
+
const ch = text[i];
|
|
10195
|
+
const next = text[i + 1];
|
|
10196
|
+
if (inString) {
|
|
10197
|
+
output += ch;
|
|
10198
|
+
if (escaped) {
|
|
10199
|
+
escaped = false;
|
|
10200
|
+
} else if (ch === '\\') {
|
|
10201
|
+
escaped = true;
|
|
10202
|
+
} else if (ch === quote) {
|
|
10203
|
+
inString = false;
|
|
10204
|
+
quote = '';
|
|
10205
|
+
}
|
|
10206
|
+
continue;
|
|
10207
|
+
}
|
|
10208
|
+
if (ch === '"' || ch === "'") {
|
|
10209
|
+
inString = true;
|
|
10210
|
+
quote = ch;
|
|
10211
|
+
output += ch;
|
|
10212
|
+
continue;
|
|
10213
|
+
}
|
|
10214
|
+
if (ch === '/' && next === '/') {
|
|
10215
|
+
while (i < text.length && text[i] !== '\n') i++;
|
|
10216
|
+
output += '\n';
|
|
10217
|
+
continue;
|
|
10218
|
+
}
|
|
10219
|
+
if (ch === '/' && next === '*') {
|
|
10220
|
+
i += 2;
|
|
10221
|
+
while (i < text.length && !(text[i] === '*' && text[i + 1] === '/')) i++;
|
|
10222
|
+
i += 1;
|
|
10223
|
+
continue;
|
|
10224
|
+
}
|
|
10225
|
+
output += ch;
|
|
10226
|
+
}
|
|
10227
|
+
return output.replace(/,\s*([}\]])/g, '$1');
|
|
10228
|
+
}
|
|
10229
|
+
|
|
10230
|
+
function readKilocodeGlobalConfig() {
|
|
10231
|
+
const candidates = [KILOCODE_GLOBAL_JSONC_CONFIG_FILE, KILOCODE_GLOBAL_JSON_CONFIG_FILE];
|
|
10232
|
+
const filePath = candidates.find((candidate) => fs.existsSync(candidate)) || KILOCODE_GLOBAL_JSONC_CONFIG_FILE;
|
|
10233
|
+
if (!fs.existsSync(filePath)) {
|
|
10234
|
+
return { filePath, config: {} };
|
|
10235
|
+
}
|
|
10236
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
10237
|
+
if (!raw.trim()) {
|
|
10238
|
+
return { filePath, config: {} };
|
|
10239
|
+
}
|
|
10240
|
+
try {
|
|
10241
|
+
const parsed = JSON.parse(stripJsoncComments(raw));
|
|
10242
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
10243
|
+
throw new Error('配置根节点必须是对象');
|
|
10244
|
+
}
|
|
10245
|
+
return { filePath, config: parsed };
|
|
10246
|
+
} catch (e) {
|
|
10247
|
+
throw new Error(`读取 KiloCode 配置失败: ${e.message}`);
|
|
10248
|
+
}
|
|
10249
|
+
}
|
|
10250
|
+
|
|
10251
|
+
function normalizeKilocodeProviderName(value) {
|
|
10252
|
+
const name = typeof value === 'string' && value.trim() ? value.trim() : 'codexmate';
|
|
10253
|
+
if (!/^[A-Za-z0-9._-]+$/.test(name)) {
|
|
10254
|
+
throw new Error('provider 仅支持字母/数字/._-');
|
|
10255
|
+
}
|
|
10256
|
+
return name;
|
|
10257
|
+
}
|
|
10258
|
+
|
|
10259
|
+
function writeKilocodeProviderConfig(params = {}) {
|
|
10260
|
+
const provider = normalizeKilocodeProviderName(params.provider);
|
|
10261
|
+
const url = normalizeBaseUrl(params.url || '');
|
|
10262
|
+
const incomingKey = typeof params.key === 'string' ? params.key.trim() : '';
|
|
10263
|
+
const model = typeof params.model === 'string' ? params.model.trim() : '';
|
|
10264
|
+
if (!url) throw new Error('URL 必填');
|
|
10265
|
+
if (!model) throw new Error('模型名称必填');
|
|
10266
|
+
if (!isValidHttpUrl(url)) throw new Error('URL 仅支持 http/https');
|
|
10267
|
+
|
|
10268
|
+
const { filePath, config } = readKilocodeGlobalConfig();
|
|
10269
|
+
const existingProvider = config.provider
|
|
10270
|
+
&& typeof config.provider === 'object'
|
|
10271
|
+
&& !Array.isArray(config.provider)
|
|
10272
|
+
&& config.provider[provider]
|
|
10273
|
+
&& typeof config.provider[provider] === 'object'
|
|
10274
|
+
&& !Array.isArray(config.provider[provider])
|
|
10275
|
+
? config.provider[provider]
|
|
10276
|
+
: {};
|
|
10277
|
+
const existingOptions = existingProvider.options && typeof existingProvider.options === 'object' && !Array.isArray(existingProvider.options)
|
|
10278
|
+
? existingProvider.options
|
|
10279
|
+
: {};
|
|
10280
|
+
const existingKey = typeof existingOptions.apiKey === 'string' ? existingOptions.apiKey.trim() : '';
|
|
10281
|
+
const key = incomingKey || existingKey;
|
|
10282
|
+
if (!key) throw new Error('API Key 必填');
|
|
10283
|
+
|
|
10284
|
+
const next = { ...config };
|
|
10285
|
+
if (!next.$schema) {
|
|
10286
|
+
next.$schema = 'https://app.kilo.ai/config.json';
|
|
10287
|
+
}
|
|
10288
|
+
next.provider = next.provider && typeof next.provider === 'object' && !Array.isArray(next.provider)
|
|
10289
|
+
? { ...next.provider }
|
|
10290
|
+
: {};
|
|
10291
|
+
next.provider[provider] = {
|
|
10292
|
+
...(next.provider[provider] && typeof next.provider[provider] === 'object' && !Array.isArray(next.provider[provider])
|
|
10293
|
+
? next.provider[provider]
|
|
10294
|
+
: {}),
|
|
10295
|
+
name: provider,
|
|
10296
|
+
npm: '@ai-sdk/openai-compatible',
|
|
10297
|
+
api: url,
|
|
10298
|
+
env: [],
|
|
10299
|
+
models: {
|
|
10300
|
+
[model]: {
|
|
10301
|
+
name: model,
|
|
10302
|
+
tool_call: true
|
|
10303
|
+
}
|
|
10304
|
+
},
|
|
10305
|
+
options: {
|
|
10306
|
+
...(next.provider[provider]
|
|
10307
|
+
&& typeof next.provider[provider] === 'object'
|
|
10308
|
+
&& next.provider[provider].options
|
|
10309
|
+
&& typeof next.provider[provider].options === 'object'
|
|
10310
|
+
&& !Array.isArray(next.provider[provider].options)
|
|
10311
|
+
? next.provider[provider].options
|
|
10312
|
+
: {}),
|
|
10313
|
+
apiKey: key,
|
|
10314
|
+
baseURL: url
|
|
10315
|
+
}
|
|
10316
|
+
};
|
|
10317
|
+
next.model = `${provider}/${model}`;
|
|
10318
|
+
const enabled = Array.isArray(next.enabled_providers) ? next.enabled_providers.filter((item) => typeof item === 'string' && item.trim()) : [];
|
|
10319
|
+
next.enabled_providers = enabled.includes(provider) ? enabled : [...enabled, provider];
|
|
10320
|
+
|
|
10321
|
+
ensureDir(path.dirname(filePath));
|
|
10322
|
+
fs.writeFileSync(filePath, JSON.stringify(next, null, 2) + '\n', 'utf-8');
|
|
10323
|
+
return { filePath, provider, model, url };
|
|
10324
|
+
}
|
|
10325
|
+
|
|
10326
|
+
function parseKilocodeCommandArgs(argv = []) {
|
|
10327
|
+
const options = { provider: 'codexmate', command: '' };
|
|
10328
|
+
const positional = [];
|
|
10329
|
+
let passthrough = [];
|
|
10330
|
+
for (let i = 0; i < argv.length; i++) {
|
|
10331
|
+
const token = String(argv[i] || '');
|
|
10332
|
+
if (token === '--') {
|
|
10333
|
+
passthrough = argv.slice(i + 1).map(item => String(item));
|
|
10334
|
+
break;
|
|
10335
|
+
}
|
|
10336
|
+
if (token === '--provider') {
|
|
10337
|
+
const value = String(argv[i + 1] || '');
|
|
10338
|
+
if (!value || value.startsWith('--')) throw new Error('错误: --provider 需要一个值');
|
|
10339
|
+
options.provider = value;
|
|
10340
|
+
i += 1;
|
|
10341
|
+
continue;
|
|
10342
|
+
}
|
|
10343
|
+
if (token.startsWith('--provider=')) {
|
|
10344
|
+
options.provider = token.slice('--provider='.length);
|
|
10345
|
+
continue;
|
|
10346
|
+
}
|
|
10347
|
+
if (token === '--help' || token === '-h') {
|
|
10348
|
+
options.help = true;
|
|
10349
|
+
continue;
|
|
10350
|
+
}
|
|
10351
|
+
positional.push(token);
|
|
10352
|
+
}
|
|
10353
|
+
if (positional[0] === 'config' || positional[0] === 'setup') {
|
|
10354
|
+
options.command = positional.shift();
|
|
10355
|
+
}
|
|
10356
|
+
return {
|
|
10357
|
+
command: options.command,
|
|
10358
|
+
url: positional[0],
|
|
10359
|
+
key: positional[1],
|
|
10360
|
+
model: positional[2],
|
|
10361
|
+
provider: options.provider,
|
|
10362
|
+
help: !!options.help,
|
|
10363
|
+
passthrough: passthrough.length ? passthrough : positional.slice(options.command ? 3 : 0)
|
|
10364
|
+
};
|
|
10365
|
+
}
|
|
10366
|
+
|
|
10367
|
+
function printKilocodeUsage() {
|
|
10368
|
+
console.log('\n用法:');
|
|
10369
|
+
console.log(' codexmate kilo [KiloCode参数...]');
|
|
10370
|
+
console.log(' codexmate kilo <URL> <API密钥> <模型> [--provider <id>] [-- KiloCode参数...]');
|
|
10371
|
+
console.log(' codexmate kilo config <URL> <API密钥> <模型> [--provider <id>]');
|
|
10372
|
+
console.log('\n说明:');
|
|
10373
|
+
console.log(' codexmate kilo 默认启动 KiloCode;带 URL/API密钥/模型时会先写入 KiloCode provider 配置再启动。');
|
|
10374
|
+
console.log(' 纯配置请使用 codexmate kilo config ...');
|
|
10375
|
+
console.log(' 配置写入 ~/.config/kilo/kilo.jsonc(或已存在的 kilo.json)。');
|
|
10376
|
+
}
|
|
10377
|
+
|
|
10378
|
+
function resolveKilocodeBinary() {
|
|
10379
|
+
for (const bin of ['kilo', 'kilocode']) {
|
|
10380
|
+
if (commandExists(bin, '--version')) return bin;
|
|
10381
|
+
}
|
|
10382
|
+
return '';
|
|
10383
|
+
}
|
|
10384
|
+
|
|
10385
|
+
function runKilocodeCommand(args = [], options = {}) {
|
|
10386
|
+
const bin = resolveKilocodeBinary();
|
|
10387
|
+
if (!bin) {
|
|
10388
|
+
throw new Error('无法启动 KiloCode,请确认已安装 KiloCode CLI,并且 kilo 或 kilocode 在 PATH 中。');
|
|
10389
|
+
}
|
|
10390
|
+
const finalArgs = Array.isArray(args) ? args.filter(item => item !== undefined).map(item => String(item)) : [];
|
|
10391
|
+
if (options.detached === true) {
|
|
10392
|
+
const child = spawn(bin, finalArgs, {
|
|
10393
|
+
detached: true,
|
|
10394
|
+
stdio: 'ignore',
|
|
10395
|
+
windowsHide: true
|
|
10396
|
+
});
|
|
10397
|
+
child.unref();
|
|
10398
|
+
return { success: true, bin, args: finalArgs, pid: child.pid };
|
|
10399
|
+
}
|
|
10400
|
+
return new Promise((resolve, reject) => {
|
|
10401
|
+
const child = spawn(bin, finalArgs, {
|
|
10402
|
+
stdio: 'inherit',
|
|
10403
|
+
windowsHide: false
|
|
10404
|
+
});
|
|
10405
|
+
child.on('error', reject);
|
|
10406
|
+
child.on('exit', (code, signal) => {
|
|
10407
|
+
if (signal) {
|
|
10408
|
+
reject(new Error(`KiloCode 已终止: ${signal}`));
|
|
10409
|
+
return;
|
|
10410
|
+
}
|
|
10411
|
+
resolve(code || 0);
|
|
10412
|
+
});
|
|
10413
|
+
});
|
|
10414
|
+
}
|
|
10415
|
+
|
|
10416
|
+
async function cmdKilocode(argv = []) {
|
|
10417
|
+
const parsed = parseKilocodeCommandArgs(argv);
|
|
10418
|
+
if (parsed.help) {
|
|
10419
|
+
printKilocodeUsage();
|
|
10420
|
+
return 0;
|
|
10421
|
+
}
|
|
10422
|
+
if (parsed.command === 'config') {
|
|
10423
|
+
if (!parsed.url || !parsed.key || !parsed.model) {
|
|
10424
|
+
printKilocodeUsage();
|
|
10425
|
+
throw new Error('URL、API密钥和模型名称必填');
|
|
10426
|
+
}
|
|
10427
|
+
const result = writeKilocodeProviderConfig(parsed);
|
|
10428
|
+
console.log('✓ 已写入 KiloCode 配置');
|
|
10429
|
+
console.log(' 文件:', result.filePath);
|
|
10430
|
+
console.log(' provider:', result.provider);
|
|
10431
|
+
console.log(' URL:', result.url);
|
|
10432
|
+
console.log(' 模型:', `${result.provider}/${result.model}`);
|
|
10433
|
+
console.log();
|
|
10434
|
+
return 0;
|
|
10435
|
+
}
|
|
10436
|
+
let passthrough = parsed.passthrough;
|
|
10437
|
+
if (parsed.url && parsed.key && parsed.model) {
|
|
10438
|
+
const result = writeKilocodeProviderConfig(parsed);
|
|
10439
|
+
console.log('✓ 已写入 KiloCode 配置,正在启动 KiloCode');
|
|
10440
|
+
console.log(' 文件:', result.filePath);
|
|
10441
|
+
console.log(' 模型:', `${result.provider}/${result.model}`);
|
|
10442
|
+
console.log();
|
|
10443
|
+
passthrough = parsed.passthrough;
|
|
10444
|
+
} else if (parsed.url || parsed.key || parsed.model) {
|
|
10445
|
+
passthrough = [parsed.url, parsed.key, parsed.model, ...parsed.passthrough].filter(item => item !== undefined && item !== '');
|
|
10446
|
+
}
|
|
10447
|
+
return runKilocodeCommand(passthrough);
|
|
10448
|
+
}
|
|
10449
|
+
|
|
10450
|
+
function summarizeKilocodeConfig(config = {}, targetPath = KILOCODE_GLOBAL_JSONC_CONFIG_FILE, exists = false) {
|
|
10451
|
+
const providers = getRecord(config.provider);
|
|
10452
|
+
const modelRef = typeof config.model === 'string' ? config.model.trim() : '';
|
|
10453
|
+
const slash = modelRef.indexOf('/');
|
|
10454
|
+
const currentProvider = slash > 0 ? modelRef.slice(0, slash) : '';
|
|
10455
|
+
const currentModel = slash > 0 ? modelRef.slice(slash + 1) : modelRef;
|
|
10456
|
+
const providerNames = [...new Set([...Object.keys(providers), currentProvider].filter(Boolean))];
|
|
10457
|
+
const redactedConfig = JSON.parse(JSON.stringify(config && typeof config === 'object' ? config : {}));
|
|
10458
|
+
const redactedProviders = getRecord(redactedConfig.provider);
|
|
10459
|
+
for (const provider of Object.values(redactedProviders)) {
|
|
10460
|
+
const options = getRecord(provider && provider.options);
|
|
10461
|
+
if (typeof options.apiKey === 'string' && options.apiKey) {
|
|
10462
|
+
options.apiKey = maskKey(options.apiKey);
|
|
10463
|
+
}
|
|
10464
|
+
}
|
|
10465
|
+
return {
|
|
10466
|
+
exists: !!exists,
|
|
10467
|
+
targetPath,
|
|
10468
|
+
currentProvider,
|
|
10469
|
+
currentModel,
|
|
10470
|
+
providers: providerNames.map((name) => {
|
|
10471
|
+
const provider = getRecord(providers[name]);
|
|
10472
|
+
const options = getRecord(provider.options);
|
|
10473
|
+
const apiKey = typeof options.apiKey === 'string' ? options.apiKey : '';
|
|
10474
|
+
const modelNames = Object.keys(getRecord(provider.models));
|
|
10475
|
+
return {
|
|
10476
|
+
name,
|
|
10477
|
+
api: typeof provider.api === 'string' ? provider.api : '',
|
|
10478
|
+
baseURL: typeof options.baseURL === 'string' ? options.baseURL : '',
|
|
10479
|
+
hasKey: apiKey.trim().length > 0,
|
|
10480
|
+
apiKey: maskKey(apiKey),
|
|
10481
|
+
models: modelNames
|
|
10482
|
+
};
|
|
10483
|
+
}),
|
|
10484
|
+
content: JSON.stringify(redactedConfig, null, 2) + '\n',
|
|
10485
|
+
redacted: true
|
|
10486
|
+
};
|
|
10487
|
+
}
|
|
10488
|
+
|
|
10489
|
+
function readKilocodeConfigInfo() {
|
|
10490
|
+
try {
|
|
10491
|
+
const { filePath, config } = readKilocodeGlobalConfig();
|
|
10492
|
+
return summarizeKilocodeConfig(config, filePath, fs.existsSync(filePath));
|
|
10493
|
+
} catch (e) {
|
|
10494
|
+
return { error: e.message || '读取 KiloCode 配置失败', targetPath: KILOCODE_GLOBAL_JSONC_CONFIG_FILE };
|
|
10495
|
+
}
|
|
10496
|
+
}
|
|
10497
|
+
|
|
10498
|
+
function applyKilocodeConfig(params = {}) {
|
|
10499
|
+
assertToolConfigWriteAllowed('kilocode');
|
|
10500
|
+
try {
|
|
10501
|
+
const result = writeKilocodeProviderConfig({
|
|
10502
|
+
provider: params.provider,
|
|
10503
|
+
url: params.url,
|
|
10504
|
+
key: params.apiKey,
|
|
10505
|
+
model: params.model
|
|
10506
|
+
});
|
|
10507
|
+
const info = readKilocodeConfigInfo();
|
|
10508
|
+
return { success: true, ...result, ...info };
|
|
10509
|
+
} catch (e) {
|
|
10510
|
+
return { error: e.message || '写入 KiloCode 配置失败' };
|
|
10511
|
+
}
|
|
10512
|
+
}
|
|
10513
|
+
|
|
10514
|
+
function startKilocodeFromWeb(params = {}) {
|
|
10515
|
+
assertToolConfigWriteAllowed('kilocode');
|
|
10516
|
+
try {
|
|
10517
|
+
if (params && params.configure === true) {
|
|
10518
|
+
const saved = applyKilocodeConfig(params);
|
|
10519
|
+
if (saved && saved.error) return saved;
|
|
10520
|
+
}
|
|
10521
|
+
const args = Array.isArray(params.args) ? params.args.map(item => String(item)) : [];
|
|
10522
|
+
return runKilocodeCommand(args, { detached: true });
|
|
10523
|
+
} catch (e) {
|
|
10524
|
+
return { error: e.message || '启动 KiloCode 失败' };
|
|
10525
|
+
}
|
|
10526
|
+
}
|
|
10527
|
+
|
|
10172
10528
|
// 添加模型
|
|
10173
10529
|
function cmdAddModel(modelName, silent = false) {
|
|
10174
10530
|
if (!modelName) {
|
|
@@ -12776,12 +13132,21 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
|
|
|
12776
13132
|
case 'get-opencode-config':
|
|
12777
13133
|
result = readOpencodeConfigInfo();
|
|
12778
13134
|
break;
|
|
13135
|
+
case 'get-kilocode-config':
|
|
13136
|
+
result = readKilocodeConfigInfo();
|
|
13137
|
+
break;
|
|
12779
13138
|
case 'apply-opencode-config':
|
|
12780
13139
|
result = applyOpencodeConfigRaw(params || {});
|
|
12781
13140
|
break;
|
|
12782
13141
|
case 'update-opencode-selection':
|
|
12783
13142
|
result = updateOpencodeSelection(params || {});
|
|
12784
13143
|
break;
|
|
13144
|
+
case 'apply-kilocode-config':
|
|
13145
|
+
result = applyKilocodeConfig(params || {});
|
|
13146
|
+
break;
|
|
13147
|
+
case 'start-kilocode':
|
|
13148
|
+
result = startKilocodeFromWeb(params || {});
|
|
13149
|
+
break;
|
|
12785
13150
|
case 'apply-claude-config':
|
|
12786
13151
|
result = await applyToClaudeSettings(params.config);
|
|
12787
13152
|
if (result && !result.error) {
|
|
@@ -18322,6 +18687,7 @@ function printMainHelp() {
|
|
|
18322
18687
|
console.log(' codexmate delete <名称> 删除提供商');
|
|
18323
18688
|
console.log(' codexmate claude 等同于 claude --dangerously-skip-permissions');
|
|
18324
18689
|
console.log(' codexmate claude <BaseURL> <API密钥> [模型] [--target-api responses|chat_completions|ollama] 写入 Claude Code 配置');
|
|
18690
|
+
console.log(' codexmate kilo [URL API密钥 模型] [--provider <id>] 配置后启动 KiloCode');
|
|
18325
18691
|
console.log(' codexmate auth <list|import|switch|delete|status> 认证管理');
|
|
18326
18692
|
console.log(' codexmate add-model <模型> 添加模型');
|
|
18327
18693
|
console.log(' codexmate delete-model <模型> 删除模型');
|
|
@@ -18417,6 +18783,11 @@ async function main() {
|
|
|
18417
18783
|
const exitCode = await cmdClaude(args.slice(1));
|
|
18418
18784
|
process.exit(exitCode);
|
|
18419
18785
|
}
|
|
18786
|
+
case 'kilo':
|
|
18787
|
+
case 'kilocode': {
|
|
18788
|
+
const exitCode = await cmdKilocode(args.slice(1));
|
|
18789
|
+
process.exit(exitCode || 0);
|
|
18790
|
+
}
|
|
18420
18791
|
case 'add-model': cmdAddModel(args[1]); break;
|
|
18421
18792
|
case 'delete-model': cmdDeleteModel(args[1]); break;
|
|
18422
18793
|
case 'auth': cmdAuth(args.slice(1)); break;
|
package/package.json
CHANGED
package/web-ui/app.js
CHANGED
|
@@ -290,9 +290,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
290
290
|
appVersionStatusChecked: false,
|
|
291
291
|
appVersionStatusCheckedAt: '',
|
|
292
292
|
appVersionStatusSource: '',
|
|
293
|
-
newProvider: { name: '', url: '', key: '', model: '', useTransform: false
|
|
293
|
+
newProvider: { name: '', url: '', key: '', model: '', useTransform: false },
|
|
294
294
|
resetConfigLoading: false,
|
|
295
|
-
editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false, useTransform: false
|
|
295
|
+
editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false, useTransform: false },
|
|
296
296
|
newModelName: '',
|
|
297
297
|
currentClaudeConfig: '',
|
|
298
298
|
currentClaudeModel: '',
|
|
@@ -396,8 +396,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
396
396
|
providerCacheError: '',
|
|
397
397
|
providerCacheRequestSeq: 0,
|
|
398
398
|
settingsTab: 'general',
|
|
399
|
-
toolConfigPermissions: { codex: false, claude: false, opencode: false },
|
|
400
|
-
toolConfigPermissionSaving: { codex: false, claude: false, opencode: false },
|
|
399
|
+
toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: false },
|
|
400
|
+
toolConfigPermissionSaving: { codex: false, claude: false, opencode: false, kilocode: false },
|
|
401
401
|
sessionTrashEnabled: true,
|
|
402
402
|
sessionTrashItems: [],
|
|
403
403
|
sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE,
|
|
@@ -440,6 +440,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
440
440
|
opencodeAutoCompact: true,
|
|
441
441
|
opencodeMaxTokens: '',
|
|
442
442
|
opencodeReasoningEffort: '',
|
|
443
|
+
kilocodeConfigPath: '',
|
|
444
|
+
kilocodeConfigExists: false,
|
|
445
|
+
kilocodeContent: '{}\n',
|
|
446
|
+
kilocodeLoading: false,
|
|
447
|
+
kilocodeSaving: false,
|
|
448
|
+
kilocodeStarting: false,
|
|
449
|
+
kilocodeError: '',
|
|
450
|
+
kilocodeProviders: [],
|
|
451
|
+
kilocodeProvider: 'codexmate',
|
|
452
|
+
kilocodeBaseUrl: '',
|
|
453
|
+
kilocodeModel: 'gpt-5.3',
|
|
454
|
+
kilocodeApiKey: '',
|
|
455
|
+
kilocodeShowKey: false,
|
|
456
|
+
kilocodeAutoSaveSignature: '',
|
|
443
457
|
forceCompactLayout: false,
|
|
444
458
|
sidebarCollapsed: false,
|
|
445
459
|
sessionLoadNativeDialog: false,
|
package/web-ui/index.html
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
<!-- @include ./partials/index/panel-config-claude.html -->
|
|
18
18
|
<!-- @include ./partials/index/panel-config-openclaw.html -->
|
|
19
19
|
<!-- @include ./partials/index/panel-config-opencode.html -->
|
|
20
|
+
<!-- @include ./partials/index/panel-config-kilocode.html -->
|
|
20
21
|
<!-- @include ./partials/index/panel-sessions.html -->
|
|
21
22
|
<!-- @include ./partials/index/panel-usage.html -->
|
|
22
23
|
<!-- @include ./partials/index/panel-orchestration.html -->
|
|
@@ -149,6 +149,16 @@ export function createDashboardComputed() {
|
|
|
149
149
|
version: '',
|
|
150
150
|
commandPath: '',
|
|
151
151
|
error: ''
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: 'kilocode',
|
|
155
|
+
name: 'KiloCode CLI',
|
|
156
|
+
packageName: '@kilocode/cli',
|
|
157
|
+
installed: false,
|
|
158
|
+
bin: 'kilo',
|
|
159
|
+
version: '',
|
|
160
|
+
commandPath: '',
|
|
161
|
+
error: ''
|
|
152
162
|
}
|
|
153
163
|
];
|
|
154
164
|
const action = this.normalizeInstallAction(this.installCommandAction);
|
|
@@ -164,6 +174,10 @@ export function createDashboardComputed() {
|
|
|
164
174
|
};
|
|
165
175
|
});
|
|
166
176
|
},
|
|
177
|
+
currentInstalledCommandCards() {
|
|
178
|
+
const targets = Array.isArray(this.installTargetCards) ? this.installTargetCards : [];
|
|
179
|
+
return targets.filter((target) => target && target.installed === true);
|
|
180
|
+
},
|
|
167
181
|
installRegistryPreview() {
|
|
168
182
|
return this.resolveInstallRegistryUrl(this.installRegistryPreset, this.installRegistryCustom);
|
|
169
183
|
},
|
|
@@ -206,13 +206,13 @@ export function createAgentsMethods(options = {}) {
|
|
|
206
206
|
this.agentsContext = 'openclaw-workspace';
|
|
207
207
|
this.agentsWorkspaceFileName = fileName;
|
|
208
208
|
this.agentsModalTitle = tr('modal.agents.title.openclawWorkspaceFile', `OpenClaw 工作区文件: ${fileName}`, { fileName });
|
|
209
|
-
this.agentsModalHint = tr('modal.agents.hint.openclawWorkspaceFile',
|
|
209
|
+
this.agentsModalHint = tr('modal.agents.hint.openclawWorkspaceFile', `Workspace / ${fileName}`, { fileName });
|
|
210
210
|
return;
|
|
211
211
|
}
|
|
212
212
|
this.agentsContext = context === 'openclaw' ? 'openclaw' : 'codex';
|
|
213
213
|
if (this.agentsContext === 'openclaw') {
|
|
214
214
|
this.agentsModalTitle = tr('modal.agents.title.openclaw', 'OpenClaw AGENTS.md 编辑器');
|
|
215
|
-
this.agentsModalHint = tr('modal.agents.hint.openclaw', '
|
|
215
|
+
this.agentsModalHint = tr('modal.agents.hint.openclaw', 'Workspace / AGENTS.md');
|
|
216
216
|
} else {
|
|
217
217
|
this.agentsModalTitle = tr('modal.agents.title.default', 'AGENTS.md 编辑器');
|
|
218
218
|
this.agentsModalHint = tr('modal.agents.hint.default', '保存后会写入目标 AGENTS.md(与 config.toml 同级)。');
|
|
@@ -20,6 +20,7 @@ import { createOpenclawCoreMethods } from './app.methods.openclaw-core.mjs';
|
|
|
20
20
|
import { createOpenclawEditingMethods } from './app.methods.openclaw-editing.mjs';
|
|
21
21
|
import { createOpenclawPersistMethods } from './app.methods.openclaw-persist.mjs';
|
|
22
22
|
import { createOpencodeConfigMethods } from './app.methods.opencode-config.mjs';
|
|
23
|
+
import { createKilocodeConfigMethods } from './app.methods.kilocode-config.mjs';
|
|
23
24
|
import { createProvidersMethods } from './app.methods.providers.mjs';
|
|
24
25
|
import { createProviderCacheMethods } from './app.methods.provider-cache.mjs';
|
|
25
26
|
import { createRuntimeMethods } from './app.methods.runtime.mjs';
|
|
@@ -99,6 +100,7 @@ export function createAppMethods() {
|
|
|
99
100
|
api,
|
|
100
101
|
modelCatalog: OPENCODE_MODEL_CATALOG
|
|
101
102
|
}),
|
|
103
|
+
...createKilocodeConfigMethods({ api }),
|
|
102
104
|
...createInstallMethods({ api }),
|
|
103
105
|
...createRuntimeMethods({ api }),
|
|
104
106
|
...createTaskOrchestrationMethods({ api })
|
|
@@ -246,6 +246,11 @@ export function createInstallMethods(options = {}) {
|
|
|
246
246
|
install: '',
|
|
247
247
|
update: '',
|
|
248
248
|
uninstall: ''
|
|
249
|
+
},
|
|
250
|
+
kilocode: {
|
|
251
|
+
install: '',
|
|
252
|
+
update: '',
|
|
253
|
+
uninstall: ''
|
|
249
254
|
}
|
|
250
255
|
};
|
|
251
256
|
if (manager === 'pnpm') {
|
|
@@ -261,6 +266,9 @@ export function createInstallMethods(options = {}) {
|
|
|
261
266
|
matrix.codex.install = `pnpm add -g ${codexInstallPackage}`;
|
|
262
267
|
matrix.codex.update = `pnpm up -g ${codexPackage}`;
|
|
263
268
|
matrix.codex.uninstall = `pnpm remove -g ${codexPackage}`;
|
|
269
|
+
matrix.kilocode.install = 'pnpm add -g @kilocode/cli';
|
|
270
|
+
matrix.kilocode.update = 'pnpm up -g @kilocode/cli';
|
|
271
|
+
matrix.kilocode.uninstall = 'pnpm remove -g @kilocode/cli';
|
|
264
272
|
return matrix;
|
|
265
273
|
}
|
|
266
274
|
if (manager === 'bun') {
|
|
@@ -276,6 +284,9 @@ export function createInstallMethods(options = {}) {
|
|
|
276
284
|
matrix.codex.install = `bun add -g ${codexInstallPackage}`;
|
|
277
285
|
matrix.codex.update = `bun update -g ${codexPackage}`;
|
|
278
286
|
matrix.codex.uninstall = `bun remove -g ${codexPackage}`;
|
|
287
|
+
matrix.kilocode.install = 'bun add -g @kilocode/cli';
|
|
288
|
+
matrix.kilocode.update = 'bun update -g @kilocode/cli';
|
|
289
|
+
matrix.kilocode.uninstall = 'bun remove -g @kilocode/cli';
|
|
279
290
|
return matrix;
|
|
280
291
|
}
|
|
281
292
|
matrix.claude.install = 'npm install -g @anthropic-ai/claude-code';
|
|
@@ -292,6 +303,9 @@ export function createInstallMethods(options = {}) {
|
|
|
292
303
|
? `npm install -g ${codexInstallPackage}`
|
|
293
304
|
: `npm update -g ${codexPackage}`;
|
|
294
305
|
matrix.codex.uninstall = `npm uninstall -g ${codexPackage}`;
|
|
306
|
+
matrix.kilocode.install = 'npm install -g @kilocode/cli';
|
|
307
|
+
matrix.kilocode.update = 'npm update -g @kilocode/cli';
|
|
308
|
+
matrix.kilocode.uninstall = 'npm uninstall -g @kilocode/cli';
|
|
295
309
|
return matrix;
|
|
296
310
|
},
|
|
297
311
|
|