@cloudbase/cli 2.10.0 → 2.11.0-beta.0
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/lib/auth/login.js +51 -65
- package/lib/auth/logout.js +3 -14
- package/lib/commands/account/login.js +136 -149
- package/lib/commands/account/logout.js +3 -14
- package/lib/commands/ag/base.js +203 -0
- package/lib/commands/ag/create.js +301 -0
- package/lib/commands/ag/debug/index.html +699 -0
- package/lib/commands/ag/delete.js +200 -0
- package/lib/commands/ag/deploy.js +185 -0
- package/lib/commands/ag/detail.js +113 -0
- package/lib/commands/ag/index.js +22 -0
- package/lib/commands/ag/list.js +155 -0
- package/lib/commands/ag/run.js +644 -0
- package/lib/commands/ai/index.js +76 -95
- package/lib/commands/cloudfunction/base.js +182 -203
- package/lib/commands/cloudrun/base.js +578 -603
- package/lib/commands/common.js +48 -63
- package/lib/commands/config/delete.js +16 -27
- package/lib/commands/config/get.js +13 -24
- package/lib/commands/config/list.js +16 -27
- package/lib/commands/config/set.js +33 -44
- package/lib/commands/db/base.js +221 -240
- package/lib/commands/env/base.js +36 -49
- package/lib/commands/env/domain.js +79 -94
- package/lib/commands/env/login.js +120 -135
- package/lib/commands/framework/index.js +32 -49
- package/lib/commands/fun/base.js +223 -244
- package/lib/commands/functions/alias/getRoute.js +33 -44
- package/lib/commands/functions/alias/setRoute.js +36 -47
- package/lib/commands/functions/code-download.js +43 -54
- package/lib/commands/functions/code-update.js +23 -34
- package/lib/commands/functions/concurrency/delete.js +11 -22
- package/lib/commands/functions/concurrency/list.js +20 -31
- package/lib/commands/functions/concurrency/set.js +13 -24
- package/lib/commands/functions/config-update.js +30 -41
- package/lib/commands/functions/copy.js +12 -23
- package/lib/commands/functions/delete.js +30 -41
- package/lib/commands/functions/deploy.js +184 -202
- package/lib/commands/functions/detail.js +23 -34
- package/lib/commands/functions/invoke.js +69 -75
- package/lib/commands/functions/layer/bind.js +102 -105
- package/lib/commands/functions/layer/create.js +29 -35
- package/lib/commands/functions/layer/delete.js +42 -48
- package/lib/commands/functions/layer/download.js +52 -58
- package/lib/commands/functions/layer/list.js +44 -50
- package/lib/commands/functions/layer/sort.js +39 -45
- package/lib/commands/functions/list.js +25 -36
- package/lib/commands/functions/log.js +65 -73
- package/lib/commands/functions/run.js +118 -116
- package/lib/commands/functions/trigger-create.js +32 -43
- package/lib/commands/functions/trigger-delete.js +50 -61
- package/lib/commands/functions/version/list.js +29 -40
- package/lib/commands/functions/version/publish.js +11 -22
- package/lib/commands/gateway/create.js +50 -61
- package/lib/commands/gateway/delete.js +38 -49
- package/lib/commands/gateway/domain.js +65 -80
- package/lib/commands/gateway/list.js +31 -42
- package/lib/commands/gateway/switch.js +48 -61
- package/lib/commands/helpers/init.js +226 -249
- package/lib/commands/helpers/new.js +35 -46
- package/lib/commands/helpers/open.js +22 -33
- package/lib/commands/hosting/hosting.js +157 -178
- package/lib/commands/index.js +1 -0
- package/lib/commands/lowcode/app.js +114 -144
- package/lib/commands/lowcode/comps.js +136 -127
- package/lib/commands/lowcode/utils.js +11 -22
- package/lib/commands/pull/pull.js +33 -46
- package/lib/commands/run/delete.js +35 -46
- package/lib/commands/run/image/delete.js +32 -39
- package/lib/commands/run/image/download.js +26 -33
- package/lib/commands/run/image/list.js +41 -48
- package/lib/commands/run/image/upload.js +26 -33
- package/lib/commands/run/list.js +32 -43
- package/lib/commands/run/service/config.js +17 -28
- package/lib/commands/run/service/deploy.js +15 -26
- package/lib/commands/run/service/list.js +48 -59
- package/lib/commands/run/service/update.js +7 -18
- package/lib/commands/run/standalonegateway/create.js +35 -42
- package/lib/commands/run/standalonegateway/destroy.js +23 -30
- package/lib/commands/run/standalonegateway/list.js +19 -26
- package/lib/commands/run/standalonegateway/package.js +31 -38
- package/lib/commands/run/standalonegateway/turn.js +27 -34
- package/lib/commands/run/version/create.js +198 -205
- package/lib/commands/run/version/delete.js +31 -38
- package/lib/commands/run/version/list.js +42 -49
- package/lib/commands/run/version/modify.js +27 -34
- package/lib/commands/run/version/update.js +201 -208
- package/lib/commands/runf/base.js +216 -237
- package/lib/commands/self-update.js +59 -72
- package/lib/commands/smart.js +66 -79
- package/lib/commands/storage/storage.js +192 -219
- package/lib/commands/third/thirdAttach.js +16 -27
- package/lib/commands/utils.js +119 -149
- package/lib/db/index.js +48 -67
- package/lib/decorators/captureError.js +10 -21
- package/lib/decorators/guard.js +11 -22
- package/lib/decorators/injectParams.js +29 -40
- package/lib/decorators/params/common.js +5 -2
- package/lib/decorators/params/index.js +3 -12
- package/lib/env/domain.js +13 -28
- package/lib/env/index.js +25 -44
- package/lib/env/login.js +30 -45
- package/lib/function/alias.js +31 -44
- package/lib/function/base.js +187 -215
- package/lib/function/code.js +8 -19
- package/lib/function/concurrency.js +43 -58
- package/lib/function/create.js +43 -53
- package/lib/function/delete.js +22 -35
- package/lib/function/layer/attach.js +33 -46
- package/lib/function/layer/create.js +34 -45
- package/lib/function/layer/delete.js +5 -16
- package/lib/function/layer/download.js +11 -22
- package/lib/function/layer/list.js +12 -25
- package/lib/function/layer/sort.js +6 -17
- package/lib/function/trigger.js +65 -82
- package/lib/function/update.js +24 -32
- package/lib/function/version.js +29 -42
- package/lib/function/vpc.js +12 -25
- package/lib/gateway/index.js +77 -104
- package/lib/hosting.js +157 -188
- package/lib/run/delete.js +3 -12
- package/lib/run/image/build.js +6 -15
- package/lib/run/image/delete.js +3 -12
- package/lib/run/image/info.js +3 -12
- package/lib/run/image/list.js +6 -15
- package/lib/run/list.js +19 -30
- package/lib/run/repo.js +6 -15
- package/lib/run/service/common.js +160 -173
- package/lib/run/service/config.js +44 -57
- package/lib/run/service/deployPackage.js +33 -44
- package/lib/run/service/list.js +8 -14
- package/lib/run/service/showLogs.js +69 -90
- package/lib/run/service/update.js +50 -63
- package/lib/run/standalonegateway/create.js +3 -12
- package/lib/run/standalonegateway/destroy.js +3 -12
- package/lib/run/standalonegateway/list.js +3 -12
- package/lib/run/standalonegateway/package/list.js +3 -12
- package/lib/run/standalonegateway/turn/off.js +3 -12
- package/lib/run/standalonegateway/turn/on.js +3 -12
- package/lib/run/version/create.js +41 -31
- package/lib/run/version/delete.js +3 -12
- package/lib/run/version/list.js +3 -12
- package/lib/run/version/modify.js +3 -12
- package/lib/run/version/repo.js +6 -15
- package/lib/run/version/update.js +37 -26
- package/lib/storage.js +62 -93
- package/lib/third/index.js +6 -17
- package/lib/utils/ai/banner.js +49 -60
- package/lib/utils/ai/claudeWindows.js +2 -2
- package/lib/utils/ai/config.js +169 -206
- package/lib/utils/ai/ensureFiles.js +6 -17
- package/lib/utils/ai/env.js +16 -27
- package/lib/utils/ai/envLocalManager.js +35 -52
- package/lib/utils/ai/router.js +927 -1005
- package/lib/utils/ai/setup.js +527 -563
- package/lib/utils/auth.js +3 -14
- package/lib/utils/checkTcbrEnv.js +20 -31
- package/lib/utils/cli-table.js +6 -1
- package/lib/utils/config.js +4 -13
- package/lib/utils/dts.js +98 -113
- package/lib/utils/env.js +154 -175
- package/lib/utils/function-packer.js +29 -42
- package/lib/utils/log.js +10 -21
- package/lib/utils/mcp-config-modifier.js +105 -120
- package/lib/utils/net/cloud-api-request.js +15 -23
- package/lib/utils/net/credential.js +26 -39
- package/lib/utils/net/http-request.js +63 -80
- package/lib/utils/net/manager-service.js +22 -35
- package/lib/utils/notice.js +16 -27
- package/lib/utils/output/loading.js +3 -12
- package/lib/utils/parallel.js +32 -43
- package/lib/utils/platform/mac.js +4 -15
- package/lib/utils/platform/port.js +4 -15
- package/lib/utils/prompt/select.js +6 -15
- package/lib/utils/report.js +28 -33
- package/lib/utils/reporter/agree.js +11 -22
- package/lib/utils/reporter/download.js +17 -28
- package/lib/utils/reporter/usage.js +12 -23
- package/lib/utils/store/auth.js +17 -30
- package/lib/utils/store/config.js +11 -25
- package/lib/utils/store/db.js +17 -36
- package/lib/utils/tcbrApi/callTcbrApi.js +19 -28
- package/lib/utils/template-manager.js +215 -242
- package/lib/utils/template.js +81 -96
- package/lib/utils/tools/common.js +45 -56
- package/lib/utils/tools/time.js +5 -16
- package/lib/utils/url.js +10 -4
- package/package.json +2 -2
- package/specs/ag-command/design.md +421 -0
- package/specs/ag-command/doc.md +204 -0
- package/specs/ag-command/requirements.md +173 -0
- package/specs/ag-command/summary.md +174 -0
- package/specs/ag-command/tasks.md +197 -0
- package/specs/ag-command/usage-guide.md +420 -0
- package/tsconfig.json +1 -1
- package/types/commands/ag/base.d.ts +44 -0
- package/types/commands/ag/create.d.ts +25 -0
- package/types/commands/ag/delete.d.ts +32 -0
- package/types/commands/ag/deploy.d.ts +31 -0
- package/types/commands/ag/detail.d.ts +28 -0
- package/types/commands/ag/index.d.ts +6 -0
- package/types/commands/ag/list.d.ts +35 -0
- package/types/commands/ag/run.d.ts +52 -0
- package/types/commands/cloudrun/base.d.ts +10 -0
- package/types/commands/index.d.ts +1 -0
package/lib/utils/ai/router.js
CHANGED
|
@@ -22,22 +22,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
35
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
36
|
-
var m = o[Symbol.asyncIterator], i;
|
|
37
|
-
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
38
|
-
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
39
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
40
|
-
};
|
|
41
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
27
|
};
|
|
@@ -103,288 +87,258 @@ class AICommandRouter {
|
|
|
103
87
|
constructor() {
|
|
104
88
|
this.configManager = new config_1.AIConfigManager();
|
|
105
89
|
}
|
|
106
|
-
execute({ agent, addtionalArgs, log, template, useDefaultConfig }) {
|
|
90
|
+
async execute({ agent, addtionalArgs, log, template, useDefaultConfig }) {
|
|
107
91
|
var _a;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
92
|
+
if ((0, config_1.isValidAgent)(agent) !== true) {
|
|
93
|
+
log.error((0, i18n_1.t)('❌ 无效的 agent: {{agent}}', { agent }));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const config = await this.configManager.loadConfig();
|
|
97
|
+
if (!config) {
|
|
98
|
+
log.warn((0, i18n_1.t)("⚠️ 未检测到 AI ToolKit CLI 配置,请运行 'tcb ai --setup' 进行配置"));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const agentUpperCased = agent.toUpperCase();
|
|
102
|
+
const agentConfig = (_a = config.agents) === null || _a === void 0 ? void 0 : _a[agent];
|
|
103
|
+
if (!agentConfig) {
|
|
104
|
+
log.warn((0, i18n_1.t)('⚠️ 未找到 {{agent}} 配置,请运行 tcb ai --setup 进行配置', { agent: agentUpperCased }));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
log.info((0, i18n_1.t)('🚀 启动 {{agent}} AI CLI 工具', { agent: chalk_1.default.bold(agentUpperCased) }));
|
|
108
|
+
try {
|
|
109
|
+
await this.checkToolkitConfig({ agent, log, template, useDefaultConfig });
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
log.warn((0, i18n_1.t)('⚠️ 云开发功能检查失败,但 AI 工具仍可正常使用'));
|
|
113
|
+
}
|
|
114
|
+
const isValid = await this.validateAgentConfig(agent, agentConfig, log);
|
|
115
|
+
if (!isValid) {
|
|
116
|
+
log.warn((0, i18n_1.t)('⚠️ {{agent}} 配置无效,请重新配置', { agent: agent.toUpperCase() }));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.executeAgentWithConfig(agent, agentConfig, this.parseArgs(addtionalArgs), log);
|
|
120
|
+
}
|
|
121
|
+
async checkToolkitConfig(checkOptions) {
|
|
122
|
+
const { agent, log, template, useDefaultConfig } = checkOptions;
|
|
123
|
+
const { missingFiles } = await this.configManager.checkToolkitConfig(agent);
|
|
124
|
+
if (missingFiles.length > 0) {
|
|
125
|
+
const skipTemplate = await this.checkSkipTemplateConfig();
|
|
126
|
+
if (skipTemplate) {
|
|
127
|
+
log.info((0, i18n_1.t)('🚫 已跳过模板下载,无法使用云开发 MCP 及 AI 规则。如需使用,请参考 tcb pull --help'));
|
|
134
128
|
return;
|
|
135
129
|
}
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
log.log('');
|
|
131
|
+
await this.downloadTemplate({ log, template, agent, useDefaultConfig });
|
|
132
|
+
log.log('');
|
|
133
|
+
}
|
|
138
134
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
log.log('');
|
|
150
|
-
yield this.downloadTemplate({ log, template, agent, useDefaultConfig });
|
|
151
|
-
log.log('');
|
|
152
|
-
}
|
|
153
|
-
});
|
|
135
|
+
async checkSkipTemplateConfig() {
|
|
136
|
+
try {
|
|
137
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
138
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
139
|
+
const skipFile = path.join(process.cwd(), '.skip-template');
|
|
140
|
+
return await fs.pathExists(skipFile);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
154
145
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
});
|
|
146
|
+
async saveSkipTemplateConfig() {
|
|
147
|
+
try {
|
|
148
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
149
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
150
|
+
const skipFile = path.join(process.cwd(), '.skip-template');
|
|
151
|
+
await fs.writeFile(skipFile, new Date().toISOString());
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
}
|
|
167
155
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
yield fs.writeFile(skipFile, new Date().toISOString());
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
156
|
+
async downloadTemplate(options) {
|
|
157
|
+
const { log, template, agent, useDefaultConfig } = options;
|
|
158
|
+
try {
|
|
159
|
+
let templateType;
|
|
160
|
+
if (template) {
|
|
161
|
+
templateType = template;
|
|
177
162
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
-
const { log, template, agent, useDefaultConfig } = options;
|
|
183
|
-
try {
|
|
184
|
-
let templateType;
|
|
185
|
-
if (template) {
|
|
186
|
-
templateType = template;
|
|
163
|
+
else {
|
|
164
|
+
if (useDefaultConfig) {
|
|
165
|
+
templateType = 'rules';
|
|
187
166
|
}
|
|
188
167
|
else {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
name: '
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
default: 'miniprogram'
|
|
207
|
-
}
|
|
208
|
-
]);
|
|
209
|
-
templateType = selectedType;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (templateType === 'none') {
|
|
213
|
-
log.info((0, i18n_1.t)('🚫 跳过模板下载'));
|
|
214
|
-
yield this.saveSkipTemplateConfig();
|
|
215
|
-
return;
|
|
168
|
+
const { templateType: selectedType } = await inquirer_1.default.prompt([
|
|
169
|
+
{
|
|
170
|
+
type: 'list',
|
|
171
|
+
name: 'templateType',
|
|
172
|
+
message: (0, i18n_1.t)('下载模板以获取完整的开发体验: {{hint}}', { hint: const_1.LIST_HINT }),
|
|
173
|
+
choices: [
|
|
174
|
+
{ name: (0, i18n_1.t)('🟦 微信小程序 + CloudBase'), value: 'miniprogram' },
|
|
175
|
+
{ name: (0, i18n_1.t)('🚀 Web 应用 - React + CloudBase'), value: 'react' },
|
|
176
|
+
{ name: (0, i18n_1.t)('🟢 Web 应用 - Vue + CloudBase'), value: 'vue' },
|
|
177
|
+
{ name: (0, i18n_1.t)('🌈 跨端应用 - UniApp + CloudBase'), value: 'uniapp' },
|
|
178
|
+
{ name: (0, i18n_1.t)('🧩 只下载 AI 规则和配置'), value: 'rules' },
|
|
179
|
+
{ name: (0, i18n_1.t)('🚫 不下载模板'), value: 'none' }
|
|
180
|
+
],
|
|
181
|
+
default: 'miniprogram'
|
|
182
|
+
}
|
|
183
|
+
]);
|
|
184
|
+
templateType = selectedType;
|
|
216
185
|
}
|
|
217
|
-
log.info((0, i18n_1.t)('📦 正在下载并解压 {{templateType}} 模板...', { templateType }));
|
|
218
|
-
yield this.downloadAndExtractTemplate({ templateType, log, agent: agent, useDefaultConfig: useDefaultConfig || false });
|
|
219
|
-
log.info((0, i18n_1.t)('✅ {{templateType}} 模板配置完成', { templateType }));
|
|
220
186
|
}
|
|
221
|
-
|
|
222
|
-
log.
|
|
223
|
-
|
|
187
|
+
if (templateType === 'none') {
|
|
188
|
+
log.info((0, i18n_1.t)('🚫 跳过模板下载'));
|
|
189
|
+
await this.saveSkipTemplateConfig();
|
|
190
|
+
return;
|
|
224
191
|
}
|
|
225
|
-
|
|
192
|
+
log.info((0, i18n_1.t)('📦 正在下载并解压 {{templateType}} 模板...', { templateType }));
|
|
193
|
+
await this.downloadAndExtractTemplate({ templateType, log, agent: agent, useDefaultConfig: useDefaultConfig || false });
|
|
194
|
+
log.info((0, i18n_1.t)('✅ {{templateType}} 模板配置完成', { templateType }));
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
log.error((0, i18n_1.t)('❌ 配置失败: {{error}}', { error: error.message }));
|
|
198
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('模板下载失败'), { original: error });
|
|
199
|
+
}
|
|
226
200
|
}
|
|
227
|
-
downloadAndExtractTemplate(options) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
201
|
+
async downloadAndExtractTemplate(options) {
|
|
202
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
203
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
204
|
+
const os = await Promise.resolve().then(() => __importStar(require('os')));
|
|
205
|
+
const unzipper = await Promise.resolve().then(() => __importStar(require('unzipper')));
|
|
206
|
+
const https = await Promise.resolve().then(() => __importStar(require('https')));
|
|
207
|
+
let ConfigParser;
|
|
208
|
+
const { templateType, log, agent, useDefaultConfig } = options;
|
|
209
|
+
try {
|
|
210
|
+
ConfigParser = (await Promise.resolve().then(() => __importStar(require('@cloudbase/toolbox')))).ConfigParser;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
ConfigParser = undefined;
|
|
214
|
+
}
|
|
215
|
+
const templateMap = {
|
|
216
|
+
rules: {
|
|
217
|
+
url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-project.zip'
|
|
218
|
+
},
|
|
219
|
+
react: {
|
|
220
|
+
url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-react-template.zip'
|
|
221
|
+
},
|
|
222
|
+
vue: {
|
|
223
|
+
url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-vue-template.zip'
|
|
224
|
+
},
|
|
225
|
+
miniprogram: {
|
|
226
|
+
url: 'https://static.cloudbase.net/cloudbase-examples/miniprogram-cloudbase-miniprogram-template.zip'
|
|
227
|
+
},
|
|
228
|
+
uniapp: {
|
|
229
|
+
url: 'https://static.cloudbase.net/cloudbase-examples/universal-cloudbase-uniapp-template.zip'
|
|
242
230
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
uniapp: {
|
|
257
|
-
url: 'https://static.cloudbase.net/cloudbase-examples/universal-cloudbase-uniapp-template.zip'
|
|
231
|
+
};
|
|
232
|
+
const template = templateMap[templateType];
|
|
233
|
+
if (!template)
|
|
234
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('未知模板类型'));
|
|
235
|
+
const tmpDir = os.tmpdir();
|
|
236
|
+
const zipPath = path.join(tmpDir, `cloudbase-template-${templateType}-${Date.now()}.zip`);
|
|
237
|
+
await new Promise((resolve, reject) => {
|
|
238
|
+
const file = fs.createWriteStream(zipPath);
|
|
239
|
+
https
|
|
240
|
+
.get(template.url, (response) => {
|
|
241
|
+
if (response.statusCode !== 200) {
|
|
242
|
+
reject(new Error((0, i18n_1.t)('下载失败: {{statusCode}}', { statusCode: response.statusCode })));
|
|
243
|
+
return;
|
|
258
244
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const tmpDir = os.tmpdir();
|
|
264
|
-
const zipPath = path.join(tmpDir, `cloudbase-template-${templateType}-${Date.now()}.zip`);
|
|
265
|
-
yield new Promise((resolve, reject) => {
|
|
266
|
-
const file = fs.createWriteStream(zipPath);
|
|
267
|
-
https
|
|
268
|
-
.get(template.url, (response) => {
|
|
269
|
-
if (response.statusCode !== 200) {
|
|
270
|
-
reject(new Error((0, i18n_1.t)('下载失败: {{statusCode}}', { statusCode: response.statusCode })));
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
response.pipe(file);
|
|
274
|
-
file.on('finish', () => {
|
|
275
|
-
file.close();
|
|
276
|
-
resolve();
|
|
277
|
-
});
|
|
278
|
-
})
|
|
279
|
-
.on('error', (err) => {
|
|
280
|
-
fs.unlink(zipPath);
|
|
281
|
-
reject(err);
|
|
245
|
+
response.pipe(file);
|
|
246
|
+
file.on('finish', () => {
|
|
247
|
+
file.close();
|
|
248
|
+
resolve();
|
|
282
249
|
});
|
|
250
|
+
})
|
|
251
|
+
.on('error', (err) => {
|
|
252
|
+
fs.unlink(zipPath);
|
|
253
|
+
reject(err);
|
|
283
254
|
});
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (key in target &&
|
|
332
|
-
typeof target[key] === 'object' &&
|
|
333
|
-
typeof source[key] === 'object') {
|
|
334
|
-
result[key] = deepMerge(target[key], source[key]);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return result;
|
|
338
|
-
};
|
|
339
|
-
let merged = deepMerge(localJson, templateJson);
|
|
340
|
-
yield fs.writeJson(destPath, merged, { spaces: 2 });
|
|
341
|
-
}
|
|
342
|
-
catch (e) {
|
|
343
|
-
log.warn((0, i18n_1.t)('cloudbaserc.json 合并失败,已跳过: {{error}}', { error: e.message }));
|
|
344
|
-
}
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
if (yield fs.pathExists(destPath)) {
|
|
348
|
-
if (useDefaultConfig) {
|
|
349
|
-
entry.autodrain();
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
const { confirmOverwriteFile } = yield inquirer_1.default.prompt([
|
|
353
|
-
{
|
|
354
|
-
type: 'confirm',
|
|
355
|
-
name: 'confirmOverwriteFile',
|
|
356
|
-
message: (0, i18n_1.t)('检测到已存在文件 {{entryPath}},是否覆盖? {{hint}}', {
|
|
357
|
-
entryPath,
|
|
358
|
-
hint: (0, const_1.getBooleanHint)(false)
|
|
359
|
-
}),
|
|
360
|
-
default: false
|
|
361
|
-
}
|
|
362
|
-
]);
|
|
363
|
-
if (!confirmOverwriteFile) {
|
|
364
|
-
entry.autodrain();
|
|
365
|
-
continue;
|
|
255
|
+
});
|
|
256
|
+
const extractDir = process.cwd();
|
|
257
|
+
const zipStream = fs.createReadStream(zipPath).pipe(unzipper.Parse({ forceStream: true }));
|
|
258
|
+
const agentDirs = ['.claude', '.codex', '.qwen', '.codebuddy', '.cursor'];
|
|
259
|
+
for await (const entry of zipStream) {
|
|
260
|
+
const entryPath = entry.path;
|
|
261
|
+
const destPath = path.join(extractDir, entryPath);
|
|
262
|
+
if (useDefaultConfig && agent) {
|
|
263
|
+
const currentAgentDir = `.${agent}`;
|
|
264
|
+
const isAgentDir = agentDirs.some(dir => entryPath.startsWith(dir + '/') || entryPath === dir);
|
|
265
|
+
if (isAgentDir && !entryPath.startsWith(currentAgentDir + '/') && entryPath !== currentAgentDir) {
|
|
266
|
+
log.debug && log.debug((0, i18n_1.t)('跳过不匹配的 agent 目录: {{entryPath}} (当前 agent: {{agent}})', { entryPath, agent }));
|
|
267
|
+
entry.autodrain();
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (entry.type === 'Directory') {
|
|
272
|
+
await fs.ensureDir(destPath);
|
|
273
|
+
entry.autodrain();
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (MCP_CONFIG_SET.has(entryPath)) {
|
|
277
|
+
try {
|
|
278
|
+
await this.mergeMcpConfig(entry, destPath, inferConfigFormat(entryPath), log);
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
log.warn((0, i18n_1.t)('MCP 配置合并失败 {{entryPath}}:{{error}},已跳过', { entryPath, error: e.message }));
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (entryPath === 'cloudbaserc.json' && (await fs.pathExists(destPath))) {
|
|
286
|
+
try {
|
|
287
|
+
const templateContent = await entry.buffer();
|
|
288
|
+
const templateJson = JSON.parse(templateContent.toString('utf8'));
|
|
289
|
+
const localJson = await fs.readJson(destPath);
|
|
290
|
+
const deepMerge = (target, source) => {
|
|
291
|
+
if (typeof target !== 'object' ||
|
|
292
|
+
typeof source !== 'object' ||
|
|
293
|
+
!target ||
|
|
294
|
+
!source)
|
|
295
|
+
return target;
|
|
296
|
+
const result = { ...source, ...target };
|
|
297
|
+
for (const key of Object.keys(source)) {
|
|
298
|
+
if (key in target &&
|
|
299
|
+
typeof target[key] === 'object' &&
|
|
300
|
+
typeof source[key] === 'object') {
|
|
301
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
366
302
|
}
|
|
367
303
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
finally {
|
|
374
|
-
_e = true;
|
|
375
|
-
}
|
|
304
|
+
return result;
|
|
305
|
+
};
|
|
306
|
+
let merged = deepMerge(localJson, templateJson);
|
|
307
|
+
await fs.writeJson(destPath, merged, { spaces: 2 });
|
|
376
308
|
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
log.warn((0, i18n_1.t)('cloudbaserc.json 合并失败,已跳过: {{error}}', { error: e.message }));
|
|
311
|
+
}
|
|
312
|
+
continue;
|
|
377
313
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
314
|
+
if (await fs.pathExists(destPath)) {
|
|
315
|
+
if (useDefaultConfig) {
|
|
316
|
+
entry.autodrain();
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const { confirmOverwriteFile } = await inquirer_1.default.prompt([
|
|
320
|
+
{
|
|
321
|
+
type: 'confirm',
|
|
322
|
+
name: 'confirmOverwriteFile',
|
|
323
|
+
message: (0, i18n_1.t)('检测到已存在文件 {{entryPath}},是否覆盖? {{hint}}', {
|
|
324
|
+
entryPath,
|
|
325
|
+
hint: (0, const_1.getBooleanHint)(false)
|
|
326
|
+
}),
|
|
327
|
+
default: false
|
|
328
|
+
}
|
|
329
|
+
]);
|
|
330
|
+
if (!confirmOverwriteFile) {
|
|
331
|
+
entry.autodrain();
|
|
332
|
+
continue;
|
|
382
333
|
}
|
|
383
|
-
finally { if (e_1) throw e_1.error; }
|
|
384
334
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
335
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
336
|
+
await new Promise((res, rej) => {
|
|
337
|
+
entry.pipe(fs.createWriteStream(destPath)).on('finish', res).on('error', rej);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
await fs.unlink(zipPath);
|
|
341
|
+
await this.modifyMCPConfigs(extractDir, log);
|
|
388
342
|
}
|
|
389
343
|
getInstallCommand(agent) {
|
|
390
344
|
switch (agent) {
|
|
@@ -427,807 +381,778 @@ class AICommandRouter {
|
|
|
427
381
|
return { success: false, message: (0, i18n_1.t)('# 请查阅 {{agent}} 的官方安装文档', { agent }) };
|
|
428
382
|
}
|
|
429
383
|
}
|
|
430
|
-
executeCommand(command, args, env, log) {
|
|
431
|
-
return
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
});
|
|
449
|
-
log.error((0, i18n_1.t)('❌ {{errorMsg}}', { errorMsg }));
|
|
450
|
-
if (code === 127) {
|
|
451
|
-
log.info((0, i18n_1.t)('💡 命令未找到,请检查是否正确安装'));
|
|
452
|
-
log.info((0, i18n_1.t)('📦 安装命令: {{installCmd}}', { installCmd: this.getInstallCommand(command).message }));
|
|
453
|
-
}
|
|
454
|
-
else if (code === 1) {
|
|
455
|
-
log.info((0, i18n_1.t)('💡 可能是参数错误或配置问题'));
|
|
456
|
-
log.info((0, i18n_1.t)('🔧 请检查 API 密钥、模型名称等配置是否正确'));
|
|
457
|
-
}
|
|
458
|
-
if (command === 'ccr') {
|
|
459
|
-
log.info((0, i18n_1.t)('🔧 请前往 {{logPath}} 查看日志', { logPath: const_1.CLAUDE_CODE_ROUTER_LOG_PATH }));
|
|
460
|
-
}
|
|
461
|
-
reject(new Error(errorMsg));
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
child.on('error', (err) => {
|
|
465
|
-
const errorMsg = (0, i18n_1.t)('启动失败: {{command}} ({{error}})', { command, error: err.message });
|
|
384
|
+
async executeCommand(command, args, env, log) {
|
|
385
|
+
return new Promise((resolve, reject) => {
|
|
386
|
+
const useShell = process.platform === 'win32';
|
|
387
|
+
const child = (0, child_process_1.spawn)(command, args, {
|
|
388
|
+
stdio: 'inherit',
|
|
389
|
+
env,
|
|
390
|
+
shell: useShell
|
|
391
|
+
});
|
|
392
|
+
child.on('close', (code) => {
|
|
393
|
+
if (code === 0) {
|
|
394
|
+
resolve();
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
const errorMsg = (0, i18n_1.t)('命令执行失败: {{command}} {{args}} (退出码: {{code}})', {
|
|
398
|
+
command,
|
|
399
|
+
args: args.join(' '),
|
|
400
|
+
code
|
|
401
|
+
});
|
|
466
402
|
log.error((0, i18n_1.t)('❌ {{errorMsg}}', { errorMsg }));
|
|
467
|
-
if (
|
|
468
|
-
log.info((0, i18n_1.t)('💡
|
|
403
|
+
if (code === 127) {
|
|
404
|
+
log.info((0, i18n_1.t)('💡 命令未找到,请检查是否正确安装'));
|
|
469
405
|
log.info((0, i18n_1.t)('📦 安装命令: {{installCmd}}', { installCmd: this.getInstallCommand(command).message }));
|
|
470
406
|
}
|
|
471
|
-
else if (
|
|
472
|
-
log.info((0, i18n_1.t)('💡
|
|
407
|
+
else if (code === 1) {
|
|
408
|
+
log.info((0, i18n_1.t)('💡 可能是参数错误或配置问题'));
|
|
409
|
+
log.info((0, i18n_1.t)('🔧 请检查 API 密钥、模型名称等配置是否正确'));
|
|
410
|
+
}
|
|
411
|
+
if (command === 'ccr') {
|
|
412
|
+
log.info((0, i18n_1.t)('🔧 请前往 {{logPath}} 查看日志', { logPath: const_1.CLAUDE_CODE_ROUTER_LOG_PATH }));
|
|
473
413
|
}
|
|
474
414
|
reject(new Error(errorMsg));
|
|
475
|
-
}
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
child.on('error', (err) => {
|
|
418
|
+
const errorMsg = (0, i18n_1.t)('启动失败: {{command}} ({{error}})', { command, error: err.message });
|
|
419
|
+
log.error((0, i18n_1.t)('❌ {{errorMsg}}', { errorMsg }));
|
|
420
|
+
if (err.message.includes('ENOENT')) {
|
|
421
|
+
log.info((0, i18n_1.t)('💡 命令不存在,请先安装对应的 AI CLI 工具'));
|
|
422
|
+
log.info((0, i18n_1.t)('📦 安装命令: {{installCmd}}', { installCmd: this.getInstallCommand(command).message }));
|
|
423
|
+
}
|
|
424
|
+
else if (err.message.includes('EACCES')) {
|
|
425
|
+
log.info((0, i18n_1.t)('💡 权限不足,请检查文件权限或使用管理员权限'));
|
|
426
|
+
}
|
|
427
|
+
reject(new Error(errorMsg));
|
|
476
428
|
});
|
|
477
429
|
});
|
|
478
430
|
}
|
|
479
|
-
isToolAvailable(command) {
|
|
480
|
-
return
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
resolve(false);
|
|
491
|
-
});
|
|
431
|
+
async isToolAvailable(command) {
|
|
432
|
+
return new Promise((resolve) => {
|
|
433
|
+
const child = (0, child_process_1.spawn)(command, ['--version'], {
|
|
434
|
+
stdio: 'pipe',
|
|
435
|
+
shell: true
|
|
436
|
+
});
|
|
437
|
+
child.on('close', (code) => {
|
|
438
|
+
resolve(code === 0);
|
|
439
|
+
});
|
|
440
|
+
child.on('error', () => {
|
|
441
|
+
resolve(false);
|
|
492
442
|
});
|
|
493
443
|
});
|
|
494
444
|
}
|
|
495
|
-
validateAgentConfig(agent, agentConfig, log) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
});
|
|
445
|
+
async validateAgentConfig(agent, agentConfig, log) {
|
|
446
|
+
if (!agentConfig) {
|
|
447
|
+
log.error((0, i18n_1.t)('❌ Agent 配置为空'));
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
const validateResult = (0, const_1.getAgentConfigValidator)(agent)(agentConfig);
|
|
451
|
+
if (validateResult.success === true) {
|
|
452
|
+
log.debug((0, i18n_1.t)('✅ Agent 配置验证通过'));
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
log.error((0, i18n_1.t)('❌ {{agent}} 配置无效: {{error}}', { agent, error: validateResult.error }));
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
511
459
|
}
|
|
512
|
-
executeAgentWithConfig(agent, agentConfig, additionalArgs, log) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
try {
|
|
517
|
-
if (agentConfig.type === 'cloudbase') {
|
|
518
|
-
return yield this.executeClaudeCloudbaseAgent(agentConfig, additionalArgs, log);
|
|
519
|
-
}
|
|
520
|
-
else if (agentConfig.type === 'custom') {
|
|
521
|
-
return yield this.executeClaudeAgent(agentConfig, additionalArgs, log);
|
|
522
|
-
}
|
|
523
|
-
else {
|
|
524
|
-
return yield this.executeNoneClaudeAgent(additionalArgs, log);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
catch (_) {
|
|
528
|
-
(0, claudeWindows_1.claudeWindowsCheck)(log);
|
|
529
|
-
return process.exit(1);
|
|
530
|
-
}
|
|
531
|
-
case const_1.QWEN.value:
|
|
532
|
-
if (agentConfig.type === 'cloudbase') {
|
|
533
|
-
return yield this.executeQwenCloudbaseAgent(agentConfig, additionalArgs, log);
|
|
534
|
-
}
|
|
535
|
-
else if (agentConfig.type === 'custom') {
|
|
536
|
-
return yield this.executeQwenAgent(agentConfig, additionalArgs, log);
|
|
537
|
-
}
|
|
538
|
-
else {
|
|
539
|
-
return yield this.executeNoneQwenAgent(additionalArgs, log);
|
|
540
|
-
}
|
|
541
|
-
case const_1.CODEX.value:
|
|
460
|
+
async executeAgentWithConfig(agent, agentConfig, additionalArgs, log) {
|
|
461
|
+
switch (agent) {
|
|
462
|
+
case const_1.CLAUDE.value:
|
|
463
|
+
try {
|
|
542
464
|
if (agentConfig.type === 'cloudbase') {
|
|
543
|
-
return
|
|
465
|
+
return await this.executeClaudeCloudbaseAgent(agentConfig, additionalArgs, log);
|
|
544
466
|
}
|
|
545
467
|
else if (agentConfig.type === 'custom') {
|
|
546
|
-
return
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
return yield this.executeNoneCodexAgent(additionalArgs, log);
|
|
550
|
-
}
|
|
551
|
-
case const_1.AIDER.value:
|
|
552
|
-
if (agentConfig.type === 'cloudbase') {
|
|
553
|
-
return yield this.executeAiderCloudbaseAgent(agentConfig, additionalArgs, log);
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
return yield this.executeAiderAgent(agentConfig, additionalArgs, log);
|
|
557
|
-
}
|
|
558
|
-
case const_1.CURSOR.value:
|
|
559
|
-
return yield this.executeNoneCursorAgent(additionalArgs, log);
|
|
560
|
-
case const_1.CODEBUDDY.value:
|
|
561
|
-
if (agentConfig.type === 'custom') {
|
|
562
|
-
return yield this.executeCodebuddyAgent(agentConfig, additionalArgs, log);
|
|
468
|
+
return await this.executeClaudeAgent(agentConfig, additionalArgs, log);
|
|
563
469
|
}
|
|
564
470
|
else {
|
|
565
|
-
return
|
|
471
|
+
return await this.executeNoneClaudeAgent(additionalArgs, log);
|
|
566
472
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
473
|
+
}
|
|
474
|
+
catch (_) {
|
|
475
|
+
(0, claudeWindows_1.claudeWindowsCheck)(log);
|
|
476
|
+
return process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
case const_1.QWEN.value:
|
|
479
|
+
if (agentConfig.type === 'cloudbase') {
|
|
480
|
+
return await this.executeQwenCloudbaseAgent(agentConfig, additionalArgs, log);
|
|
481
|
+
}
|
|
482
|
+
else if (agentConfig.type === 'custom') {
|
|
483
|
+
return await this.executeQwenAgent(agentConfig, additionalArgs, log);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
return await this.executeNoneQwenAgent(additionalArgs, log);
|
|
487
|
+
}
|
|
488
|
+
case const_1.CODEX.value:
|
|
489
|
+
if (agentConfig.type === 'cloudbase') {
|
|
490
|
+
return await this.executeCodexCloudbaseAgent(agentConfig, additionalArgs, log);
|
|
491
|
+
}
|
|
492
|
+
else if (agentConfig.type === 'custom') {
|
|
493
|
+
return await this.executeCodexAgent(agentConfig, additionalArgs, log);
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
return await this.executeNoneCodexAgent(additionalArgs, log);
|
|
497
|
+
}
|
|
498
|
+
case const_1.AIDER.value:
|
|
499
|
+
if (agentConfig.type === 'cloudbase') {
|
|
500
|
+
return await this.executeAiderCloudbaseAgent(agentConfig, additionalArgs, log);
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
return await this.executeAiderAgent(agentConfig, additionalArgs, log);
|
|
504
|
+
}
|
|
505
|
+
case const_1.CURSOR.value:
|
|
506
|
+
return await this.executeNoneCursorAgent(additionalArgs, log);
|
|
507
|
+
case const_1.CODEBUDDY.value:
|
|
508
|
+
if (agentConfig.type === 'custom') {
|
|
509
|
+
return await this.executeCodebuddyAgent(agentConfig, additionalArgs, log);
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
return await this.executeNoneCodebuddyAgent(additionalArgs, log);
|
|
513
|
+
}
|
|
514
|
+
default:
|
|
515
|
+
throw new Error((0, i18n_1.t)('不支持的 AI 工具: {{agent}}', { agent }));
|
|
516
|
+
}
|
|
571
517
|
}
|
|
572
|
-
executeClaudeAgent({ apiKey, baseUrl, model }, additionalArgs, log) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
518
|
+
async executeClaudeAgent({ apiKey, baseUrl, model }, additionalArgs, log) {
|
|
519
|
+
(0, nodeVersion_1.ensureNodeVersion)('v18.0.0', log);
|
|
520
|
+
await this.ensureClaudeCode(log);
|
|
521
|
+
await this.executeCommand('claude', additionalArgs, {
|
|
522
|
+
...process.env,
|
|
523
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
524
|
+
ANTHROPIC_BASE_URL: baseUrl,
|
|
525
|
+
ANTHROPIC_MODEL: model
|
|
526
|
+
}, log);
|
|
578
527
|
}
|
|
579
|
-
executeNoneClaudeAgent(additionalArgs, log) {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
yield this.executeCommand('claude', additionalArgs, Object.assign({}, process.env), log);
|
|
583
|
-
});
|
|
528
|
+
async executeNoneClaudeAgent(additionalArgs, log) {
|
|
529
|
+
await this.ensureClaudeCode(log);
|
|
530
|
+
await this.executeCommand('claude', additionalArgs, { ...process.env }, log);
|
|
584
531
|
}
|
|
585
|
-
executeQwenAgent({ apiKey, baseUrl, model }, additionalArgs, log) {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
532
|
+
async executeQwenAgent({ apiKey, baseUrl, model }, additionalArgs, log) {
|
|
533
|
+
(0, nodeVersion_1.ensureNodeVersion)('v20.0.0', log);
|
|
534
|
+
await this.ensureQwenCode(log);
|
|
535
|
+
await this.executeCommand('qwen', additionalArgs, {
|
|
536
|
+
...process.env,
|
|
537
|
+
OPENAI_API_KEY: apiKey,
|
|
538
|
+
OPENAI_BASE_URL: baseUrl,
|
|
539
|
+
OPENAI_MODEL: model
|
|
540
|
+
}, log);
|
|
591
541
|
}
|
|
592
|
-
executeNoneQwenAgent(additionalArgs, log) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
yield this.executeCommand('qwen', additionalArgs, Object.assign({}, process.env), log);
|
|
596
|
-
});
|
|
542
|
+
async executeNoneQwenAgent(additionalArgs, log) {
|
|
543
|
+
await this.ensureQwenCode(log);
|
|
544
|
+
await this.executeCommand('qwen', additionalArgs, { ...process.env }, log);
|
|
597
545
|
}
|
|
598
|
-
executeQwenCloudbaseAgent({ provider, model }, additionalArgs, log) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
token: credential.token
|
|
611
|
-
});
|
|
612
|
-
if (!accessToken.access_token) {
|
|
613
|
-
log.error((0, i18n_1.t)('获取云开发 Access Token 失败,请运行 tcb login 后再重试,{{token}}', {
|
|
614
|
-
token: JSON.stringify(accessToken)
|
|
615
|
-
}));
|
|
616
|
-
process.exit(1);
|
|
617
|
-
}
|
|
618
|
-
const baseUrl = `${(0, url_1.getGatewayUrl)(envId)}/v1/ai/${provider}`;
|
|
619
|
-
const apiKey = accessToken.access_token;
|
|
620
|
-
yield this.executeCommand('qwen', additionalArgs, Object.assign(Object.assign({}, process.env), { OPENAI_API_KEY: apiKey, OPENAI_BASE_URL: baseUrl, OPENAI_MODEL: model }), log);
|
|
546
|
+
async executeQwenCloudbaseAgent({ provider, model }, additionalArgs, log) {
|
|
547
|
+
(0, nodeVersion_1.ensureNodeVersion)('v20.0.0', log);
|
|
548
|
+
await this.ensureQwenCode(log);
|
|
549
|
+
const _envId = await (0, config_1.createConfigParser)().get('envId');
|
|
550
|
+
await (0, auth_1.checkLogin)();
|
|
551
|
+
const credential = await (0, utils_1.getCredential)({}, {});
|
|
552
|
+
const envId = await (0, env_1.ensureValidEnv)(_envId, log);
|
|
553
|
+
const accessToken = await (0, utils_1.rawFetchAccessToken)({
|
|
554
|
+
envId,
|
|
555
|
+
secretId: credential.secretId,
|
|
556
|
+
secretKey: credential.secretKey,
|
|
557
|
+
token: credential.token
|
|
621
558
|
});
|
|
559
|
+
if (!accessToken.access_token) {
|
|
560
|
+
log.error((0, i18n_1.t)('获取云开发 Access Token 失败,请运行 tcb login 后再重试,{{token}}', {
|
|
561
|
+
token: JSON.stringify(accessToken)
|
|
562
|
+
}));
|
|
563
|
+
process.exit(1);
|
|
564
|
+
}
|
|
565
|
+
const baseUrl = `${(0, url_1.getGatewayUrl)(envId)}/v1/ai/${provider}`;
|
|
566
|
+
const apiKey = accessToken.access_token;
|
|
567
|
+
await this.executeCommand('qwen', additionalArgs, {
|
|
568
|
+
...process.env,
|
|
569
|
+
OPENAI_API_KEY: apiKey,
|
|
570
|
+
OPENAI_BASE_URL: baseUrl,
|
|
571
|
+
OPENAI_MODEL: model
|
|
572
|
+
}, log);
|
|
622
573
|
}
|
|
623
|
-
executeClaudeCloudbaseAgent({ provider, model, transformer }, additionalArgs, log) {
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
yield this.executeCommand('ccr', ['code', ...additionalArgs], Object.assign({}, process.env), log);
|
|
631
|
-
});
|
|
574
|
+
async executeClaudeCloudbaseAgent({ provider, model, transformer }, additionalArgs, log) {
|
|
575
|
+
(0, nodeVersion_1.ensureNodeVersion)('v18.19.0', log);
|
|
576
|
+
await this.ensureClaudeCodeRouter(log);
|
|
577
|
+
await this.ensureClaudeCode(log);
|
|
578
|
+
await this.configureClaudeCodeRouter(provider, model, transformer, log);
|
|
579
|
+
await this.restartClaudeCodeRouter(log);
|
|
580
|
+
await this.executeCommand('ccr', ['code', ...additionalArgs], { ...process.env }, log);
|
|
632
581
|
}
|
|
633
|
-
restartClaudeCodeRouter(log) {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
break;
|
|
654
|
-
}
|
|
582
|
+
async restartClaudeCodeRouter(log) {
|
|
583
|
+
try {
|
|
584
|
+
await this.executeCommand('ccr', ['restart'], process.env, log);
|
|
585
|
+
}
|
|
586
|
+
catch (e) {
|
|
587
|
+
log.error((0, i18n_1.t)('执行 ccr restart 失败,尝试执行 ccr stop && ccr start'));
|
|
588
|
+
await this.executeCommand('ccr', ['stop'], process.env, log);
|
|
589
|
+
this.executeCommand('ccr', ['start'], process.env, log);
|
|
590
|
+
const max = 3;
|
|
591
|
+
let current = 0;
|
|
592
|
+
while (current < max) {
|
|
593
|
+
current++;
|
|
594
|
+
if (current > max) {
|
|
595
|
+
throw new Error((0, i18n_1.t)('ccr 重启完成失败,可前往 {{logPath}} 查看日志', { logPath: const_1.CLAUDE_CODE_ROUTER_LOG_PATH }));
|
|
596
|
+
}
|
|
597
|
+
log.info((0, i18n_1.t)('{{current}}/{{max}}等待 ccr 重启完成...', { current, max }));
|
|
598
|
+
const isRunning = await this.isClaudeCodeRouterRunning();
|
|
599
|
+
if (isRunning) {
|
|
600
|
+
log.info((0, i18n_1.t)('ccr 重启完成'));
|
|
601
|
+
break;
|
|
655
602
|
}
|
|
656
603
|
}
|
|
657
|
-
}
|
|
604
|
+
}
|
|
658
605
|
}
|
|
659
|
-
isClaudeCodeRouterRunning() {
|
|
660
|
-
return
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
resolve(true);
|
|
671
|
-
child.kill();
|
|
672
|
-
}
|
|
673
|
-
});
|
|
674
|
-
setTimeout(() => {
|
|
675
|
-
resolve(false);
|
|
606
|
+
async isClaudeCodeRouterRunning() {
|
|
607
|
+
return new Promise((resolve) => {
|
|
608
|
+
const child = (0, child_process_1.spawn)('ccr', ['status'], {
|
|
609
|
+
stdio: 'pipe',
|
|
610
|
+
env: process.env,
|
|
611
|
+
shell: process.platform === 'win32'
|
|
612
|
+
});
|
|
613
|
+
child.stdout.on('data', (data) => {
|
|
614
|
+
const str = data.toString();
|
|
615
|
+
if (str.includes('Status: Running')) {
|
|
616
|
+
resolve(true);
|
|
676
617
|
child.kill();
|
|
677
|
-
}
|
|
618
|
+
}
|
|
678
619
|
});
|
|
620
|
+
setTimeout(() => {
|
|
621
|
+
resolve(false);
|
|
622
|
+
child.kill();
|
|
623
|
+
}, 3000);
|
|
679
624
|
});
|
|
680
625
|
}
|
|
681
|
-
configureClaudeCodeRouter(provider, model, transformer, log) {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
626
|
+
async configureClaudeCodeRouter(provider, model, transformer, log) {
|
|
627
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
628
|
+
const _envId = await (0, config_1.createConfigParser)().get('envId');
|
|
629
|
+
await (0, auth_1.checkLogin)();
|
|
630
|
+
const credential = await (0, utils_1.getCredential)({}, {});
|
|
631
|
+
const envId = await (0, env_1.ensureValidEnv)(_envId, log);
|
|
632
|
+
const accessToken = await (0, utils_1.rawFetchAccessToken)({
|
|
633
|
+
envId,
|
|
634
|
+
secretId: credential.secretId,
|
|
635
|
+
secretKey: credential.secretKey,
|
|
636
|
+
token: credential.token
|
|
637
|
+
});
|
|
638
|
+
if (!accessToken.access_token) {
|
|
639
|
+
log.error((0, i18n_1.t)('获取云开发 Access Token 失败,请运行 tcb login 后再重试,{{token}}', {
|
|
640
|
+
token: JSON.stringify(accessToken)
|
|
641
|
+
}));
|
|
642
|
+
process.exit(1);
|
|
643
|
+
}
|
|
644
|
+
const cloudbaseProvider = {
|
|
645
|
+
name: 'cloudbase',
|
|
646
|
+
api_base_url: `${(0, url_1.getGatewayUrl)(envId)}/v1/ai/${provider}/chat/completions`,
|
|
647
|
+
api_key: accessToken.access_token,
|
|
648
|
+
models: [model]
|
|
649
|
+
};
|
|
650
|
+
if (transformer && transformer.trim().length > 0) {
|
|
651
|
+
cloudbaseProvider.transformer = { use: [transformer] };
|
|
652
|
+
}
|
|
653
|
+
await fs.ensureFile(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH);
|
|
654
|
+
const claudeCodeRouterConfig = await fs.readFile(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, 'utf-8');
|
|
655
|
+
if (claudeCodeRouterConfig.trim().length === 0) {
|
|
656
|
+
log.debug((0, i18n_1.t)('🛠️ claude-code-router 配置为空,写入 Cloudbase 配置...'));
|
|
657
|
+
await fs.writeJson(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, { Providers: [cloudbaseProvider] });
|
|
658
|
+
log.debug((0, i18n_1.t)('✅ claude-code-router 配置完成'));
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
const config = safeParseJson(claudeCodeRouterConfig);
|
|
662
|
+
if (!config) {
|
|
663
|
+
const { shouldOverwrite } = await inquirer_1.default.prompt([
|
|
664
|
+
{
|
|
665
|
+
type: 'confirm',
|
|
666
|
+
name: 'shouldOverwrite',
|
|
667
|
+
message: (0, i18n_1.t)('claude-code-router 配置文件非合法 JSON ,是否覆盖? {{hint}}', { hint: (0, const_1.getBooleanHint)() })
|
|
668
|
+
}
|
|
669
|
+
]);
|
|
670
|
+
if (shouldOverwrite) {
|
|
671
|
+
await fs.writeJson(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, {
|
|
672
|
+
Providers: [cloudbaseProvider]
|
|
673
|
+
}, { spaces: 2 });
|
|
674
|
+
log.debug((0, i18n_1.t)('✅ claude-code-router 配置完成'));
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
log.error((0, i18n_1.t)('❌ claude-code-router 配置文件非合法 JSON ,请手动修复'));
|
|
678
|
+
process.exit(1);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (typeof config !== 'object' || config === null) {
|
|
682
|
+
log.error((0, i18n_1.t)('❌ 未知 claude-code-router 配置文件格式请手动修复'));
|
|
698
683
|
process.exit(1);
|
|
699
684
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
if (transformer && transformer.trim().length > 0) {
|
|
707
|
-
cloudbaseProvider.transformer = { use: [transformer] };
|
|
685
|
+
if (!('Providers' in config)) {
|
|
686
|
+
config.Providers = [cloudbaseProvider];
|
|
687
|
+
}
|
|
688
|
+
if (!Array.isArray(config.Providers)) {
|
|
689
|
+
log.error((0, i18n_1.t)('❌ claude-code-router 配置文件 Providers 字段非数组类型,请手动修复'));
|
|
690
|
+
process.exit(1);
|
|
708
691
|
}
|
|
709
|
-
|
|
710
|
-
const
|
|
711
|
-
if (
|
|
712
|
-
|
|
713
|
-
yield fs.writeJson(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, { Providers: [cloudbaseProvider] });
|
|
714
|
-
log.debug((0, i18n_1.t)('✅ claude-code-router 配置完成'));
|
|
692
|
+
const providers = config.Providers;
|
|
693
|
+
const index = providers.findIndex((p) => typeof p === 'object' && (p === null || p === void 0 ? void 0 : p.name) === 'cloudbase');
|
|
694
|
+
if (index !== -1) {
|
|
695
|
+
providers[index] = cloudbaseProvider;
|
|
715
696
|
}
|
|
716
697
|
else {
|
|
717
|
-
|
|
718
|
-
if (!config) {
|
|
719
|
-
const { shouldOverwrite } = yield inquirer_1.default.prompt([
|
|
720
|
-
{
|
|
721
|
-
type: 'confirm',
|
|
722
|
-
name: 'shouldOverwrite',
|
|
723
|
-
message: (0, i18n_1.t)('claude-code-router 配置文件非合法 JSON ,是否覆盖? {{hint}}', { hint: (0, const_1.getBooleanHint)() })
|
|
724
|
-
}
|
|
725
|
-
]);
|
|
726
|
-
if (shouldOverwrite) {
|
|
727
|
-
yield fs.writeJson(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, {
|
|
728
|
-
Providers: [cloudbaseProvider]
|
|
729
|
-
}, { spaces: 2 });
|
|
730
|
-
log.debug((0, i18n_1.t)('✅ claude-code-router 配置完成'));
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
log.error((0, i18n_1.t)('❌ claude-code-router 配置文件非合法 JSON ,请手动修复'));
|
|
734
|
-
process.exit(1);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
if (typeof config !== 'object' || config === null) {
|
|
738
|
-
log.error((0, i18n_1.t)('❌ 未知 claude-code-router 配置文件格式请手动修复'));
|
|
739
|
-
process.exit(1);
|
|
740
|
-
}
|
|
741
|
-
if (!('Providers' in config)) {
|
|
742
|
-
config.Providers = [cloudbaseProvider];
|
|
743
|
-
}
|
|
744
|
-
if (!Array.isArray(config.Providers)) {
|
|
745
|
-
log.error((0, i18n_1.t)('❌ claude-code-router 配置文件 Providers 字段非数组类型,请手动修复'));
|
|
746
|
-
process.exit(1);
|
|
747
|
-
}
|
|
748
|
-
const providers = config.Providers;
|
|
749
|
-
const index = providers.findIndex((p) => typeof p === 'object' && (p === null || p === void 0 ? void 0 : p.name) === 'cloudbase');
|
|
750
|
-
if (index !== -1) {
|
|
751
|
-
providers[index] = cloudbaseProvider;
|
|
752
|
-
}
|
|
753
|
-
else {
|
|
754
|
-
providers.push(cloudbaseProvider);
|
|
755
|
-
}
|
|
756
|
-
if (!('Router' in config) ||
|
|
757
|
-
typeof config.Router !== 'object' ||
|
|
758
|
-
config.Router === null) {
|
|
759
|
-
config.Router = {};
|
|
760
|
-
}
|
|
761
|
-
config.Router.default = `cloudbase,${model}`;
|
|
762
|
-
config.Log = true;
|
|
763
|
-
yield fs.writeJson(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, config, { spaces: 2 });
|
|
764
|
-
log.debug((0, i18n_1.t)('✅ claude-code-router 配置完成'));
|
|
698
|
+
providers.push(cloudbaseProvider);
|
|
765
699
|
}
|
|
766
|
-
|
|
700
|
+
if (!('Router' in config) ||
|
|
701
|
+
typeof config.Router !== 'object' ||
|
|
702
|
+
config.Router === null) {
|
|
703
|
+
config.Router = {};
|
|
704
|
+
}
|
|
705
|
+
config.Router.default = `cloudbase,${model}`;
|
|
706
|
+
config.Log = true;
|
|
707
|
+
await fs.writeJson(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, config, { spaces: 2 });
|
|
708
|
+
log.debug((0, i18n_1.t)('✅ claude-code-router 配置完成'));
|
|
709
|
+
}
|
|
767
710
|
}
|
|
768
711
|
parseArgs(args) {
|
|
769
712
|
return args.filter((arg) => typeof arg === 'string' && arg.trim().length > 0);
|
|
770
713
|
}
|
|
771
|
-
ensureClaudeCodeRouter(log) {
|
|
772
|
-
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
data.toString().includes('Usage: ccr [command]') && resolve(true);
|
|
780
|
-
});
|
|
781
|
-
child.on('close', (code) => {
|
|
782
|
-
resolve(code === 0);
|
|
783
|
-
});
|
|
784
|
-
child.on('error', () => {
|
|
785
|
-
resolve(false);
|
|
786
|
-
});
|
|
714
|
+
async ensureClaudeCodeRouter(log) {
|
|
715
|
+
const isAvailable = await new Promise((resolve) => {
|
|
716
|
+
const child = (0, child_process_1.spawn)('ccr', ['--version'], {
|
|
717
|
+
stdio: 'pipe',
|
|
718
|
+
shell: true
|
|
719
|
+
});
|
|
720
|
+
child.stdout.on('data', (data) => {
|
|
721
|
+
data.toString().includes('Usage: ccr [command]') && resolve(true);
|
|
787
722
|
});
|
|
788
|
-
|
|
789
|
-
|
|
723
|
+
child.on('close', (code) => {
|
|
724
|
+
resolve(code === 0);
|
|
725
|
+
});
|
|
726
|
+
child.on('error', () => {
|
|
727
|
+
resolve(false);
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
if (isAvailable) {
|
|
731
|
+
log.debug((0, i18n_1.t)('claude code router 已安装!'));
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
const { shouldInstall } = await inquirer_1.default.prompt([
|
|
735
|
+
{
|
|
736
|
+
type: 'confirm',
|
|
737
|
+
name: 'shouldInstall',
|
|
738
|
+
message: (0, i18n_1.t)('AI 开发缺少 claude code router 依赖,是否安装? {{hint}}', {
|
|
739
|
+
hint: (0, const_1.getBooleanHint)(true)
|
|
740
|
+
})
|
|
741
|
+
}
|
|
742
|
+
]);
|
|
743
|
+
if (shouldInstall) {
|
|
744
|
+
await this.executeCommand('npm', ['install', '-g', '@musistudio/claude-code-router'], process.env, log);
|
|
745
|
+
log.info((0, i18n_1.t)('✅ claude-code-router 安装完成'));
|
|
790
746
|
}
|
|
791
747
|
else {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
type: 'confirm',
|
|
795
|
-
name: 'shouldInstall',
|
|
796
|
-
message: (0, i18n_1.t)('AI 开发缺少 claude code router 依赖,是否安装? {{hint}}', {
|
|
797
|
-
hint: (0, const_1.getBooleanHint)(true)
|
|
798
|
-
})
|
|
799
|
-
}
|
|
800
|
-
]);
|
|
801
|
-
if (shouldInstall) {
|
|
802
|
-
yield this.executeCommand('npm', ['install', '-g', '@musistudio/claude-code-router'], process.env, log);
|
|
803
|
-
log.info((0, i18n_1.t)('✅ claude-code-router 安装完成'));
|
|
804
|
-
}
|
|
805
|
-
else {
|
|
806
|
-
log.info((0, i18n_1.t)('❌ claude code router 未安装,请手动安装'));
|
|
807
|
-
process.exit(1);
|
|
808
|
-
}
|
|
748
|
+
log.info((0, i18n_1.t)('❌ claude code router 未安装,请手动安装'));
|
|
749
|
+
process.exit(1);
|
|
809
750
|
}
|
|
810
|
-
}
|
|
751
|
+
}
|
|
811
752
|
}
|
|
812
|
-
ensureClaudeCode(log) {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
753
|
+
async ensureClaudeCode(log) {
|
|
754
|
+
if (await this.isToolAvailable('claude')) {
|
|
755
|
+
log.debug((0, i18n_1.t)('claude code 已安装!'));
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
const { shouldInstall } = await inquirer_1.default.prompt([
|
|
759
|
+
{
|
|
760
|
+
type: 'confirm',
|
|
761
|
+
name: 'shouldInstall',
|
|
762
|
+
message: (0, i18n_1.t)('AI 开发缺少 claude code 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
763
|
+
}
|
|
764
|
+
]);
|
|
765
|
+
if (shouldInstall) {
|
|
766
|
+
const platform = process.platform;
|
|
767
|
+
if (platform === 'win32') {
|
|
768
|
+
await this.executeCommand('npm', ['install', '-g', '@anthropic-ai/claude-code'], process.env, log);
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
try {
|
|
772
|
+
await this.executeCommand('sh', ['-c', 'curl -fsSL https://claude.ai/install.sh | bash'], process.env, log);
|
|
829
773
|
}
|
|
830
|
-
|
|
774
|
+
catch (error) {
|
|
775
|
+
log.warn((0, i18n_1.t)('⚠️ curl 安装失败,尝试使用 npm 安装...'));
|
|
831
776
|
try {
|
|
832
|
-
|
|
777
|
+
await this.executeCommand('npm', ['install', '-g', '@anthropic-ai/claude-code'], process.env, log);
|
|
833
778
|
}
|
|
834
|
-
catch (
|
|
835
|
-
log.
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
log.error((0, i18n_1.t)('请手动安装 claude code:'));
|
|
842
|
-
log.error((0, i18n_1.t)('📖 安装文档: {{link}}', {
|
|
843
|
-
link: (0, output_1.genClickableLink)('https://docs.anthropic.com/en/docs/claude-code/setup')
|
|
844
|
-
}));
|
|
845
|
-
process.exit(1);
|
|
846
|
-
}
|
|
779
|
+
catch (npmError) {
|
|
780
|
+
log.error((0, i18n_1.t)('❌ claude code 安装失败'));
|
|
781
|
+
log.error((0, i18n_1.t)('请手动安装 claude code:'));
|
|
782
|
+
log.error((0, i18n_1.t)('📖 安装文档: {{link}}', {
|
|
783
|
+
link: (0, output_1.genClickableLink)('https://docs.anthropic.com/en/docs/claude-code/setup')
|
|
784
|
+
}));
|
|
785
|
+
process.exit(1);
|
|
847
786
|
}
|
|
848
787
|
}
|
|
849
|
-
log.info((0, i18n_1.t)('✅ claude code 安装完成'));
|
|
850
|
-
}
|
|
851
|
-
else {
|
|
852
|
-
log.info((0, i18n_1.t)('❌ claude code 未安装,请手动安装'));
|
|
853
|
-
log.info((0, i18n_1.t)('📖 安装文档: {{link}}', {
|
|
854
|
-
link: (0, output_1.genClickableLink)('https://docs.anthropic.com/en/docs/claude-code/setup')
|
|
855
|
-
}));
|
|
856
|
-
process.exit(1);
|
|
857
788
|
}
|
|
789
|
+
log.info((0, i18n_1.t)('✅ claude code 安装完成'));
|
|
858
790
|
}
|
|
859
|
-
|
|
791
|
+
else {
|
|
792
|
+
log.info((0, i18n_1.t)('❌ claude code 未安装,请手动安装'));
|
|
793
|
+
log.info((0, i18n_1.t)('📖 安装文档: {{link}}', {
|
|
794
|
+
link: (0, output_1.genClickableLink)('https://docs.anthropic.com/en/docs/claude-code/setup')
|
|
795
|
+
}));
|
|
796
|
+
process.exit(1);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
860
799
|
}
|
|
861
|
-
ensureQwenCode(log) {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
800
|
+
async ensureQwenCode(log) {
|
|
801
|
+
if (await this.isToolAvailable('qwen')) {
|
|
802
|
+
log.debug((0, i18n_1.t)('qwen code 已安装!'));
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
const { shouldInstall } = await inquirer_1.default.prompt([
|
|
806
|
+
{
|
|
807
|
+
type: 'confirm',
|
|
808
|
+
name: 'shouldInstall',
|
|
809
|
+
message: (0, i18n_1.t)('AI 开发缺少 qwen code 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
810
|
+
}
|
|
811
|
+
]);
|
|
812
|
+
if (shouldInstall) {
|
|
813
|
+
await this.executeCommand('npm', ['install', '-g', '@qwen-code/qwen-code'], process.env, log);
|
|
814
|
+
log.info((0, i18n_1.t)('✅ qwen code 安装完成'));
|
|
865
815
|
}
|
|
866
816
|
else {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
type: 'confirm',
|
|
870
|
-
name: 'shouldInstall',
|
|
871
|
-
message: (0, i18n_1.t)('AI 开发缺少 qwen code 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
872
|
-
}
|
|
873
|
-
]);
|
|
874
|
-
if (shouldInstall) {
|
|
875
|
-
yield this.executeCommand('npm', ['install', '-g', '@qwen-code/qwen-code'], process.env, log);
|
|
876
|
-
log.info((0, i18n_1.t)('✅ qwen code 安装完成'));
|
|
877
|
-
}
|
|
878
|
-
else {
|
|
879
|
-
log.info((0, i18n_1.t)('❌ qwen code 未安装,请手动安装'));
|
|
880
|
-
process.exit(1);
|
|
881
|
-
}
|
|
817
|
+
log.info((0, i18n_1.t)('❌ qwen code 未安装,请手动安装'));
|
|
818
|
+
process.exit(1);
|
|
882
819
|
}
|
|
883
|
-
}
|
|
820
|
+
}
|
|
884
821
|
}
|
|
885
|
-
executeCodexAgent({ apiKey, baseUrl, model }, additionalArgs, log) {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
822
|
+
async executeCodexAgent({ apiKey, baseUrl, model }, additionalArgs, log) {
|
|
823
|
+
await this.ensureCodexCode(log);
|
|
824
|
+
const codexArgs = [
|
|
825
|
+
...(model ? ['--config', `model=${model}`] : []),
|
|
826
|
+
'--config',
|
|
827
|
+
'model_providers.custom.name=Custom OpenAI',
|
|
828
|
+
...(baseUrl ? ['--config', `model_providers.custom.base_url=${baseUrl}`] : []),
|
|
829
|
+
'--config',
|
|
830
|
+
'model_providers.custom.env_key=OPENAI_API_KEY',
|
|
831
|
+
'--config',
|
|
832
|
+
'model_provider=custom',
|
|
833
|
+
...additionalArgs
|
|
834
|
+
];
|
|
835
|
+
await this.executeCommand('codex', codexArgs, {
|
|
836
|
+
...process.env,
|
|
837
|
+
OPENAI_API_KEY: apiKey
|
|
838
|
+
}, log);
|
|
901
839
|
}
|
|
902
|
-
executeNoneCodexAgent(additionalArgs, log) {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
yield this.executeCommand('codex', additionalArgs, Object.assign({}, process.env), log);
|
|
906
|
-
});
|
|
840
|
+
async executeNoneCodexAgent(additionalArgs, log) {
|
|
841
|
+
await this.ensureCodexCode(log);
|
|
842
|
+
await this.executeCommand('codex', additionalArgs, { ...process.env }, log);
|
|
907
843
|
}
|
|
908
|
-
executeCodexCloudbaseAgent({ provider, model }, additionalArgs, log) {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
token: credential.token
|
|
920
|
-
});
|
|
921
|
-
if (!accessToken.access_token) {
|
|
922
|
-
log.error((0, i18n_1.t)('获取云开发 Access Token 失败,请运行 tcb login 后再重试,{{token}}', {
|
|
923
|
-
token: JSON.stringify(accessToken)
|
|
924
|
-
}));
|
|
925
|
-
process.exit(1);
|
|
926
|
-
}
|
|
927
|
-
const baseUrl = `${(0, url_1.getGatewayUrl)(envId)}/v1/ai/${provider}`;
|
|
928
|
-
const apiKey = accessToken.access_token;
|
|
929
|
-
const codexArgs = [
|
|
930
|
-
'--config',
|
|
931
|
-
`model=${model}`,
|
|
932
|
-
'--config',
|
|
933
|
-
'model_providers.cloudbase.name=CloudBase AI',
|
|
934
|
-
'--config',
|
|
935
|
-
`model_providers.cloudbase.base_url=${baseUrl}`,
|
|
936
|
-
'--config',
|
|
937
|
-
'model_providers.cloudbase.env_key=CLOUDBASE_ACCESS_TOKEN',
|
|
938
|
-
'--config',
|
|
939
|
-
'model_provider=cloudbase',
|
|
940
|
-
...additionalArgs
|
|
941
|
-
];
|
|
942
|
-
yield this.executeCommand('codex', codexArgs, Object.assign(Object.assign({}, process.env), { CLOUDBASE_ACCESS_TOKEN: apiKey }), log);
|
|
844
|
+
async executeCodexCloudbaseAgent({ provider, model }, additionalArgs, log) {
|
|
845
|
+
await this.ensureCodexCode(log);
|
|
846
|
+
const _envId = await (0, config_1.createConfigParser)().get('envId');
|
|
847
|
+
await (0, auth_1.checkLogin)();
|
|
848
|
+
const credential = await (0, utils_1.getCredential)({}, {});
|
|
849
|
+
const envId = await (0, env_1.ensureValidEnv)(_envId, log);
|
|
850
|
+
const accessToken = await (0, utils_1.rawFetchAccessToken)({
|
|
851
|
+
envId,
|
|
852
|
+
secretId: credential.secretId,
|
|
853
|
+
secretKey: credential.secretKey,
|
|
854
|
+
token: credential.token
|
|
943
855
|
});
|
|
856
|
+
if (!accessToken.access_token) {
|
|
857
|
+
log.error((0, i18n_1.t)('获取云开发 Access Token 失败,请运行 tcb login 后再重试,{{token}}', {
|
|
858
|
+
token: JSON.stringify(accessToken)
|
|
859
|
+
}));
|
|
860
|
+
process.exit(1);
|
|
861
|
+
}
|
|
862
|
+
const baseUrl = `${(0, url_1.getGatewayUrl)(envId)}/v1/ai/${provider}`;
|
|
863
|
+
const apiKey = accessToken.access_token;
|
|
864
|
+
const codexArgs = [
|
|
865
|
+
'--config',
|
|
866
|
+
`model=${model}`,
|
|
867
|
+
'--config',
|
|
868
|
+
'model_providers.cloudbase.name=CloudBase AI',
|
|
869
|
+
'--config',
|
|
870
|
+
`model_providers.cloudbase.base_url=${baseUrl}`,
|
|
871
|
+
'--config',
|
|
872
|
+
'model_providers.cloudbase.env_key=CLOUDBASE_ACCESS_TOKEN',
|
|
873
|
+
'--config',
|
|
874
|
+
'model_provider=cloudbase',
|
|
875
|
+
...additionalArgs
|
|
876
|
+
];
|
|
877
|
+
await this.executeCommand('codex', codexArgs, {
|
|
878
|
+
...process.env,
|
|
879
|
+
CLOUDBASE_ACCESS_TOKEN: apiKey
|
|
880
|
+
}, log);
|
|
944
881
|
}
|
|
945
|
-
executeNoneCursorAgent(additionalArgs, log) {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
yield this.executeCommand('cursor-agent', additionalArgs, Object.assign({}, process.env), log);
|
|
949
|
-
});
|
|
882
|
+
async executeNoneCursorAgent(additionalArgs, log) {
|
|
883
|
+
await this.ensureCursor(log);
|
|
884
|
+
await this.executeCommand('cursor-agent', additionalArgs, { ...process.env }, log);
|
|
950
885
|
}
|
|
951
|
-
ensureCursor(log) {
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
886
|
+
async ensureCursor(log) {
|
|
887
|
+
if (await this.isToolAvailable('cursor-agent')) {
|
|
888
|
+
log.debug((0, i18n_1.t)('Cursor CLI 已安装!'));
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
const { shouldInstall } = await inquirer_1.default.prompt([
|
|
892
|
+
{
|
|
893
|
+
type: 'confirm',
|
|
894
|
+
name: 'shouldInstall',
|
|
895
|
+
message: (0, i18n_1.t)('AI 开发缺少 Cursor CLI 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
896
|
+
}
|
|
897
|
+
]);
|
|
898
|
+
if (shouldInstall) {
|
|
899
|
+
log.info((0, i18n_1.t)('正在安装 Cursor CLI...'));
|
|
900
|
+
await this.executeCommand('sh', ['-c', 'curl https://cursor.com/install -fsS | bash'], process.env, log);
|
|
901
|
+
log.info((0, i18n_1.t)('✅ Cursor CLI 安装完成'));
|
|
955
902
|
}
|
|
956
903
|
else {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
type: 'confirm',
|
|
960
|
-
name: 'shouldInstall',
|
|
961
|
-
message: (0, i18n_1.t)('AI 开发缺少 Cursor CLI 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
962
|
-
}
|
|
963
|
-
]);
|
|
964
|
-
if (shouldInstall) {
|
|
965
|
-
log.info((0, i18n_1.t)('正在安装 Cursor CLI...'));
|
|
966
|
-
yield this.executeCommand('sh', ['-c', 'curl https://cursor.com/install -fsS | bash'], process.env, log);
|
|
967
|
-
log.info((0, i18n_1.t)('✅ Cursor CLI 安装完成'));
|
|
968
|
-
}
|
|
969
|
-
else {
|
|
970
|
-
log.info((0, i18n_1.t)('❌ Cursor CLI 未安装,请手动安装'));
|
|
971
|
-
process.exit(1);
|
|
972
|
-
}
|
|
904
|
+
log.info((0, i18n_1.t)('❌ Cursor CLI 未安装,请手动安装'));
|
|
905
|
+
process.exit(1);
|
|
973
906
|
}
|
|
974
|
-
}
|
|
907
|
+
}
|
|
975
908
|
}
|
|
976
|
-
ensureCodexCode(log) {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
909
|
+
async ensureCodexCode(log) {
|
|
910
|
+
if (await this.isToolAvailable('codex')) {
|
|
911
|
+
log.debug((0, i18n_1.t)('codex 已安装!'));
|
|
912
|
+
}
|
|
913
|
+
else {
|
|
914
|
+
const { shouldInstall } = await inquirer_1.default.prompt([
|
|
915
|
+
{
|
|
916
|
+
type: 'confirm',
|
|
917
|
+
name: 'shouldInstall',
|
|
918
|
+
message: (0, i18n_1.t)('AI 开发缺少 codex 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
919
|
+
}
|
|
920
|
+
]);
|
|
921
|
+
if (shouldInstall) {
|
|
922
|
+
await this.executeCommand('npm', ['install', '-g', '@openai/codex'], process.env, log);
|
|
923
|
+
log.info((0, i18n_1.t)('✅ codex 安装完成'));
|
|
980
924
|
}
|
|
981
925
|
else {
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
type: 'confirm',
|
|
985
|
-
name: 'shouldInstall',
|
|
986
|
-
message: (0, i18n_1.t)('AI 开发缺少 codex 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
987
|
-
}
|
|
988
|
-
]);
|
|
989
|
-
if (shouldInstall) {
|
|
990
|
-
yield this.executeCommand('npm', ['install', '-g', '@openai/codex'], process.env, log);
|
|
991
|
-
log.info((0, i18n_1.t)('✅ codex 安装完成'));
|
|
992
|
-
}
|
|
993
|
-
else {
|
|
994
|
-
log.info((0, i18n_1.t)('❌ codex 未安装,请手动安装'));
|
|
995
|
-
process.exit(1);
|
|
996
|
-
}
|
|
926
|
+
log.info((0, i18n_1.t)('❌ codex 未安装,请手动安装'));
|
|
927
|
+
process.exit(1);
|
|
997
928
|
}
|
|
998
|
-
}
|
|
929
|
+
}
|
|
999
930
|
}
|
|
1000
|
-
executeAiderAgent(config, additionalArgs, log) {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
931
|
+
async executeAiderAgent(config, additionalArgs, log) {
|
|
932
|
+
await this.ensureAider(log);
|
|
933
|
+
const { apiKey, baseUrl, model } = config;
|
|
934
|
+
const aiderArgs = ['--model', `openai/${model}`, ...additionalArgs];
|
|
935
|
+
const env = {
|
|
936
|
+
...process.env,
|
|
937
|
+
OPENAI_API_KEY: apiKey,
|
|
938
|
+
OPENAI_API_BASE: baseUrl
|
|
939
|
+
};
|
|
940
|
+
await this.executeCommand('aider', aiderArgs, env, log);
|
|
1008
941
|
}
|
|
1009
|
-
executeAiderCloudbaseAgent(config, additionalArgs, log) {
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
token: credential.token
|
|
1022
|
-
});
|
|
1023
|
-
if (!accessToken.access_token) {
|
|
1024
|
-
log.error((0, i18n_1.t)('获取云开发 Access Token 失败,请运行 tcb login 后再重试,{{token}}', {
|
|
1025
|
-
token: JSON.stringify(accessToken)
|
|
1026
|
-
}));
|
|
1027
|
-
process.exit(1);
|
|
1028
|
-
}
|
|
1029
|
-
const baseUrl = `${(0, url_1.getGatewayUrl)(envId)}/v1/ai/${provider}`;
|
|
1030
|
-
const apiKey = accessToken.access_token;
|
|
1031
|
-
const aiderArgs = ['--model', `openai/${model}`, ...additionalArgs];
|
|
1032
|
-
const env = Object.assign(Object.assign({}, process.env), { OPENAI_API_KEY: apiKey, OPENAI_API_BASE: baseUrl });
|
|
1033
|
-
yield this.executeCommand('aider', aiderArgs, env, log);
|
|
942
|
+
async executeAiderCloudbaseAgent(config, additionalArgs, log) {
|
|
943
|
+
await this.ensureAider(log);
|
|
944
|
+
const { provider, model } = config;
|
|
945
|
+
const _envId = await (0, config_1.createConfigParser)().get('envId');
|
|
946
|
+
await (0, auth_1.checkLogin)();
|
|
947
|
+
const credential = await (0, utils_1.getCredential)({}, {});
|
|
948
|
+
const envId = await (0, env_1.ensureValidEnv)(_envId, log);
|
|
949
|
+
const accessToken = await (0, utils_1.rawFetchAccessToken)({
|
|
950
|
+
envId,
|
|
951
|
+
secretId: credential.secretId,
|
|
952
|
+
secretKey: credential.secretKey,
|
|
953
|
+
token: credential.token
|
|
1034
954
|
});
|
|
955
|
+
if (!accessToken.access_token) {
|
|
956
|
+
log.error((0, i18n_1.t)('获取云开发 Access Token 失败,请运行 tcb login 后再重试,{{token}}', {
|
|
957
|
+
token: JSON.stringify(accessToken)
|
|
958
|
+
}));
|
|
959
|
+
process.exit(1);
|
|
960
|
+
}
|
|
961
|
+
const baseUrl = `${(0, url_1.getGatewayUrl)(envId)}/v1/ai/${provider}`;
|
|
962
|
+
const apiKey = accessToken.access_token;
|
|
963
|
+
const aiderArgs = ['--model', `openai/${model}`, ...additionalArgs];
|
|
964
|
+
const env = {
|
|
965
|
+
...process.env,
|
|
966
|
+
OPENAI_API_KEY: apiKey,
|
|
967
|
+
OPENAI_API_BASE: baseUrl
|
|
968
|
+
};
|
|
969
|
+
await this.executeCommand('aider', aiderArgs, env, log);
|
|
1035
970
|
}
|
|
1036
|
-
ensureAider(log) {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
message: (0, i18n_1.t)('AI 开发缺少 aider 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
1047
|
-
}
|
|
1048
|
-
]);
|
|
1049
|
-
if (shouldInstall) {
|
|
1050
|
-
log.info((0, i18n_1.t)('正在安装 aider...'));
|
|
1051
|
-
const platform = process.platform;
|
|
1052
|
-
if (platform === 'win32') {
|
|
1053
|
-
yield this.executeCommand('powershell', ['-c', 'irm https://aider.chat/install.ps1 | iex'], process.env, log);
|
|
1054
|
-
}
|
|
1055
|
-
else {
|
|
1056
|
-
yield this.executeCommand('sh', ['-c', 'curl -LsSf https://aider.chat/install.sh | sh'], process.env, log);
|
|
1057
|
-
}
|
|
1058
|
-
log.info((0, i18n_1.t)('✅ aider 安装完成'));
|
|
971
|
+
async ensureAider(log) {
|
|
972
|
+
if (await this.isToolAvailable('aider')) {
|
|
973
|
+
log.debug((0, i18n_1.t)('aider 已安装!'));
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
const { shouldInstall } = await inquirer_1.default.prompt([
|
|
977
|
+
{
|
|
978
|
+
type: 'confirm',
|
|
979
|
+
name: 'shouldInstall',
|
|
980
|
+
message: (0, i18n_1.t)('AI 开发缺少 aider 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
1059
981
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
982
|
+
]);
|
|
983
|
+
if (shouldInstall) {
|
|
984
|
+
log.info((0, i18n_1.t)('正在安装 aider...'));
|
|
985
|
+
const platform = process.platform;
|
|
986
|
+
if (platform === 'win32') {
|
|
987
|
+
await this.executeCommand('powershell', ['-c', 'irm https://aider.chat/install.ps1 | iex'], process.env, log);
|
|
1063
988
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
}
|
|
1067
|
-
modifyMCPConfigs(extractDir, log) {
|
|
1068
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1069
|
-
const fs = yield Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
1070
|
-
const path = yield Promise.resolve().then(() => __importStar(require('path')));
|
|
1071
|
-
try {
|
|
1072
|
-
log.info((0, i18n_1.t)('🔧 正在修改 MCP 配置文件...'));
|
|
1073
|
-
for (const [, files] of Object.entries(IDE_FILE_MAPPINGS)) {
|
|
1074
|
-
for (const descriptor of files) {
|
|
1075
|
-
if (!descriptor.isMcpConfig)
|
|
1076
|
-
continue;
|
|
1077
|
-
const filePath = path.join(extractDir, descriptor.path);
|
|
1078
|
-
if (yield fs.pathExists(filePath)) {
|
|
1079
|
-
const format = inferConfigFormat(descriptor.path);
|
|
1080
|
-
if (format === 'json') {
|
|
1081
|
-
yield this.modifyMCPJsonFile(filePath, log);
|
|
1082
|
-
}
|
|
1083
|
-
else if (format === 'toml') {
|
|
1084
|
-
yield this.modifyMCPTomlFile(filePath, log);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
989
|
+
else {
|
|
990
|
+
await this.executeCommand('sh', ['-c', 'curl -LsSf https://aider.chat/install.sh | sh'], process.env, log);
|
|
1088
991
|
}
|
|
1089
|
-
log.info((0, i18n_1.t)('✅
|
|
992
|
+
log.info((0, i18n_1.t)('✅ aider 安装完成'));
|
|
1090
993
|
}
|
|
1091
|
-
|
|
1092
|
-
log.
|
|
994
|
+
else {
|
|
995
|
+
log.info((0, i18n_1.t)('❌ aider 未安装,请手动安装'));
|
|
996
|
+
process.exit(1);
|
|
1093
997
|
}
|
|
1094
|
-
}
|
|
998
|
+
}
|
|
1095
999
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if (argsStr.includes('npm-global-exec@latest') &&
|
|
1114
|
-
argsStr.includes('@cloudbase/cloudbase-mcp@latest')) {
|
|
1115
|
-
result.command = 'cloudbase-mcp';
|
|
1116
|
-
result.args = [];
|
|
1117
|
-
result.env = {
|
|
1118
|
-
INTEGRATION_IDE: process.env.INTEGRATION_IDE || 'CloudBaseCLI'
|
|
1119
|
-
};
|
|
1120
|
-
modified = true;
|
|
1121
|
-
log.debug((0, i18n_1.t)('修改配置文件 {{filePath}}: npx -> cloudbase-mcp', { filePath }));
|
|
1000
|
+
async modifyMCPConfigs(extractDir, log) {
|
|
1001
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
1002
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
1003
|
+
try {
|
|
1004
|
+
log.info((0, i18n_1.t)('🔧 正在修改 MCP 配置文件...'));
|
|
1005
|
+
for (const [, files] of Object.entries(IDE_FILE_MAPPINGS)) {
|
|
1006
|
+
for (const descriptor of files) {
|
|
1007
|
+
if (!descriptor.isMcpConfig)
|
|
1008
|
+
continue;
|
|
1009
|
+
const filePath = path.join(extractDir, descriptor.path);
|
|
1010
|
+
if (await fs.pathExists(filePath)) {
|
|
1011
|
+
const format = inferConfigFormat(descriptor.path);
|
|
1012
|
+
if (format === 'json') {
|
|
1013
|
+
await this.modifyMCPJsonFile(filePath, log);
|
|
1014
|
+
}
|
|
1015
|
+
else if (format === 'toml') {
|
|
1016
|
+
await this.modifyMCPTomlFile(filePath, log);
|
|
1122
1017
|
}
|
|
1123
1018
|
}
|
|
1124
|
-
for (const [key, value] of Object.entries(result)) {
|
|
1125
|
-
result[key] = modifyCommands(value);
|
|
1126
|
-
}
|
|
1127
|
-
return result;
|
|
1128
|
-
};
|
|
1129
|
-
const modifiedConfig = modifyCommands(config);
|
|
1130
|
-
if (modified) {
|
|
1131
|
-
yield fs.writeJson(filePath, modifiedConfig, { spaces: 2 });
|
|
1132
|
-
log.debug((0, i18n_1.t)('✅ 已修改 {{filePath}}', { filePath }));
|
|
1133
1019
|
}
|
|
1134
1020
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1021
|
+
log.info((0, i18n_1.t)('✅ MCP 配置文件修改完成'));
|
|
1022
|
+
}
|
|
1023
|
+
catch (error) {
|
|
1024
|
+
log.warn((0, i18n_1.t)('⚠️ MCP 配置文件修改失败: {{error}}', { error: error.message }));
|
|
1025
|
+
}
|
|
1139
1026
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
const
|
|
1156
|
-
if (
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
log.debug((0, i18n_1.t)('修改配置文件 {{filePath}}: npx -> cloudbase-mcp', { filePath }));
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
for (const [key, value] of Object.entries(result)) {
|
|
1169
|
-
result[key] = modifyCommands(value);
|
|
1027
|
+
async modifyMCPJsonFile(filePath, log) {
|
|
1028
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
1029
|
+
try {
|
|
1030
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
1031
|
+
const config = JSON.parse(content);
|
|
1032
|
+
let modified = false;
|
|
1033
|
+
const modifyCommands = (obj) => {
|
|
1034
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
1035
|
+
return obj;
|
|
1036
|
+
}
|
|
1037
|
+
if (Array.isArray(obj)) {
|
|
1038
|
+
return obj.map((item) => modifyCommands(item));
|
|
1039
|
+
}
|
|
1040
|
+
const result = { ...obj };
|
|
1041
|
+
if (result.command === 'npx' && Array.isArray(result.args)) {
|
|
1042
|
+
const argsStr = result.args.join(' ');
|
|
1043
|
+
if (argsStr.includes('npm-global-exec@latest') &&
|
|
1044
|
+
argsStr.includes('@cloudbase/cloudbase-mcp@latest')) {
|
|
1045
|
+
result.command = 'cloudbase-mcp';
|
|
1046
|
+
result.args = [];
|
|
1047
|
+
result.env = {
|
|
1048
|
+
INTEGRATION_IDE: process.env.INTEGRATION_IDE || 'CloudBaseCLI'
|
|
1049
|
+
};
|
|
1050
|
+
modified = true;
|
|
1051
|
+
log.debug((0, i18n_1.t)('修改配置文件 {{filePath}}: npx -> cloudbase-mcp', { filePath }));
|
|
1170
1052
|
}
|
|
1171
|
-
return result;
|
|
1172
|
-
};
|
|
1173
|
-
const modifiedConfig = modifyCommands(config);
|
|
1174
|
-
if (modified) {
|
|
1175
|
-
const tomlString = this.objectToToml(modifiedConfig);
|
|
1176
|
-
yield fs.writeFile(filePath, tomlString, 'utf-8');
|
|
1177
|
-
log.debug((0, i18n_1.t)('✅ 已修改 {{filePath}}', { filePath }));
|
|
1178
1053
|
}
|
|
1054
|
+
for (const [key, value] of Object.entries(result)) {
|
|
1055
|
+
result[key] = modifyCommands(value);
|
|
1056
|
+
}
|
|
1057
|
+
return result;
|
|
1058
|
+
};
|
|
1059
|
+
const modifiedConfig = modifyCommands(config);
|
|
1060
|
+
if (modified) {
|
|
1061
|
+
await fs.writeJson(filePath, modifiedConfig, { spaces: 2 });
|
|
1062
|
+
log.debug((0, i18n_1.t)('✅ 已修改 {{filePath}}', { filePath }));
|
|
1179
1063
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1064
|
+
}
|
|
1065
|
+
catch (error) {
|
|
1066
|
+
log.warn((0, i18n_1.t)('⚠️ 修改配置文件 {{filePath}} 失败: {{error}}', { filePath, error: error.message }));
|
|
1067
|
+
}
|
|
1184
1068
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
const
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
|
|
1069
|
+
async modifyMCPTomlFile(filePath, log) {
|
|
1070
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
1071
|
+
const toml = await Promise.resolve().then(() => __importStar(require('toml')));
|
|
1072
|
+
try {
|
|
1073
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
1074
|
+
const config = toml.parse(content);
|
|
1075
|
+
let modified = false;
|
|
1076
|
+
const modifyCommands = (obj) => {
|
|
1077
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
1078
|
+
return obj;
|
|
1079
|
+
}
|
|
1080
|
+
if (Array.isArray(obj)) {
|
|
1081
|
+
return obj.map((item) => modifyCommands(item));
|
|
1193
1082
|
}
|
|
1194
|
-
const result =
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
result
|
|
1083
|
+
const result = { ...obj };
|
|
1084
|
+
if (result.command === 'npx' && Array.isArray(result.args)) {
|
|
1085
|
+
const argsStr = result.args.join(' ');
|
|
1086
|
+
if (argsStr.includes('@cloudbase/cloudbase-mcp@latest')) {
|
|
1087
|
+
result.command = 'cloudbase-mcp';
|
|
1088
|
+
result.args = [];
|
|
1089
|
+
result.env = {
|
|
1090
|
+
INTEGRATION_IDE: process.env.INTEGRATION_IDE || 'CloudBaseCLI'
|
|
1091
|
+
};
|
|
1092
|
+
modified = true;
|
|
1093
|
+
log.debug((0, i18n_1.t)('修改配置文件 {{filePath}}: npx -> cloudbase-mcp', { filePath }));
|
|
1200
1094
|
}
|
|
1201
1095
|
}
|
|
1096
|
+
for (const [key, value] of Object.entries(result)) {
|
|
1097
|
+
result[key] = modifyCommands(value);
|
|
1098
|
+
}
|
|
1202
1099
|
return result;
|
|
1203
1100
|
};
|
|
1204
|
-
|
|
1205
|
-
if (
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1101
|
+
const modifiedConfig = modifyCommands(config);
|
|
1102
|
+
if (modified) {
|
|
1103
|
+
const tomlString = this.objectToToml(modifiedConfig);
|
|
1104
|
+
await fs.writeFile(filePath, tomlString, 'utf-8');
|
|
1105
|
+
log.debug((0, i18n_1.t)('✅ 已修改 {{filePath}}', { filePath }));
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
catch (error) {
|
|
1109
|
+
log.warn((0, i18n_1.t)('⚠️ 修改配置文件 {{filePath}} 失败: {{error}}', { filePath, error: error.message }));
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
async mergeMcpConfig(entry, destPath, format, log) {
|
|
1113
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
1114
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
1115
|
+
const incomingStr = (await entry.buffer()).toString('utf8');
|
|
1116
|
+
const deepMerge = (target, source) => {
|
|
1117
|
+
if (typeof target !== 'object' || typeof source !== 'object' || !target || !source) {
|
|
1118
|
+
return target;
|
|
1119
|
+
}
|
|
1120
|
+
const result = Array.isArray(target) ? [...target] : { ...source, ...target };
|
|
1121
|
+
for (const key of Object.keys(source)) {
|
|
1122
|
+
if (key in target &&
|
|
1123
|
+
typeof target[key] === 'object' &&
|
|
1124
|
+
typeof source[key] === 'object') {
|
|
1125
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
1214
1126
|
}
|
|
1215
|
-
return;
|
|
1216
1127
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1128
|
+
return result;
|
|
1129
|
+
};
|
|
1130
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
1131
|
+
if (format === 'json') {
|
|
1132
|
+
const incomingObj = JSON.parse(incomingStr);
|
|
1133
|
+
if (await fs.pathExists(destPath)) {
|
|
1134
|
+
const existingObj = await fs.readJson(destPath);
|
|
1222
1135
|
const merged = deepMerge(existingObj, incomingObj);
|
|
1223
|
-
|
|
1224
|
-
yield fs.writeFile(destPath, out, 'utf-8');
|
|
1136
|
+
await fs.writeJson(destPath, merged, { spaces: 2 });
|
|
1225
1137
|
}
|
|
1226
1138
|
else {
|
|
1227
|
-
|
|
1228
|
-
yield fs.writeFile(destPath, out, 'utf-8');
|
|
1139
|
+
await fs.writeJson(destPath, incomingObj, { spaces: 2 });
|
|
1229
1140
|
}
|
|
1230
|
-
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
const toml = await Promise.resolve().then(() => __importStar(require('toml')));
|
|
1144
|
+
const incomingObj = toml.parse(incomingStr);
|
|
1145
|
+
if (await fs.pathExists(destPath)) {
|
|
1146
|
+
const existingStr = await fs.readFile(destPath, 'utf-8');
|
|
1147
|
+
const existingObj = toml.parse(existingStr);
|
|
1148
|
+
const merged = deepMerge(existingObj, incomingObj);
|
|
1149
|
+
const out = this.objectToToml(merged);
|
|
1150
|
+
await fs.writeFile(destPath, out, 'utf-8');
|
|
1151
|
+
}
|
|
1152
|
+
else {
|
|
1153
|
+
const out = this.objectToToml(incomingObj);
|
|
1154
|
+
await fs.writeFile(destPath, out, 'utf-8');
|
|
1155
|
+
}
|
|
1231
1156
|
}
|
|
1232
1157
|
objectToToml(obj, prefix = '') {
|
|
1233
1158
|
const lines = [];
|
|
@@ -1257,42 +1182,39 @@ class AICommandRouter {
|
|
|
1257
1182
|
}
|
|
1258
1183
|
return lines.join('\n');
|
|
1259
1184
|
}
|
|
1260
|
-
executeCodebuddyAgent({ apiKey }, additionalArgs, log) {
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1185
|
+
async executeCodebuddyAgent({ apiKey }, additionalArgs, log) {
|
|
1186
|
+
await this.ensureCodebuddy(log);
|
|
1187
|
+
await this.executeCommand('codebuddy', additionalArgs, {
|
|
1188
|
+
...process.env,
|
|
1189
|
+
...(apiKey && { CODEBUDDY_API_KEY: apiKey })
|
|
1190
|
+
}, log);
|
|
1265
1191
|
}
|
|
1266
|
-
executeNoneCodebuddyAgent(additionalArgs, log) {
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
yield this.executeCommand('codebuddy', additionalArgs, Object.assign({}, process.env), log);
|
|
1270
|
-
});
|
|
1192
|
+
async executeNoneCodebuddyAgent(additionalArgs, log) {
|
|
1193
|
+
await this.ensureCodebuddy(log);
|
|
1194
|
+
await this.executeCommand('codebuddy', additionalArgs, { ...process.env }, log);
|
|
1271
1195
|
}
|
|
1272
|
-
ensureCodebuddy(log) {
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1196
|
+
async ensureCodebuddy(log) {
|
|
1197
|
+
if (await this.isToolAvailable('codebuddy')) {
|
|
1198
|
+
log.debug((0, i18n_1.t)('codebuddy 已安装!'));
|
|
1199
|
+
}
|
|
1200
|
+
else {
|
|
1201
|
+
const { shouldInstall } = await inquirer_1.default.prompt([
|
|
1202
|
+
{
|
|
1203
|
+
type: 'confirm',
|
|
1204
|
+
name: 'shouldInstall',
|
|
1205
|
+
message: (0, i18n_1.t)('AI 开发缺少 codebuddy 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
1206
|
+
}
|
|
1207
|
+
]);
|
|
1208
|
+
if (shouldInstall) {
|
|
1209
|
+
await this.executeCommand('npm', ['install', '-g', '@tencent-ai/codebuddy-code'], process.env, log);
|
|
1210
|
+
log.info((0, i18n_1.t)('✅ codebuddy 安装完成'));
|
|
1276
1211
|
}
|
|
1277
1212
|
else {
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
name: 'shouldInstall',
|
|
1282
|
-
message: (0, i18n_1.t)('AI 开发缺少 codebuddy 依赖,是否安装? {{hint}}', { hint: (0, const_1.getBooleanHint)(true) })
|
|
1283
|
-
}
|
|
1284
|
-
]);
|
|
1285
|
-
if (shouldInstall) {
|
|
1286
|
-
yield this.executeCommand('npm', ['install', '-g', '@tencent-ai/codebuddy-code'], process.env, log);
|
|
1287
|
-
log.info((0, i18n_1.t)('✅ codebuddy 安装完成'));
|
|
1288
|
-
}
|
|
1289
|
-
else {
|
|
1290
|
-
log.info((0, i18n_1.t)('❌ codebuddy 未安装,请手动安装'));
|
|
1291
|
-
log.info((0, i18n_1.t)('📦 安装命令: npm install -g @tencent-ai/codebuddy-code'));
|
|
1292
|
-
process.exit(1);
|
|
1293
|
-
}
|
|
1213
|
+
log.info((0, i18n_1.t)('❌ codebuddy 未安装,请手动安装'));
|
|
1214
|
+
log.info((0, i18n_1.t)('📦 安装命令: npm install -g @tencent-ai/codebuddy-code'));
|
|
1215
|
+
process.exit(1);
|
|
1294
1216
|
}
|
|
1295
|
-
}
|
|
1217
|
+
}
|
|
1296
1218
|
}
|
|
1297
1219
|
}
|
|
1298
1220
|
exports.AICommandRouter = AICommandRouter;
|