@cloudbase/cli 2.8.0-beta.3 → 2.8.0-beta.5
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 +13 -1
- package/bin/cloudbase-mcp.js +24 -0
- package/cloudbaserc.json +3 -0
- package/lib/commands/ai/index.js +1 -1
- package/lib/utils/ai/config.js +29 -3
- package/lib/utils/ai/const.js +46 -3
- package/lib/utils/ai/router.js +304 -27
- package/lib/utils/ai/setup.js +199 -68
- package/package.json +4 -2
- package/specs/mcp-global-bin/design.md +57 -0
- package/specs/mcp-global-bin/requirements.md +43 -0
- package/specs/mcp-global-bin/tasks.md +54 -0
- package/types/utils/ai/config.d.ts +12 -1
- package/types/utils/ai/const.d.ts +85 -0
- package/types/utils/ai/router.d.ts +7 -0
- package/types/utils/ai/setup.d.ts +2 -0
- package/.claude/settings.local.json +0 -6
package/lib/utils/ai/setup.js
CHANGED
|
@@ -149,13 +149,13 @@ class AISetupWizard {
|
|
|
149
149
|
}
|
|
150
150
|
if (configuredAgents.length === 1) {
|
|
151
151
|
const selectedAgent = configuredAgents[0];
|
|
152
|
-
const agentInfo = [const_1.CLAUDE, const_1.QWEN, const_1.CODEX].find((a) => a.value === selectedAgent);
|
|
152
|
+
const agentInfo = [const_1.CLAUDE, const_1.QWEN, const_1.CODEX, const_1.AIDER].find((a) => a.value === selectedAgent);
|
|
153
153
|
const agentName = (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.name) || selectedAgent.toUpperCase();
|
|
154
154
|
log.info(`🔧 自动选择已配置的唯一 AI 工具: ${agentName}`);
|
|
155
155
|
return selectedAgent;
|
|
156
156
|
}
|
|
157
157
|
const availableChoices = configuredAgents.map((agent) => {
|
|
158
|
-
const agentInfo = [const_1.CLAUDE, const_1.QWEN, const_1.CODEX].find((a) => a.value === agent);
|
|
158
|
+
const agentInfo = [const_1.CLAUDE, const_1.QWEN, const_1.CODEX, const_1.AIDER].find((a) => a.value === agent);
|
|
159
159
|
return agentInfo || { name: agent.toUpperCase(), value: agent };
|
|
160
160
|
});
|
|
161
161
|
const { agent } = yield inquirer_1.default.prompt([
|
|
@@ -182,7 +182,7 @@ class AISetupWizard {
|
|
|
182
182
|
type: 'list',
|
|
183
183
|
name: 'agent',
|
|
184
184
|
message,
|
|
185
|
-
choices: [const_1.CLAUDE, const_1.QWEN, const_1.CODEX, ...(includeNone ? [const_1.NONE] : [])],
|
|
185
|
+
choices: [const_1.CLAUDE, const_1.QWEN, const_1.CODEX, const_1.AIDER, ...(includeNone ? [const_1.NONE] : [])],
|
|
186
186
|
default: const_1.CLAUDE.value
|
|
187
187
|
}
|
|
188
188
|
]);
|
|
@@ -199,6 +199,8 @@ class AISetupWizard {
|
|
|
199
199
|
return yield this.configureQwenAgent(log);
|
|
200
200
|
case const_1.CODEX.value:
|
|
201
201
|
return yield this.configureCodexAgent(log);
|
|
202
|
+
case const_1.AIDER.value:
|
|
203
|
+
return yield this.configureAiderAgent(log);
|
|
202
204
|
default:
|
|
203
205
|
throw new Error(`不支持的 AI 工具: ${agent}`);
|
|
204
206
|
}
|
|
@@ -214,47 +216,47 @@ class AISetupWizard {
|
|
|
214
216
|
message: '选择配置方式:',
|
|
215
217
|
choices: [
|
|
216
218
|
{ name: '使用 CloudBase 服务,一键登录,无需配置', value: 'cloudbase' },
|
|
217
|
-
{ name: '自配置 API KEY 和
|
|
219
|
+
{ name: '自配置 API KEY 和 Base URL', value: 'custom' }
|
|
218
220
|
],
|
|
219
221
|
default: 'cloudbase'
|
|
220
222
|
}
|
|
221
223
|
]);
|
|
222
224
|
if (configMethod === 'cloudbase') {
|
|
223
225
|
yield this.configureEnvId(log, this.envId);
|
|
224
|
-
const { provider, model, transformer } = yield
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
name: 'provider',
|
|
228
|
-
message: '大模型供应商(留空使用默认):',
|
|
229
|
-
default: 'deepseek'
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
type: 'input',
|
|
233
|
-
name: 'model',
|
|
234
|
-
message: '模型名称(留空使用默认):',
|
|
235
|
-
default: 'deepseek-v3'
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
type: 'input',
|
|
239
|
-
name: 'transformer',
|
|
240
|
-
message: 'Transformer 名称(留空使用默认):',
|
|
241
|
-
default: 'deepseek'
|
|
242
|
-
}
|
|
243
|
-
]);
|
|
244
|
-
yield this.aiConfigManager.updateClaudeConfig('cloudbase', {
|
|
245
|
-
provider,
|
|
246
|
-
model,
|
|
247
|
-
transformer
|
|
248
|
-
});
|
|
226
|
+
const { provider, model, transformer } = yield this.selectCloudBaseProvider();
|
|
227
|
+
yield this.aiConfigManager.updateClaudeConfig('cloudbase', Object.assign({ provider,
|
|
228
|
+
model }, (transformer && { transformer })));
|
|
249
229
|
}
|
|
250
230
|
else {
|
|
251
|
-
const {
|
|
231
|
+
const { baseUrlChoice } = yield inquirer_1.default.prompt([
|
|
252
232
|
{
|
|
253
|
-
type: '
|
|
254
|
-
name: '
|
|
255
|
-
message: 'API Base URL
|
|
233
|
+
type: 'list',
|
|
234
|
+
name: 'baseUrlChoice',
|
|
235
|
+
message: '选择 API Base URL:',
|
|
236
|
+
choices: [
|
|
237
|
+
{ name: 'Kimi - https://api.moonshot.cn/anthropic', value: 'https://api.moonshot.cn/anthropic' },
|
|
238
|
+
{ name: '智谱 - https://open.bigmodel.cn/api/anthropic', value: 'https://open.bigmodel.cn/api/anthropic' },
|
|
239
|
+
{ name: '🛠️ 自定义 URL', value: 'custom' }
|
|
240
|
+
],
|
|
256
241
|
default: 'https://api.moonshot.cn/anthropic'
|
|
257
|
-
}
|
|
242
|
+
}
|
|
243
|
+
]);
|
|
244
|
+
let baseUrl;
|
|
245
|
+
if (baseUrlChoice === 'custom') {
|
|
246
|
+
const { customUrl } = yield inquirer_1.default.prompt([
|
|
247
|
+
{
|
|
248
|
+
type: 'input',
|
|
249
|
+
name: 'customUrl',
|
|
250
|
+
message: '请输入自定义 API Base URL:',
|
|
251
|
+
validate: (input) => input.trim().length > 0 || '请输入有效的 Base URL'
|
|
252
|
+
}
|
|
253
|
+
]);
|
|
254
|
+
baseUrl = customUrl;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
baseUrl = baseUrlChoice;
|
|
258
|
+
}
|
|
259
|
+
const { apikey } = yield inquirer_1.default.prompt([
|
|
258
260
|
{
|
|
259
261
|
type: 'password',
|
|
260
262
|
name: 'apikey',
|
|
@@ -276,27 +278,14 @@ class AISetupWizard {
|
|
|
276
278
|
message: '选择配置方式:',
|
|
277
279
|
choices: [
|
|
278
280
|
{ name: '使用 CloudBase 服务,一键登录,无需配置', value: 'cloudbase' },
|
|
279
|
-
{ name: '自配置 API KEY 和
|
|
281
|
+
{ name: '自配置 API KEY 和 Base URL', value: 'custom' }
|
|
280
282
|
],
|
|
281
283
|
default: 'cloudbase'
|
|
282
284
|
}
|
|
283
285
|
]);
|
|
284
286
|
if (configMethod === 'cloudbase') {
|
|
285
287
|
yield this.configureEnvId(log, this.envId);
|
|
286
|
-
const { provider, model } = yield
|
|
287
|
-
{
|
|
288
|
-
type: 'input',
|
|
289
|
-
name: 'provider',
|
|
290
|
-
message: '大模型供应商(留空使用默认):',
|
|
291
|
-
default: 'deepseek'
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
type: 'input',
|
|
295
|
-
name: 'model',
|
|
296
|
-
message: '模型名称(留空使用默认):',
|
|
297
|
-
default: 'deepseek-v3'
|
|
298
|
-
}
|
|
299
|
-
]);
|
|
288
|
+
const { provider, model } = yield this.selectCloudBaseProvider();
|
|
300
289
|
yield this.aiConfigManager.updateQwenConfig('cloudbase', {
|
|
301
290
|
provider,
|
|
302
291
|
model
|
|
@@ -337,54 +326,134 @@ class AISetupWizard {
|
|
|
337
326
|
message: '选择配置方式:',
|
|
338
327
|
choices: [
|
|
339
328
|
{ name: '使用 CloudBase 服务,一键登录,无需配置', value: 'cloudbase' },
|
|
340
|
-
{ name: '自配置 API KEY 和
|
|
329
|
+
{ name: '自配置 API KEY 和 Base URL', value: 'custom' }
|
|
341
330
|
],
|
|
342
331
|
default: 'cloudbase'
|
|
343
332
|
}
|
|
344
333
|
]);
|
|
345
334
|
if (configMethod === 'cloudbase') {
|
|
346
335
|
yield this.configureEnvId(log, this.envId);
|
|
347
|
-
const { provider, model } = yield
|
|
336
|
+
const { provider, model } = yield this.selectCloudBaseProvider();
|
|
337
|
+
yield this.aiConfigManager.updateCodexConfig('cloudbase', {
|
|
338
|
+
provider,
|
|
339
|
+
model
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
const { baseUrlChoice } = yield inquirer_1.default.prompt([
|
|
348
344
|
{
|
|
349
|
-
type: '
|
|
350
|
-
name: '
|
|
351
|
-
message: '
|
|
352
|
-
|
|
345
|
+
type: 'list',
|
|
346
|
+
name: 'baseUrlChoice',
|
|
347
|
+
message: '选择 API Base URL:',
|
|
348
|
+
choices: [
|
|
349
|
+
{ name: 'Kimi - https://api.moonshot.cn/v1', value: 'https://api.moonshot.cn/v1' },
|
|
350
|
+
{ name: '智谱 - https://open.bigmodel.cn/api/paas/v4', value: 'https://open.bigmodel.cn/api/paas/v4' },
|
|
351
|
+
{ name: '🛠️ 自定义 URL', value: 'custom' }
|
|
352
|
+
],
|
|
353
|
+
default: 'https://api.moonshot.cn/v1'
|
|
354
|
+
}
|
|
355
|
+
]);
|
|
356
|
+
let baseUrl;
|
|
357
|
+
if (baseUrlChoice === 'custom') {
|
|
358
|
+
const { customUrl } = yield inquirer_1.default.prompt([
|
|
359
|
+
{
|
|
360
|
+
type: 'input',
|
|
361
|
+
name: 'customUrl',
|
|
362
|
+
message: '请输入自定义 API Base URL:',
|
|
363
|
+
validate: (input) => input.trim().length > 0 || '请输入有效的 Base URL'
|
|
364
|
+
}
|
|
365
|
+
]);
|
|
366
|
+
baseUrl = customUrl;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
baseUrl = baseUrlChoice;
|
|
370
|
+
}
|
|
371
|
+
const { apiKey, model } = yield inquirer_1.default.prompt([
|
|
372
|
+
{
|
|
373
|
+
type: 'password',
|
|
374
|
+
name: 'apiKey',
|
|
375
|
+
message: 'API Key:',
|
|
376
|
+
validate: (input) => input.length > 0 || '请输入有效的 API Key'
|
|
353
377
|
},
|
|
354
378
|
{
|
|
355
379
|
type: 'input',
|
|
356
380
|
name: 'model',
|
|
357
|
-
message: '模型名称(留空使用默认):',
|
|
358
|
-
default:
|
|
381
|
+
message: '模型名称 (留空使用默认):',
|
|
382
|
+
default: (0, const_1.getDefaultModelByBaseUrl)(baseUrl)
|
|
359
383
|
}
|
|
360
384
|
]);
|
|
361
|
-
yield this.aiConfigManager.updateCodexConfig('
|
|
385
|
+
yield this.aiConfigManager.updateCodexConfig('custom', { baseUrl, apiKey, model });
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
configureAiderAgent(log) {
|
|
390
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
391
|
+
log.info(`配置说明可参考 ${(0, output_1.genClickableLink)('https://docs.cloudbase.net/cli-v1/ai/aider')}`);
|
|
392
|
+
const { configMethod } = yield inquirer_1.default.prompt([
|
|
393
|
+
{
|
|
394
|
+
type: 'list',
|
|
395
|
+
name: 'configMethod',
|
|
396
|
+
message: '选择配置方式:',
|
|
397
|
+
choices: [
|
|
398
|
+
{ name: '使用 CloudBase 服务,一键登录,无需配置', value: 'cloudbase' },
|
|
399
|
+
{ name: '自配置 API KEY 和 Base URL', value: 'custom' }
|
|
400
|
+
],
|
|
401
|
+
default: 'cloudbase'
|
|
402
|
+
}
|
|
403
|
+
]);
|
|
404
|
+
if (configMethod === 'cloudbase') {
|
|
405
|
+
yield this.configureEnvId(log, this.envId);
|
|
406
|
+
const { provider, model } = yield this.selectCloudBaseProvider();
|
|
407
|
+
yield this.aiConfigManager.updateAiderConfig('cloudbase', {
|
|
362
408
|
provider,
|
|
363
409
|
model
|
|
364
410
|
});
|
|
365
411
|
}
|
|
366
412
|
else {
|
|
367
|
-
const {
|
|
413
|
+
const { baseUrlChoice } = yield inquirer_1.default.prompt([
|
|
368
414
|
{
|
|
369
|
-
type: '
|
|
370
|
-
name: '
|
|
371
|
-
message: 'API Base URL
|
|
372
|
-
|
|
373
|
-
|
|
415
|
+
type: 'list',
|
|
416
|
+
name: 'baseUrlChoice',
|
|
417
|
+
message: '选择 API Base URL:',
|
|
418
|
+
choices: [
|
|
419
|
+
{ name: 'Kimi - https://api.moonshot.cn/v1', value: 'https://api.moonshot.cn/v1' },
|
|
420
|
+
{ name: '智谱 - https://open.bigmodel.cn/api/paas/v4', value: 'https://open.bigmodel.cn/api/paas/v4' },
|
|
421
|
+
{ name: '🛠️ 自定义 URL', value: 'custom' }
|
|
422
|
+
],
|
|
423
|
+
default: 'https://api.moonshot.cn/v1'
|
|
424
|
+
}
|
|
425
|
+
]);
|
|
426
|
+
let baseUrl;
|
|
427
|
+
if (baseUrlChoice === 'custom') {
|
|
428
|
+
const { customUrl } = yield inquirer_1.default.prompt([
|
|
429
|
+
{
|
|
430
|
+
type: 'input',
|
|
431
|
+
name: 'customUrl',
|
|
432
|
+
message: '请输入自定义 API Base URL:',
|
|
433
|
+
validate: (input) => input.trim().length > 0 || '请输入有效的 Base URL'
|
|
434
|
+
}
|
|
435
|
+
]);
|
|
436
|
+
baseUrl = customUrl;
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
baseUrl = baseUrlChoice;
|
|
440
|
+
}
|
|
441
|
+
const { apiKey, model } = yield inquirer_1.default.prompt([
|
|
374
442
|
{
|
|
375
443
|
type: 'password',
|
|
376
444
|
name: 'apiKey',
|
|
377
|
-
message: '
|
|
445
|
+
message: 'API Key:',
|
|
378
446
|
validate: (input) => input.length > 0 || '请输入有效的 API Key'
|
|
379
447
|
},
|
|
380
448
|
{
|
|
381
449
|
type: 'input',
|
|
382
450
|
name: 'model',
|
|
383
|
-
message: '
|
|
384
|
-
default:
|
|
451
|
+
message: '模型名称:',
|
|
452
|
+
default: (0, const_1.getDefaultModelByBaseUrl)(baseUrl),
|
|
453
|
+
validate: (input) => input.length > 0 || '请输入有效的模型名称'
|
|
385
454
|
}
|
|
386
455
|
]);
|
|
387
|
-
yield this.aiConfigManager.
|
|
456
|
+
yield this.aiConfigManager.updateAiderConfig('custom', { baseUrl, apiKey, model });
|
|
388
457
|
}
|
|
389
458
|
});
|
|
390
459
|
}
|
|
@@ -415,5 +484,67 @@ class AISetupWizard {
|
|
|
415
484
|
}
|
|
416
485
|
});
|
|
417
486
|
}
|
|
487
|
+
selectCloudBaseProvider() {
|
|
488
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
489
|
+
const { selectedProvider } = yield inquirer_1.default.prompt([
|
|
490
|
+
{
|
|
491
|
+
type: 'list',
|
|
492
|
+
name: 'selectedProvider',
|
|
493
|
+
message: '选择大模型供应商:',
|
|
494
|
+
choices: const_1.CLOUDBASE_PROVIDERS.map(p => ({ name: p.name, value: p.value })),
|
|
495
|
+
default: 'deepseek'
|
|
496
|
+
}
|
|
497
|
+
]);
|
|
498
|
+
if (selectedProvider === 'custom') {
|
|
499
|
+
const { provider, model } = yield inquirer_1.default.prompt([
|
|
500
|
+
{
|
|
501
|
+
type: 'input',
|
|
502
|
+
name: 'provider',
|
|
503
|
+
message: '请输入自定义供应商名称:',
|
|
504
|
+
validate: (input) => input.trim().length > 0 || '供应商名称不能为空'
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
type: 'input',
|
|
508
|
+
name: 'model',
|
|
509
|
+
message: '请输入模型名称:',
|
|
510
|
+
validate: (input) => input.trim().length > 0 || '模型名称不能为空'
|
|
511
|
+
}
|
|
512
|
+
]);
|
|
513
|
+
return { provider, model, isCustom: true, transformer: undefined };
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
const selectedConfig = const_1.CLOUDBASE_PROVIDERS.find(p => p.value === selectedProvider);
|
|
517
|
+
const modelChoices = [
|
|
518
|
+
...selectedConfig.models.map(m => ({ name: m, value: m })),
|
|
519
|
+
{ name: '🛠️ 自定义', value: 'custom' }
|
|
520
|
+
];
|
|
521
|
+
const { selectedModel } = yield inquirer_1.default.prompt([
|
|
522
|
+
{
|
|
523
|
+
type: 'list',
|
|
524
|
+
name: 'selectedModel',
|
|
525
|
+
message: '选择模型:',
|
|
526
|
+
choices: modelChoices,
|
|
527
|
+
default: selectedConfig.models[0]
|
|
528
|
+
}
|
|
529
|
+
]);
|
|
530
|
+
let model;
|
|
531
|
+
if (selectedModel === 'custom') {
|
|
532
|
+
const { customModel } = yield inquirer_1.default.prompt([
|
|
533
|
+
{
|
|
534
|
+
type: 'input',
|
|
535
|
+
name: 'customModel',
|
|
536
|
+
message: '请输入自定义模型名称:',
|
|
537
|
+
validate: (input) => input.trim().length > 0 || '模型名称不能为空'
|
|
538
|
+
}
|
|
539
|
+
]);
|
|
540
|
+
model = customModel;
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
model = selectedModel;
|
|
544
|
+
}
|
|
545
|
+
return { provider: selectedProvider, model, isCustom: false, transformer: selectedConfig.transformer };
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
418
549
|
}
|
|
419
550
|
exports.AISetupWizard = AISetupWizard;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbase/cli",
|
|
3
|
-
"version": "2.8.0-beta.
|
|
3
|
+
"version": "2.8.0-beta.5",
|
|
4
4
|
"description": "cli tool for cloudbase",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
"bin": {
|
|
23
23
|
"cloudbase": "bin/cloudbase.js",
|
|
24
|
-
"tcb": "bin/tcb.js"
|
|
24
|
+
"tcb": "bin/tcb.js",
|
|
25
|
+
"cloudbase-mcp": "bin/cloudbase-mcp.js"
|
|
25
26
|
},
|
|
26
27
|
"husky": {
|
|
27
28
|
"hooks": {
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
"semver": "^7.3.7",
|
|
71
72
|
"tar-fs": "^2.0.1",
|
|
72
73
|
"terminal-link": "^2.1.1",
|
|
74
|
+
"toml": "^3.0.0",
|
|
73
75
|
"unzipper": "^0.10.10",
|
|
74
76
|
"update-notifier": "^4.0.0",
|
|
75
77
|
"xdg-basedir": "^4.0.0",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# 技术方案设计
|
|
2
|
+
|
|
3
|
+
## 架构概述
|
|
4
|
+
|
|
5
|
+
本方案通过在 CLI 中暴露一个全局的 `cloudbase-mcp` bin 命令,直接调用内置的 `@cloudbase/cloudbase-mcp` 包,并修改模板下载逻辑,使所有 MCP 配置都指向这个全局命令,从而避免用户需要额外安装 MCP 包。
|
|
6
|
+
|
|
7
|
+
## 技术栈
|
|
8
|
+
|
|
9
|
+
- Node.js CLI 工具
|
|
10
|
+
- `@cloudbase/cloudbase-mcp` 内置依赖
|
|
11
|
+
- 文件系统操作 (fs-extra)
|
|
12
|
+
- 模板下载和解压
|
|
13
|
+
|
|
14
|
+
## 技术选型
|
|
15
|
+
|
|
16
|
+
### 1. Bin 文件创建
|
|
17
|
+
- 在 `bin/` 目录下创建 `cloudbase-mcp.js` 文件
|
|
18
|
+
- 在 `package.json` 的 `bin` 字段中注册 `cloudbase-mcp` 命令
|
|
19
|
+
- 直接调用内置的 `@cloudbase/cloudbase-mcp` 包
|
|
20
|
+
|
|
21
|
+
### 2. 模板配置生成
|
|
22
|
+
- 修改 `router.ts` 中的模板下载逻辑
|
|
23
|
+
- 根据 IDE 类型生成对应的 MCP 配置文件
|
|
24
|
+
- 所有配置都指向 `cloudbase-mcp` 命令
|
|
25
|
+
|
|
26
|
+
### 3. IDE 配置映射和格式支持
|
|
27
|
+
- 参考 CloudBase-AI-ToolKit 的 IDE 到文件映射关系
|
|
28
|
+
- 支持 Cursor、VSCode、Claude Code、OpenAI Codex、OpenCode、Aider 等多种 IDE
|
|
29
|
+
- 支持 JSON 和 TOML 两种配置文件格式
|
|
30
|
+
- 实现 TOML 解析和修改功能
|
|
31
|
+
|
|
32
|
+
## 数据库/接口设计
|
|
33
|
+
|
|
34
|
+
无需数据库设计,主要涉及文件系统操作和命令行接口。
|
|
35
|
+
|
|
36
|
+
## 测试策略
|
|
37
|
+
|
|
38
|
+
1. **单元测试**:测试 bin 文件是否正确调用内置包
|
|
39
|
+
2. **集成测试**:测试模板下载和配置生成功能
|
|
40
|
+
3. **端到端测试**:测试完整的 MCP 配置流程
|
|
41
|
+
|
|
42
|
+
## 安全性
|
|
43
|
+
|
|
44
|
+
- 确保 bin 文件有正确的执行权限
|
|
45
|
+
- 验证模板下载源的安全性
|
|
46
|
+
- 防止路径遍历攻击
|
|
47
|
+
|
|
48
|
+
## 实施计划
|
|
49
|
+
|
|
50
|
+
```mermaid
|
|
51
|
+
graph TD
|
|
52
|
+
A[创建 cloudbase-mcp.js bin 文件] --> B[在 package.json 中注册命令]
|
|
53
|
+
B --> C[修改 router.ts 模板下载逻辑]
|
|
54
|
+
C --> D[实现 IDE 配置映射]
|
|
55
|
+
D --> E[测试验证]
|
|
56
|
+
E --> F[文档更新]
|
|
57
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 需求文档
|
|
2
|
+
|
|
3
|
+
## 介绍
|
|
4
|
+
|
|
5
|
+
当前下载的模板中的 MCP 配置使用 `npx npm-global-exec@latest @cloudbase/cloudbase-mcp@latest` 命令,这会导致用户需要安装 MCP,降低了成功率。CLI 已经内置了 `@cloudbase/cloudbase-mcp@latest` 依赖,需要在 CLI 中直接暴露一个额外的 bin 文件指向到这个依赖,然后修改下载模板的 MCP 配置都指向这个全局命令。
|
|
6
|
+
|
|
7
|
+
## 需求
|
|
8
|
+
|
|
9
|
+
### 需求 1 - 创建全局 MCP bin 命令
|
|
10
|
+
|
|
11
|
+
**用户故事:** 作为开发者,我希望 CLI 提供一个全局的 MCP 命令,这样我就不需要额外安装 MCP 包,提高配置成功率。
|
|
12
|
+
|
|
13
|
+
#### 验收标准
|
|
14
|
+
|
|
15
|
+
1. When CLI 安装后,the 系统 shall 提供一个全局的 `cloudbase-mcp` 命令
|
|
16
|
+
2. When 执行 `cloudbase-mcp` 命令时,the 系统 shall 直接调用内置的 `@cloudbase/cloudbase-mcp` 包
|
|
17
|
+
3. When 验证 MCP 配置时,the 配置文件中的命令 shall 指向 `cloudbase-mcp` 而不是 `npx npm-global-exec@latest @cloudbase/cloudbase-mcp@latest`
|
|
18
|
+
|
|
19
|
+
### 需求 2 - 自动修改模板 MCP 配置
|
|
20
|
+
|
|
21
|
+
**用户故事:** 作为开发者,我希望下载模板后系统自动修改 MCP 配置,将 npx 命令替换为 CLI 提供的全局命令,避免额外的依赖安装。
|
|
22
|
+
|
|
23
|
+
#### 验收标准
|
|
24
|
+
|
|
25
|
+
1. When 下载模板完成后,the 系统 shall 自动检测并修改所有 MCP 配置文件
|
|
26
|
+
2. When 发现 `.cursor/mcp.json` 等配置文件时,the 系统 shall 将 `npx npm-global-exec@latest @cloudbase/cloudbase-mcp@latest` 替换为 `cloudbase-mcp`
|
|
27
|
+
3. When 修改完成后,the 系统 shall 验证配置文件的正确性
|
|
28
|
+
4. When 用户使用模板时,the MCP 功能 shall 无需额外安装即可正常工作
|
|
29
|
+
|
|
30
|
+
### 需求 3 - 支持多种 IDE 配置和格式
|
|
31
|
+
|
|
32
|
+
**用户故事:** 作为开发者,我希望模板支持多种 IDE 的 MCP 配置,包括不同格式(JSON、TOML)和各种工具。
|
|
33
|
+
|
|
34
|
+
#### 验收标准
|
|
35
|
+
|
|
36
|
+
1. When 下载模板时,the 系统 shall 根据 IDE 类型生成对应的配置文件
|
|
37
|
+
2. When 配置 Cursor 时,the 系统 shall 生成 `.cursor/mcp.json` 和 `.cursor/rules/cloudbase-rules.mdc`
|
|
38
|
+
3. When 配置 VSCode 时,the 系统 shall 生成 `.vscode/mcp.json` 和 `.vscode/settings.json`
|
|
39
|
+
4. When 配置 Claude Code 时,the 系统 shall 生成 `CLAUDE.md` 和 `.mcp.json`
|
|
40
|
+
5. When 配置 OpenAI Codex 时,the 系统 shall 处理 `.codex/config.toml` 文件
|
|
41
|
+
6. When 配置 OpenCode 时,the 系统 shall 处理 `.opencode.json` 文件
|
|
42
|
+
7. When 配置 Aider 时,the 系统 shall 处理 `mcp.json` 文件
|
|
43
|
+
8. When 处理 TOML 格式时,the 系统 shall 正确解析和修改配置
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# 实施计划
|
|
2
|
+
|
|
3
|
+
- [x] 1. 创建 cloudbase-mcp bin 文件
|
|
4
|
+
- 在 `bin/` 目录下创建 `cloudbase-mcp.js` 文件
|
|
5
|
+
- 实现直接调用内置 `@cloudbase/cloudbase-mcp` 包的逻辑
|
|
6
|
+
- 确保文件有正确的执行权限
|
|
7
|
+
- _需求: 需求 1
|
|
8
|
+
|
|
9
|
+
- [x] 2. 在 package.json 中注册新命令
|
|
10
|
+
- 在 `bin` 字段中添加 `cloudbase-mcp` 命令映射
|
|
11
|
+
- 确保命令指向正确的 bin 文件
|
|
12
|
+
- _需求: 需求 1
|
|
13
|
+
|
|
14
|
+
- [x] 3. 实现 IDE 配置映射常量
|
|
15
|
+
- 在 `router.ts` 中添加 IDE 到文件的映射关系常量
|
|
16
|
+
- 参考 CloudBase-AI-ToolKit 的映射配置
|
|
17
|
+
- 支持 Cursor、VSCode、Claude Code 等多种 IDE
|
|
18
|
+
- _需求: 需求 3
|
|
19
|
+
|
|
20
|
+
- [x] 3.1. 扩展支持更多 IDE 和格式
|
|
21
|
+
- 添加 OpenAI Codex (.codex/config.toml) 支持
|
|
22
|
+
- 添加 OpenCode (.opencode.json) 支持
|
|
23
|
+
- 添加 Aider (mcp.json) 支持
|
|
24
|
+
- 实现 TOML 格式解析和修改功能
|
|
25
|
+
- _需求: 需求 3
|
|
26
|
+
|
|
27
|
+
- [x] 4. 修改模板下载后的处理逻辑
|
|
28
|
+
- 在 `downloadAndExtractTemplate` 方法中添加 MCP 配置修改逻辑
|
|
29
|
+
- 实现自动检测 MCP 配置文件的功能
|
|
30
|
+
- 实现将 npx 命令替换为 cloudbase-mcp 命令的功能
|
|
31
|
+
- _需求: 需求 2
|
|
32
|
+
|
|
33
|
+
- [x] 5. 实现 MCP 配置文件修改函数
|
|
34
|
+
- 创建 `modifyMCPConfigs` 函数
|
|
35
|
+
- 遍历所有 IDE 配置文件并修改命令
|
|
36
|
+
- 验证修改后的配置文件正确性
|
|
37
|
+
- _需求: 需求 2
|
|
38
|
+
|
|
39
|
+
- [x] 6. 添加配置验证功能
|
|
40
|
+
- 实现验证 MCP 配置文件格式的功能
|
|
41
|
+
- 确保修改后的配置仍然有效
|
|
42
|
+
- 添加错误处理和回滚机制
|
|
43
|
+
- _需求: 需求 2
|
|
44
|
+
|
|
45
|
+
- [x] 7. 测试验证
|
|
46
|
+
- 测试 cloudbase-mcp 命令是否正常工作
|
|
47
|
+
- 测试模板下载和配置修改功能
|
|
48
|
+
- 测试不同 IDE 配置文件的生成
|
|
49
|
+
- _需求: 需求 1, 需求 2, 需求 3
|
|
50
|
+
|
|
51
|
+
- [x] 8. 文档更新
|
|
52
|
+
- 更新 README.md 说明新的 cloudbase-mcp 命令
|
|
53
|
+
- 更新模板下载相关的文档
|
|
54
|
+
- _需求: 需求 1, 需求 2
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConfigParser } from '@cloudbase/toolbox';
|
|
2
|
-
import { CLAUDE, QWEN, CODEX } from './const';
|
|
2
|
+
import { CLAUDE, QWEN, CODEX, AIDER } from './const';
|
|
3
3
|
import z from 'zod/v3';
|
|
4
4
|
export declare const CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND";
|
|
5
5
|
export declare function isValidAgent(agent: unknown): agent is keyof AIConfig['agents'];
|
|
@@ -9,6 +9,7 @@ export interface AIConfig {
|
|
|
9
9
|
claude?: z.infer<(typeof CLAUDE)['configSchema']>;
|
|
10
10
|
qwen?: z.infer<(typeof QWEN)['configSchema']>;
|
|
11
11
|
codex?: z.infer<(typeof CODEX)['configSchema']>;
|
|
12
|
+
aider?: z.infer<(typeof AIDER)['configSchema']>;
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
export interface AgentConfig {
|
|
@@ -28,6 +29,10 @@ export declare const TOOLKIT_CONFIGS: {
|
|
|
28
29
|
config: string;
|
|
29
30
|
rules: string;
|
|
30
31
|
mcp?: undefined;
|
|
32
|
+
} | {
|
|
33
|
+
config: string;
|
|
34
|
+
mcp?: undefined;
|
|
35
|
+
rules?: undefined;
|
|
31
36
|
};
|
|
32
37
|
};
|
|
33
38
|
export declare function createConfigParser(): ConfigParser;
|
|
@@ -64,5 +69,11 @@ export declare class AIConfigManager {
|
|
|
64
69
|
provider?: string;
|
|
65
70
|
model?: string;
|
|
66
71
|
}): Promise<void>;
|
|
72
|
+
updateAiderConfig(type: 'custom' | 'cloudbase', config: {
|
|
73
|
+
apiKey?: string;
|
|
74
|
+
baseUrl?: string;
|
|
75
|
+
model?: string;
|
|
76
|
+
provider?: string;
|
|
77
|
+
}): Promise<void>;
|
|
67
78
|
private updateConfig;
|
|
68
79
|
}
|