@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/README.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
CloudBase CLI 是一个开源的命令行界面交互工具,用于帮助用户快速、方便的部署项目,管理云开发资源。
|
|
4
4
|
|
|
5
|
+
## 主要功能
|
|
6
|
+
|
|
7
|
+
- **云开发资源管理**:快速部署项目,管理云函数、数据库、存储等资源
|
|
8
|
+
- **AI 开发助手**:集成多种 AI 工具(Claude、Qwen、Codex、Aider 等),提升开发效率
|
|
9
|
+
- **MCP 协议支持**:内置 `cloudbase-mcp` 命令,支持 Model Context Protocol,无需额外安装
|
|
10
|
+
- **模板下载**:提供多种项目模板,自动配置 IDE 和 MCP 环境
|
|
11
|
+
|
|
5
12
|
## 安装 CloudBase CLI
|
|
6
13
|
|
|
7
14
|
### npm
|
|
@@ -19,9 +26,14 @@ yarn global add @cloudbase/cli
|
|
|
19
26
|
安装完成后,你可以使用 `cloudbase -v` 验证是否安装成功,如果输出了类似下面的版本号,则表明 CloudBase CLI 被成功安装到您的计算机中。
|
|
20
27
|
|
|
21
28
|
```text
|
|
22
|
-
|
|
29
|
+
2.7.8
|
|
23
30
|
```
|
|
24
31
|
|
|
32
|
+
## 可用命令
|
|
33
|
+
|
|
34
|
+
- `cloudbase` 或 `tcb`:主要的 CLI 命令
|
|
35
|
+
- `cloudbase-mcp`:内置的 MCP 服务器命令,支持 Model Context Protocol
|
|
36
|
+
|
|
25
37
|
## CloudBase CLI 运行要求
|
|
26
38
|
|
|
27
39
|
**Node.js 8.6.0+**
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// 直接调用内置的 @cloudbase/cloudbase-mcp 包的 CLI
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const { spawn } = require('child_process')
|
|
6
|
+
|
|
7
|
+
// 获取内置包的路径并拼接 CLI 路径
|
|
8
|
+
const mcpPackagePath = require.resolve('@cloudbase/cloudbase-mcp')
|
|
9
|
+
const mcpCliPath = path.join(path.dirname(mcpPackagePath), 'cli.js')
|
|
10
|
+
|
|
11
|
+
// 执行内置的 MCP CLI
|
|
12
|
+
const child = spawn('node', [mcpCliPath, ...process.argv.slice(2)], {
|
|
13
|
+
stdio: 'inherit',
|
|
14
|
+
env: process.env
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
child.on('close', (code) => {
|
|
18
|
+
process.exit(code)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
child.on('error', (err) => {
|
|
22
|
+
console.error('Error executing cloudbase-mcp:', err)
|
|
23
|
+
process.exit(1)
|
|
24
|
+
})
|
package/cloudbaserc.json
ADDED
package/lib/commands/ai/index.js
CHANGED
package/lib/utils/ai/config.js
CHANGED
|
@@ -25,7 +25,7 @@ const notFoundError = () => {
|
|
|
25
25
|
});
|
|
26
26
|
};
|
|
27
27
|
function isValidAgent(agent) {
|
|
28
|
-
return ['claude', 'qwen', 'codex'].includes(agent);
|
|
28
|
+
return ['claude', 'qwen', 'codex', 'aider'].includes(agent);
|
|
29
29
|
}
|
|
30
30
|
exports.isValidAgent = isValidAgent;
|
|
31
31
|
exports.TOOLKIT_CONFIGS = {
|
|
@@ -38,8 +38,10 @@ exports.TOOLKIT_CONFIGS = {
|
|
|
38
38
|
rules: 'QWEN.md'
|
|
39
39
|
},
|
|
40
40
|
[const_1.CODEX.value]: {
|
|
41
|
-
config: '.env.local'
|
|
42
|
-
|
|
41
|
+
config: '.env.local'
|
|
42
|
+
},
|
|
43
|
+
[const_1.AIDER.value]: {
|
|
44
|
+
config: '.aider.conf.yml'
|
|
43
45
|
}
|
|
44
46
|
};
|
|
45
47
|
function createConfigParser() {
|
|
@@ -212,6 +214,30 @@ class AIConfigManager {
|
|
|
212
214
|
}
|
|
213
215
|
});
|
|
214
216
|
}
|
|
217
|
+
updateAiderConfig(type, config) {
|
|
218
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
219
|
+
yield this.updateConfig('ai.agents.aider.type', type);
|
|
220
|
+
if (type === 'custom') {
|
|
221
|
+
if (config.apiKey) {
|
|
222
|
+
yield this.updateConfig('ai.agents.aider.apiKey', config.apiKey, 'AI_AIDER_API_KEY');
|
|
223
|
+
}
|
|
224
|
+
if (config.baseUrl) {
|
|
225
|
+
yield this.updateConfig('ai.agents.aider.baseUrl', config.baseUrl, 'AI_AIDER_BASE_URL');
|
|
226
|
+
}
|
|
227
|
+
if (config.model) {
|
|
228
|
+
yield this.updateConfig('ai.agents.aider.model', config.model, 'AI_AIDER_MODEL');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else if (type === 'cloudbase') {
|
|
232
|
+
if (config.provider) {
|
|
233
|
+
yield this.updateConfig('ai.agents.aider.provider', config.provider);
|
|
234
|
+
}
|
|
235
|
+
if (config.model) {
|
|
236
|
+
yield this.updateConfig('ai.agents.aider.model', config.model);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
215
241
|
updateConfig(key, value, env) {
|
|
216
242
|
return __awaiter(this, void 0, void 0, function* () {
|
|
217
243
|
const configParser = createConfigParser();
|
package/lib/utils/ai/const.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getAgentConfigValidator = exports.getDefaultConfig = exports.AGENTS = exports.NONE = exports.CODEX = exports.QWEN = exports.CLAUDE = exports.DEFAULT_CONFIG = exports.CLOUDBASE_MCP_CONFIG_PATH = exports.CLAUDE_CODE_ROUTER_CONFIG_PATH = exports.ENV_LOCAL_PATH = exports.CONFIG_PATH = void 0;
|
|
6
|
+
exports.getDefaultModelByBaseUrl = exports.BASE_URL_MODEL_MAPPING = exports.getAgentConfigValidator = exports.getDefaultConfig = exports.CLOUDBASE_PROVIDERS = exports.AGENTS = exports.NONE = exports.AIDER = exports.CODEX = exports.QWEN = exports.CLAUDE = exports.DEFAULT_CONFIG = exports.CLOUDBASE_MCP_CONFIG_PATH = exports.CLAUDE_CODE_ROUTER_CONFIG_PATH = exports.ENV_LOCAL_PATH = exports.CONFIG_PATH = void 0;
|
|
7
7
|
const os_1 = __importDefault(require("os"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const v3_1 = __importDefault(require("zod/v3"));
|
|
@@ -31,7 +31,7 @@ exports.CLAUDE = {
|
|
|
31
31
|
return data.baseUrl && data.apiKey;
|
|
32
32
|
}
|
|
33
33
|
else if (data.type === 'cloudbase') {
|
|
34
|
-
return data.provider && data.model
|
|
34
|
+
return data.provider && data.model;
|
|
35
35
|
}
|
|
36
36
|
return false;
|
|
37
37
|
})
|
|
@@ -78,11 +78,46 @@ exports.CODEX = {
|
|
|
78
78
|
return false;
|
|
79
79
|
})
|
|
80
80
|
};
|
|
81
|
+
exports.AIDER = {
|
|
82
|
+
name: 'aider',
|
|
83
|
+
value: 'aider',
|
|
84
|
+
configSchema: v3_1.default
|
|
85
|
+
.object({
|
|
86
|
+
type: v3_1.default.enum(['custom', 'cloudbase']).optional(),
|
|
87
|
+
apiKey: v3_1.default.string().optional(),
|
|
88
|
+
baseUrl: v3_1.default.string().optional(),
|
|
89
|
+
model: v3_1.default.string().optional(),
|
|
90
|
+
provider: v3_1.default.string().optional()
|
|
91
|
+
})
|
|
92
|
+
.refine((data) => {
|
|
93
|
+
if (data.type === 'custom' || !data.type) {
|
|
94
|
+
return data.baseUrl && data.apiKey && data.model;
|
|
95
|
+
}
|
|
96
|
+
else if (data.type === 'cloudbase') {
|
|
97
|
+
return data.provider && data.model;
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
})
|
|
101
|
+
};
|
|
81
102
|
exports.NONE = {
|
|
82
103
|
name: '暂不配置',
|
|
83
104
|
value: 'none'
|
|
84
105
|
};
|
|
85
|
-
exports.AGENTS = [exports.CLAUDE, exports.QWEN, exports.CODEX, exports.NONE];
|
|
106
|
+
exports.AGENTS = [exports.CLAUDE, exports.QWEN, exports.CODEX, exports.AIDER, exports.NONE];
|
|
107
|
+
exports.CLOUDBASE_PROVIDERS = [
|
|
108
|
+
{
|
|
109
|
+
name: 'DeepSeek',
|
|
110
|
+
value: 'deepseek',
|
|
111
|
+
models: ['deepseek-v3'],
|
|
112
|
+
transformer: 'deepseek'
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: '自定义',
|
|
116
|
+
value: 'custom',
|
|
117
|
+
models: [],
|
|
118
|
+
transformer: undefined
|
|
119
|
+
}
|
|
120
|
+
];
|
|
86
121
|
function getDefaultConfig(agent) {
|
|
87
122
|
const agentConfig = exports.AGENTS.find((a) => a.value === agent);
|
|
88
123
|
if (!agentConfig) {
|
|
@@ -105,3 +140,11 @@ function getAgentConfigValidator(agent) {
|
|
|
105
140
|
: () => ({ success: true });
|
|
106
141
|
}
|
|
107
142
|
exports.getAgentConfigValidator = getAgentConfigValidator;
|
|
143
|
+
exports.BASE_URL_MODEL_MAPPING = {
|
|
144
|
+
'https://api.moonshot.cn/v1': 'kimi-k2-0711-preview',
|
|
145
|
+
'https://open.bigmodel.cn/api/paas/v4': 'glm-4.5'
|
|
146
|
+
};
|
|
147
|
+
function getDefaultModelByBaseUrl(baseUrl) {
|
|
148
|
+
return exports.BASE_URL_MODEL_MAPPING[baseUrl] || 'gpt-4';
|
|
149
|
+
}
|
|
150
|
+
exports.getDefaultModelByBaseUrl = getDefaultModelByBaseUrl;
|
package/lib/utils/ai/router.js
CHANGED
|
@@ -51,6 +51,68 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
51
51
|
const const_1 = require("./const");
|
|
52
52
|
const utils_1 = require("../../commands/utils");
|
|
53
53
|
const auth_1 = require("../../auth");
|
|
54
|
+
const IDE_FILE_MAPPINGS = {
|
|
55
|
+
"cursor": [
|
|
56
|
+
".cursor/rules/cloudbase-rules.mdc",
|
|
57
|
+
".cursor/mcp.json"
|
|
58
|
+
],
|
|
59
|
+
"windsurf": [
|
|
60
|
+
".windsurf/rules/cloudbase-rules.md"
|
|
61
|
+
],
|
|
62
|
+
"codebuddy": [
|
|
63
|
+
".rules/cloudbase-rules.md"
|
|
64
|
+
],
|
|
65
|
+
"claude-code": [
|
|
66
|
+
"CLAUDE.md",
|
|
67
|
+
".mcp.json"
|
|
68
|
+
],
|
|
69
|
+
"cline": [
|
|
70
|
+
".clinerules/cloudbase-rules.mdc"
|
|
71
|
+
],
|
|
72
|
+
"gemini-cli": [
|
|
73
|
+
".gemini/GEMINI.md",
|
|
74
|
+
".gemini/settings.json"
|
|
75
|
+
],
|
|
76
|
+
"opencode": [
|
|
77
|
+
".opencode.json"
|
|
78
|
+
],
|
|
79
|
+
"qwen-code": [
|
|
80
|
+
".qwen/QWEN.md",
|
|
81
|
+
".qwen/settings.json"
|
|
82
|
+
],
|
|
83
|
+
"baidu-comate": [
|
|
84
|
+
".comate/rules/cloudbase-rules.mdr",
|
|
85
|
+
".comate/rules/cloudbaase-rules.mdr",
|
|
86
|
+
".comate/mcp.json"
|
|
87
|
+
],
|
|
88
|
+
"openai-codex-cli": [
|
|
89
|
+
".codex/config.toml",
|
|
90
|
+
"AGENTS.md",
|
|
91
|
+
],
|
|
92
|
+
"augment-code": [
|
|
93
|
+
".augment-guidelines"
|
|
94
|
+
],
|
|
95
|
+
"github-copilot": [
|
|
96
|
+
".github/copilot-instructions.md"
|
|
97
|
+
],
|
|
98
|
+
"roocode": [
|
|
99
|
+
".roo/rules/cloudbaase-rules.md",
|
|
100
|
+
".roo/mcp.json"
|
|
101
|
+
],
|
|
102
|
+
"tongyi-lingma": [
|
|
103
|
+
".lingma/rules/cloudbaase-rules.md"
|
|
104
|
+
],
|
|
105
|
+
"trae": [
|
|
106
|
+
".trae/rules/cloudbase-rules.md"
|
|
107
|
+
],
|
|
108
|
+
"vscode": [
|
|
109
|
+
".vscode/mcp.json",
|
|
110
|
+
".vscode/settings.json"
|
|
111
|
+
],
|
|
112
|
+
"aider": [
|
|
113
|
+
"mcp.json"
|
|
114
|
+
]
|
|
115
|
+
};
|
|
54
116
|
class AICommandRouter {
|
|
55
117
|
constructor() {
|
|
56
118
|
this.configManager = new config_1.AIConfigManager();
|
|
@@ -92,7 +154,6 @@ class AICommandRouter {
|
|
|
92
154
|
return __awaiter(this, void 0, void 0, function* () {
|
|
93
155
|
const { missingFiles } = yield this.configManager.checkToolkitConfig(agent);
|
|
94
156
|
if (missingFiles.length > 0) {
|
|
95
|
-
log.warn(`⚠️ 缺少 AI ToolKit CLI 配置文件: ${missingFiles.join(', ')}`);
|
|
96
157
|
const shouldDownload = yield this.promptForTemplateDownload(log);
|
|
97
158
|
if (shouldDownload) {
|
|
98
159
|
log.log('');
|
|
@@ -120,7 +181,7 @@ class AICommandRouter {
|
|
|
120
181
|
{
|
|
121
182
|
type: 'confirm',
|
|
122
183
|
name: 'downloadTemplate',
|
|
123
|
-
message: '是否下载 CloudBase AI ToolKit
|
|
184
|
+
message: '是否下载 CloudBase AI ToolKit 模板?(建议下载以获取完整的开发体验)',
|
|
124
185
|
default: true
|
|
125
186
|
}
|
|
126
187
|
]);
|
|
@@ -141,11 +202,11 @@ class AICommandRouter {
|
|
|
141
202
|
name: 'templateType',
|
|
142
203
|
message: '选择要下载的模板类型:',
|
|
143
204
|
choices: [
|
|
144
|
-
{ name: '
|
|
145
|
-
{ name: '
|
|
146
|
-
{ name: '
|
|
147
|
-
{ name: '
|
|
148
|
-
{ name: '
|
|
205
|
+
{ name: '🚀 Web 应用 - React + CloudBase', value: 'react' },
|
|
206
|
+
{ name: '🟢 Web 应用 - Vue + CloudBase', value: 'vue' },
|
|
207
|
+
{ name: '🟦 微信小程序 + CloudBase', value: 'miniprogram' },
|
|
208
|
+
{ name: '🌈 跨端应用 - UniApp + CloudBase', value: 'uniapp' },
|
|
209
|
+
{ name: '🧩 只下载 AI 规则和配置', value: 'rules' }
|
|
149
210
|
],
|
|
150
211
|
default: 'rules'
|
|
151
212
|
}
|
|
@@ -295,6 +356,7 @@ class AICommandRouter {
|
|
|
295
356
|
finally { if (e_1) throw e_1.error; }
|
|
296
357
|
}
|
|
297
358
|
yield fs.unlink(zipPath);
|
|
359
|
+
yield this.modifyMCPConfigs(extractDir, log);
|
|
298
360
|
});
|
|
299
361
|
}
|
|
300
362
|
validateTemplateIntegrity(templateType, extractDir, log) {
|
|
@@ -467,6 +529,13 @@ class AICommandRouter {
|
|
|
467
529
|
else {
|
|
468
530
|
return yield this.executeCodexAgent(agentConfig, additionalArgs, log);
|
|
469
531
|
}
|
|
532
|
+
case const_1.AIDER.value:
|
|
533
|
+
if (agentConfig.type === 'cloudbase') {
|
|
534
|
+
return yield this.executeAiderCloudbaseAgent(agentConfig, additionalArgs, log);
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
return yield this.executeAiderAgent(agentConfig, additionalArgs, log);
|
|
538
|
+
}
|
|
470
539
|
default:
|
|
471
540
|
throw new Error(`不支持的 AI 工具: ${agent}`);
|
|
472
541
|
}
|
|
@@ -582,9 +651,11 @@ class AICommandRouter {
|
|
|
582
651
|
name: 'cloudbase',
|
|
583
652
|
api_base_url: `https://${envId}.api.tcloudbasegateway.com/v1/ai/${provider}/chat/completions`,
|
|
584
653
|
api_key: accessToken.access_token,
|
|
585
|
-
models: [model]
|
|
586
|
-
transformer: { use: [transformer] }
|
|
654
|
+
models: [model]
|
|
587
655
|
};
|
|
656
|
+
if (transformer) {
|
|
657
|
+
cloudbaseProvider.transformer = { use: [transformer] };
|
|
658
|
+
}
|
|
588
659
|
yield fs.ensureFile(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH);
|
|
589
660
|
const claudeCodeRouterConfig = yield fs.readFile(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, 'utf-8');
|
|
590
661
|
if (claudeCodeRouterConfig.trim().length === 0) {
|
|
@@ -671,12 +742,12 @@ class AICommandRouter {
|
|
|
671
742
|
{
|
|
672
743
|
type: 'confirm',
|
|
673
744
|
name: 'shouldInstall',
|
|
674
|
-
message: 'claude code router
|
|
745
|
+
message: 'AI 开发缺少 claude code router 依赖,是否安装?'
|
|
675
746
|
}
|
|
676
747
|
]);
|
|
677
748
|
if (shouldInstall) {
|
|
678
749
|
yield this.executeCommand('npm', ['install', '-g', '@musistudio/claude-code-router'], process.env, log);
|
|
679
|
-
log.
|
|
750
|
+
log.info('✅ claude-code-router 安装完成');
|
|
680
751
|
}
|
|
681
752
|
else {
|
|
682
753
|
log.info('❌ claude code router 未安装,请手动安装');
|
|
@@ -695,12 +766,12 @@ class AICommandRouter {
|
|
|
695
766
|
{
|
|
696
767
|
type: 'confirm',
|
|
697
768
|
name: 'shouldInstall',
|
|
698
|
-
message: 'claude code
|
|
769
|
+
message: 'AI 开发缺少 claude code 依赖,是否安装?'
|
|
699
770
|
}
|
|
700
771
|
]);
|
|
701
772
|
if (shouldInstall) {
|
|
702
773
|
yield this.executeCommand('npm', ['install', '-g', '@anthropic-ai/claude-code'], process.env, log);
|
|
703
|
-
log.
|
|
774
|
+
log.info('✅ claude code 安装完成');
|
|
704
775
|
}
|
|
705
776
|
else {
|
|
706
777
|
log.info('❌ claude code 未安装,请手动安装');
|
|
@@ -719,12 +790,12 @@ class AICommandRouter {
|
|
|
719
790
|
{
|
|
720
791
|
type: 'confirm',
|
|
721
792
|
name: 'shouldInstall',
|
|
722
|
-
message: 'qwen code
|
|
793
|
+
message: 'AI 开发缺少 qwen code 依赖,是否安装?'
|
|
723
794
|
}
|
|
724
795
|
]);
|
|
725
796
|
if (shouldInstall) {
|
|
726
797
|
yield this.executeCommand('npm', ['install', '-g', '@qwen-code/qwen-code'], process.env, log);
|
|
727
|
-
log.
|
|
798
|
+
log.info('✅ qwen code 安装完成');
|
|
728
799
|
}
|
|
729
800
|
else {
|
|
730
801
|
log.info('❌ qwen code 未安装,请手动安装');
|
|
@@ -738,10 +809,13 @@ class AICommandRouter {
|
|
|
738
809
|
yield this.ensureCodexCode(log);
|
|
739
810
|
const codexArgs = [
|
|
740
811
|
...(model ? ['--config', `model=${model}`] : []),
|
|
741
|
-
'--config',
|
|
812
|
+
'--config',
|
|
813
|
+
'model_providers.custom.name=Custom OpenAI',
|
|
742
814
|
...(baseUrl ? ['--config', `model_providers.custom.base_url=${baseUrl}`] : []),
|
|
743
|
-
'--config',
|
|
744
|
-
'
|
|
815
|
+
'--config',
|
|
816
|
+
'model_providers.custom.env_key=OPENAI_API_KEY',
|
|
817
|
+
'--config',
|
|
818
|
+
'model_provider=custom',
|
|
745
819
|
...additionalArgs
|
|
746
820
|
];
|
|
747
821
|
yield this.executeCommand('codex', codexArgs, Object.assign(Object.assign({}, process.env), { OPENAI_API_KEY: apiKey }), log);
|
|
@@ -766,11 +840,16 @@ class AICommandRouter {
|
|
|
766
840
|
const baseUrl = `https://${envId}.api.tcloudbasegateway.com/v1/ai/${provider}`;
|
|
767
841
|
const apiKey = accessToken.access_token;
|
|
768
842
|
const codexArgs = [
|
|
769
|
-
'--config',
|
|
770
|
-
|
|
771
|
-
'--config',
|
|
772
|
-
'
|
|
773
|
-
'--config',
|
|
843
|
+
'--config',
|
|
844
|
+
`model=${model}`,
|
|
845
|
+
'--config',
|
|
846
|
+
'model_providers.cloudbase.name=CloudBase AI',
|
|
847
|
+
'--config',
|
|
848
|
+
`model_providers.cloudbase.base_url=${baseUrl}`,
|
|
849
|
+
'--config',
|
|
850
|
+
'model_providers.cloudbase.env_key=CLOUDBASE_ACCESS_TOKEN',
|
|
851
|
+
'--config',
|
|
852
|
+
'model_provider=cloudbase',
|
|
774
853
|
...additionalArgs
|
|
775
854
|
];
|
|
776
855
|
yield this.executeCommand('codex', codexArgs, Object.assign(Object.assign({}, process.env), { CLOUDBASE_ACCESS_TOKEN: apiKey }), log);
|
|
@@ -779,27 +858,225 @@ class AICommandRouter {
|
|
|
779
858
|
ensureCodexCode(log) {
|
|
780
859
|
return __awaiter(this, void 0, void 0, function* () {
|
|
781
860
|
if (yield this.isToolAvailable('codex')) {
|
|
782
|
-
log.debug('codex
|
|
861
|
+
log.debug('codex 已安装!');
|
|
783
862
|
}
|
|
784
863
|
else {
|
|
785
864
|
const { shouldInstall } = yield inquirer_1.default.prompt([
|
|
786
865
|
{
|
|
787
866
|
type: 'confirm',
|
|
788
867
|
name: 'shouldInstall',
|
|
789
|
-
message: 'codex
|
|
868
|
+
message: 'AI 开发缺少 codex 依赖,是否安装?'
|
|
790
869
|
}
|
|
791
870
|
]);
|
|
792
871
|
if (shouldInstall) {
|
|
793
872
|
yield this.executeCommand('npm', ['install', '-g', '@openai/codex'], process.env, log);
|
|
794
|
-
log.
|
|
873
|
+
log.info('✅ codex 安装完成');
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
log.info('❌ codex 未安装,请手动安装');
|
|
877
|
+
process.exit(1);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
executeAiderAgent(config, additionalArgs, log) {
|
|
883
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
884
|
+
yield this.ensureAider(log);
|
|
885
|
+
const { apiKey, baseUrl, model } = config;
|
|
886
|
+
const aiderArgs = ['--model', `openai/${model}`, ...additionalArgs];
|
|
887
|
+
const env = Object.assign(Object.assign({}, process.env), { OPENAI_API_KEY: apiKey, OPENAI_API_BASE: baseUrl });
|
|
888
|
+
yield this.executeCommand('aider', aiderArgs, env, log);
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
executeAiderCloudbaseAgent(config, additionalArgs, log) {
|
|
892
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
893
|
+
yield this.ensureAider(log);
|
|
894
|
+
const { provider, model } = config;
|
|
895
|
+
const envId = yield (0, config_1.createConfigParser)().get('envId');
|
|
896
|
+
yield (0, auth_1.checkLogin)();
|
|
897
|
+
const credential = yield (0, utils_1.getCredential)({}, {});
|
|
898
|
+
const accessToken = yield (0, utils_1.rawFetchAccessToken)({
|
|
899
|
+
envId,
|
|
900
|
+
secretId: credential.secretId,
|
|
901
|
+
secretKey: credential.secretKey,
|
|
902
|
+
token: credential.token
|
|
903
|
+
});
|
|
904
|
+
if (!accessToken.access_token) {
|
|
905
|
+
log.error(`获取云开发 Access Token 失败,请运行 tcb login 后再重试,${JSON.stringify(accessToken)}`);
|
|
906
|
+
process.exit(1);
|
|
907
|
+
}
|
|
908
|
+
const baseUrl = `https://${envId}.api.tcloudbasegateway.com/v1/ai/${provider}`;
|
|
909
|
+
const apiKey = accessToken.access_token;
|
|
910
|
+
const aiderArgs = ['--model', `openai/${model}`, ...additionalArgs];
|
|
911
|
+
const env = Object.assign(Object.assign({}, process.env), { OPENAI_API_KEY: apiKey, OPENAI_API_BASE: baseUrl });
|
|
912
|
+
yield this.executeCommand('aider', aiderArgs, env, log);
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
ensureAider(log) {
|
|
916
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
917
|
+
if (yield this.isToolAvailable('aider')) {
|
|
918
|
+
log.debug('aider 已安装!');
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
const { shouldInstall } = yield inquirer_1.default.prompt([
|
|
922
|
+
{
|
|
923
|
+
type: 'confirm',
|
|
924
|
+
name: 'shouldInstall',
|
|
925
|
+
message: 'AI 开发缺少 aider 依赖,是否安装?'
|
|
926
|
+
}
|
|
927
|
+
]);
|
|
928
|
+
if (shouldInstall) {
|
|
929
|
+
log.info('正在安装 aider...');
|
|
930
|
+
const platform = process.platform;
|
|
931
|
+
if (platform === 'win32') {
|
|
932
|
+
yield this.executeCommand('powershell', ['-c', 'irm https://aider.chat/install.ps1 | iex'], process.env, log);
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
yield this.executeCommand('sh', ['-c', 'curl -LsSf https://aider.chat/install.sh | sh'], process.env, log);
|
|
936
|
+
}
|
|
937
|
+
log.info('✅ aider 安装完成');
|
|
795
938
|
}
|
|
796
939
|
else {
|
|
797
|
-
log.info('❌
|
|
940
|
+
log.info('❌ aider 未安装,请手动安装');
|
|
798
941
|
process.exit(1);
|
|
799
942
|
}
|
|
800
943
|
}
|
|
801
944
|
});
|
|
802
945
|
}
|
|
946
|
+
modifyMCPConfigs(extractDir, log) {
|
|
947
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
948
|
+
const fs = yield Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
949
|
+
const path = yield Promise.resolve().then(() => __importStar(require('path')));
|
|
950
|
+
try {
|
|
951
|
+
log.info('🔧 正在修改 MCP 配置文件...');
|
|
952
|
+
for (const [ide, files] of Object.entries(IDE_FILE_MAPPINGS)) {
|
|
953
|
+
for (const file of files) {
|
|
954
|
+
const filePath = path.join(extractDir, file);
|
|
955
|
+
if (yield fs.pathExists(filePath)) {
|
|
956
|
+
if (file.endsWith('.json')) {
|
|
957
|
+
yield this.modifyMCPJsonFile(filePath, log);
|
|
958
|
+
}
|
|
959
|
+
else if (file.endsWith('.toml')) {
|
|
960
|
+
yield this.modifyMCPTomlFile(filePath, log);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
log.info('✅ MCP 配置文件修改完成');
|
|
966
|
+
}
|
|
967
|
+
catch (error) {
|
|
968
|
+
log.warn(`⚠️ MCP 配置文件修改失败: ${error.message}`);
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
modifyMCPJsonFile(filePath, log) {
|
|
973
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
974
|
+
const fs = yield Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
975
|
+
try {
|
|
976
|
+
const content = yield fs.readFile(filePath, 'utf-8');
|
|
977
|
+
const config = JSON.parse(content);
|
|
978
|
+
let modified = false;
|
|
979
|
+
const modifyCommands = (obj) => {
|
|
980
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
981
|
+
return obj;
|
|
982
|
+
}
|
|
983
|
+
if (Array.isArray(obj)) {
|
|
984
|
+
return obj.map(item => modifyCommands(item));
|
|
985
|
+
}
|
|
986
|
+
const result = Object.assign({}, obj);
|
|
987
|
+
if (result.command === 'npx' && Array.isArray(result.args)) {
|
|
988
|
+
const argsStr = result.args.join(' ');
|
|
989
|
+
if (argsStr.includes('npm-global-exec@latest') && argsStr.includes('@cloudbase/cloudbase-mcp@latest')) {
|
|
990
|
+
result.command = 'cloudbase-mcp';
|
|
991
|
+
result.args = [];
|
|
992
|
+
modified = true;
|
|
993
|
+
log.debug(`修改配置文件 ${filePath}: npx -> cloudbase-mcp`);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
for (const [key, value] of Object.entries(result)) {
|
|
997
|
+
result[key] = modifyCommands(value);
|
|
998
|
+
}
|
|
999
|
+
return result;
|
|
1000
|
+
};
|
|
1001
|
+
const modifiedConfig = modifyCommands(config);
|
|
1002
|
+
if (modified) {
|
|
1003
|
+
yield fs.writeJson(filePath, modifiedConfig, { spaces: 2 });
|
|
1004
|
+
log.debug(`✅ 已修改 ${filePath}`);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
catch (error) {
|
|
1008
|
+
log.warn(`⚠️ 修改配置文件 ${filePath} 失败: ${error.message}`);
|
|
1009
|
+
}
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
modifyMCPTomlFile(filePath, log) {
|
|
1013
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1014
|
+
const fs = yield Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
1015
|
+
const toml = require('toml');
|
|
1016
|
+
try {
|
|
1017
|
+
const content = yield fs.readFile(filePath, 'utf-8');
|
|
1018
|
+
const config = toml.parse(content);
|
|
1019
|
+
let modified = false;
|
|
1020
|
+
const modifyCommands = (obj) => {
|
|
1021
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
1022
|
+
return obj;
|
|
1023
|
+
}
|
|
1024
|
+
if (Array.isArray(obj)) {
|
|
1025
|
+
return obj.map(item => modifyCommands(item));
|
|
1026
|
+
}
|
|
1027
|
+
const result = Object.assign({}, obj);
|
|
1028
|
+
if (result.command === 'npx' && Array.isArray(result.args)) {
|
|
1029
|
+
const argsStr = result.args.join(' ');
|
|
1030
|
+
if (argsStr.includes('@cloudbase/cloudbase-mcp@latest')) {
|
|
1031
|
+
result.command = 'cloudbase-mcp';
|
|
1032
|
+
result.args = [];
|
|
1033
|
+
modified = true;
|
|
1034
|
+
log.debug(`修改配置文件 ${filePath}: npx -> cloudbase-mcp`);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
for (const [key, value] of Object.entries(result)) {
|
|
1038
|
+
result[key] = modifyCommands(value);
|
|
1039
|
+
}
|
|
1040
|
+
return result;
|
|
1041
|
+
};
|
|
1042
|
+
const modifiedConfig = modifyCommands(config);
|
|
1043
|
+
if (modified) {
|
|
1044
|
+
const tomlString = this.objectToToml(modifiedConfig);
|
|
1045
|
+
yield fs.writeFile(filePath, tomlString, 'utf-8');
|
|
1046
|
+
log.debug(`✅ 已修改 ${filePath}`);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
catch (error) {
|
|
1050
|
+
log.warn(`⚠️ 修改配置文件 ${filePath} 失败: ${error.message}`);
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
objectToToml(obj, prefix = '') {
|
|
1055
|
+
const lines = [];
|
|
1056
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1057
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
1058
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
1059
|
+
lines.push(`[${fullKey}]`);
|
|
1060
|
+
lines.push(this.objectToToml(value, fullKey));
|
|
1061
|
+
}
|
|
1062
|
+
else if (Array.isArray(value)) {
|
|
1063
|
+
const arrayStr = value.map(item => {
|
|
1064
|
+
if (typeof item === 'string') {
|
|
1065
|
+
return `"${item}"`;
|
|
1066
|
+
}
|
|
1067
|
+
return item;
|
|
1068
|
+
}).join(', ');
|
|
1069
|
+
lines.push(`${key} = [${arrayStr}]`);
|
|
1070
|
+
}
|
|
1071
|
+
else if (typeof value === 'string') {
|
|
1072
|
+
lines.push(`${key} = "${value}"`);
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
lines.push(`${key} = ${value}`);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
return lines.join('\n');
|
|
1079
|
+
}
|
|
803
1080
|
}
|
|
804
1081
|
exports.AICommandRouter = AICommandRouter;
|
|
805
1082
|
function safeParseJson(json) {
|