@cloudbase/cli 2.8.0-beta.3 → 2.8.0-beta.4

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.
Files changed (41) hide show
  1. package/bin/tcb.js +2 -0
  2. package/lib/commands/cloudrun/base.js +2 -2
  3. package/lib/commands/index.js +0 -1
  4. package/lib/commands/utils.js +4 -10
  5. package/package.json +2 -8
  6. package/types/commands/index.d.ts +0 -1
  7. package/types/commands/utils.d.ts +0 -6
  8. package/types/utils/config.d.ts +0 -1
  9. package/.augment-guidelines +0 -119
  10. package/.claude/settings.local.json +0 -6
  11. package/.clinerules/cloudbase-rules.mdc +0 -119
  12. package/.cursor/rules/cloudbase-rules.mdc +0 -119
  13. package/.mcp.json +0 -11
  14. package/CLAUDE.md +0 -119
  15. package/lib/commands/ai/index.js +0 -172
  16. package/lib/utils/ai/banner.js +0 -88
  17. package/lib/utils/ai/config.js +0 -228
  18. package/lib/utils/ai/const.js +0 -107
  19. package/lib/utils/ai/ensureFiles.js +0 -26
  20. package/lib/utils/ai/envLocalManager.js +0 -144
  21. package/lib/utils/ai/router.js +0 -812
  22. package/lib/utils/ai/setup.js +0 -419
  23. package/rules/cloudbase-platform.mdc +0 -44
  24. package/rules/database.mdc +0 -25
  25. package/rules/miniprogram-development.mdc +0 -61
  26. package/rules/ui-design.mdc +0 -24
  27. package/rules/web-development.mdc +0 -44
  28. package/rules/workflows.mdc +0 -30
  29. package/specs/ai-cli-bootstrap/QWEN.md +0 -196
  30. package/specs/ai-cli-bootstrap/design.md +0 -185
  31. package/specs/ai-cli-bootstrap/requirements.md +0 -51
  32. package/specs/ai-cli-bootstrap/tasks.md +0 -70
  33. package/specs/ai-cli-bootstrap/technical-docs.md +0 -421
  34. package/types/commands/ai/index.d.ts +0 -23
  35. package/types/utils/ai/banner.d.ts +0 -2
  36. package/types/utils/ai/config.d.ts +0 -68
  37. package/types/utils/ai/const.d.ts +0 -238
  38. package/types/utils/ai/ensureFiles.d.ts +0 -1
  39. package/types/utils/ai/envLocalManager.d.ts +0 -23
  40. package/types/utils/ai/router.d.ts +0 -38
  41. package/types/utils/ai/setup.d.ts +0 -21
@@ -1,812 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
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
- var __importDefault = (this && this.__importDefault) || function (mod) {
42
- return (mod && mod.__esModule) ? mod : { "default": mod };
43
- };
44
- Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.AICommandRouter = void 0;
46
- const child_process_1 = require("child_process");
47
- const config_1 = require("./config");
48
- const error_1 = require("../../error");
49
- const inquirer_1 = __importDefault(require("inquirer"));
50
- const chalk_1 = __importDefault(require("chalk"));
51
- const const_1 = require("./const");
52
- const utils_1 = require("../../commands/utils");
53
- const auth_1 = require("../../auth");
54
- class AICommandRouter {
55
- constructor() {
56
- this.configManager = new config_1.AIConfigManager();
57
- }
58
- execute({ agent, addtionalArgs, log }) {
59
- var _a;
60
- return __awaiter(this, void 0, void 0, function* () {
61
- if ((0, config_1.isValidAgent)(agent) !== true) {
62
- log.error(`❌ 无效的 agent: ${agent}`);
63
- return;
64
- }
65
- const config = yield this.configManager.loadConfig();
66
- if (!config) {
67
- log.warn("⚠️ 未检测到 AI ToolKit CLI 配置,请运行 'tcb ai --setup' 进行配置");
68
- return;
69
- }
70
- const agentUpperCased = agent.toUpperCase();
71
- const agentConfig = (_a = config.agents) === null || _a === void 0 ? void 0 : _a[agent];
72
- if (!agentConfig) {
73
- log.warn(`⚠️ 未找到 ${agentUpperCased} 配置,请运行 tcb ai --setup 进行配置`);
74
- return;
75
- }
76
- log.info(`🚀 启动 ${chalk_1.default.bold(agentUpperCased)} AI CLI 工具`);
77
- try {
78
- yield this.checkToolkitConfig(agent, log);
79
- }
80
- catch (e) {
81
- log.warn('⚠️ 云开发功能检查失败,但 AI 工具仍可正常使用');
82
- }
83
- const isValid = yield this.validateAgentConfig(agent, agentConfig, log);
84
- if (!isValid) {
85
- log.warn(`⚠️ ${agent.toUpperCase()} 配置无效,请重新配置`);
86
- return;
87
- }
88
- this.executeAgentWithConfig(agent, agentConfig, this.parseArgs(addtionalArgs), log);
89
- });
90
- }
91
- checkToolkitConfig(agent, log) {
92
- return __awaiter(this, void 0, void 0, function* () {
93
- const { missingFiles } = yield this.configManager.checkToolkitConfig(agent);
94
- if (missingFiles.length > 0) {
95
- log.warn(`⚠️ 缺少 AI ToolKit CLI 配置文件: ${missingFiles.join(', ')}`);
96
- const shouldDownload = yield this.promptForTemplateDownload(log);
97
- if (shouldDownload) {
98
- log.log('');
99
- yield this.downloadTemplate(log);
100
- log.log('');
101
- }
102
- else {
103
- log.info("💡 请运行 'tcb ai --setup' 或访问官方文档进行配置");
104
- const docUrls = {
105
- claude: 'https://docs.cloudbase.net/ai/cloudbase-ai-toolkit/ide-setup/claude-code',
106
- codex: 'https://docs.cloudbase.net/ai/cloudbase-ai-toolkit/ide-setup/codex',
107
- gemini: 'https://docs.cloudbase.net/ai/cloudbase-ai-toolkit/ide-setup/gemini'
108
- };
109
- if (docUrls[agent]) {
110
- log.info(`📖 文档: ${docUrls[agent]}`);
111
- }
112
- }
113
- }
114
- });
115
- }
116
- promptForTemplateDownload(log) {
117
- return __awaiter(this, void 0, void 0, function* () {
118
- try {
119
- const { downloadTemplate } = yield inquirer_1.default.prompt([
120
- {
121
- type: 'confirm',
122
- name: 'downloadTemplate',
123
- message: '是否下载 CloudBase AI ToolKit 模板?',
124
- default: true
125
- }
126
- ]);
127
- return downloadTemplate;
128
- }
129
- catch (error) {
130
- log.debug('交互提示失败,跳过模板下载');
131
- return false;
132
- }
133
- });
134
- }
135
- downloadTemplate(log) {
136
- return __awaiter(this, void 0, void 0, function* () {
137
- try {
138
- const { templateType } = yield inquirer_1.default.prompt([
139
- {
140
- type: 'list',
141
- name: 'templateType',
142
- message: '选择要下载的模板类型:',
143
- choices: [
144
- { name: '🧩 AI编辑器配置模板(包含所有主流编辑器配置)', value: 'rules' },
145
- { name: '⚛️ React + CloudBase 全栈应用模板', value: 'react' },
146
- { name: '🟢 Vue + CloudBase 全栈应用模板', value: 'vue' },
147
- { name: '🟦 微信小程序 + 云开发模板', value: 'miniprogram' },
148
- { name: '🌈 UniApp + CloudBase 跨端应用模板', value: 'uniapp' }
149
- ],
150
- default: 'rules'
151
- }
152
- ]);
153
- const shouldCheckOverwrite = yield this.shouldCheckOverwrite(templateType);
154
- let overwrite = false;
155
- if (shouldCheckOverwrite) {
156
- const { confirmOverwrite } = yield inquirer_1.default.prompt([
157
- {
158
- type: 'confirm',
159
- name: 'confirmOverwrite',
160
- message: '检测到已存在文件,是否覆盖?',
161
- default: false
162
- }
163
- ]);
164
- overwrite = confirmOverwrite;
165
- }
166
- log.info(`📦 正在下载并解压 ${templateType} 模板...`);
167
- yield this.downloadAndExtractTemplate(templateType, overwrite, log);
168
- log.info(`✅ ${templateType} 模板配置完成`);
169
- }
170
- catch (error) {
171
- log.error(`❌ 配置失败: ${error.message}`);
172
- throw new error_1.CloudBaseError('模板下载失败', { original: error });
173
- }
174
- });
175
- }
176
- downloadAndExtractTemplate(templateType, overwrite, log) {
177
- var _a, e_1, _b, _c;
178
- return __awaiter(this, void 0, void 0, function* () {
179
- const fs = yield Promise.resolve().then(() => __importStar(require('fs-extra')));
180
- const path = yield Promise.resolve().then(() => __importStar(require('path')));
181
- const os = yield Promise.resolve().then(() => __importStar(require('os')));
182
- const unzipper = yield Promise.resolve().then(() => __importStar(require('unzipper')));
183
- const https = yield Promise.resolve().then(() => __importStar(require('https')));
184
- let ConfigParser;
185
- try {
186
- ConfigParser = (yield Promise.resolve().then(() => __importStar(require('@cloudbase/toolbox')))).ConfigParser;
187
- }
188
- catch (_d) {
189
- ConfigParser = undefined;
190
- }
191
- const templateMap = {
192
- rules: {
193
- url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-project.zip'
194
- },
195
- react: {
196
- url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-react-template.zip'
197
- },
198
- vue: {
199
- url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-vue-template.zip'
200
- },
201
- miniprogram: {
202
- url: 'https://static.cloudbase.net/cloudbase-examples/miniprogram-cloudbase-miniprogram-template.zip'
203
- },
204
- uniapp: {
205
- url: 'https://static.cloudbase.net/cloudbase-examples/universal-cloudbase-uniapp-template.zip'
206
- }
207
- };
208
- const template = templateMap[templateType];
209
- if (!template)
210
- throw new error_1.CloudBaseError('未知模板类型');
211
- const tmpDir = os.tmpdir();
212
- const zipPath = path.join(tmpDir, `cloudbase-template-${templateType}-${Date.now()}.zip`);
213
- yield new Promise((resolve, reject) => {
214
- const file = fs.createWriteStream(zipPath);
215
- https
216
- .get(template.url, (response) => {
217
- if (response.statusCode !== 200) {
218
- reject(new Error(`下载失败: ${response.statusCode}`));
219
- return;
220
- }
221
- response.pipe(file);
222
- file.on('finish', () => {
223
- file.close();
224
- resolve();
225
- });
226
- })
227
- .on('error', (err) => {
228
- fs.unlink(zipPath);
229
- reject(err);
230
- });
231
- });
232
- const extractDir = process.cwd();
233
- const zipStream = fs.createReadStream(zipPath).pipe(unzipper.Parse({ forceStream: true }));
234
- try {
235
- for (var _e = true, _f = __asyncValues(zipStream), _g; _g = yield _f.next(), _a = _g.done, !_a;) {
236
- _c = _g.value;
237
- _e = false;
238
- try {
239
- const entry = _c;
240
- const entryPath = entry.path;
241
- const destPath = path.join(extractDir, entryPath);
242
- if (entry.type === 'Directory') {
243
- yield fs.ensureDir(destPath);
244
- entry.autodrain();
245
- continue;
246
- }
247
- if (entryPath === 'cloudbaserc.json' && (yield fs.pathExists(destPath))) {
248
- try {
249
- const templateContent = yield entry.buffer();
250
- const templateJson = JSON.parse(templateContent.toString('utf8'));
251
- const localJson = yield fs.readJson(destPath);
252
- const deepMerge = (target, source) => {
253
- if (typeof target !== 'object' ||
254
- typeof source !== 'object' ||
255
- !target ||
256
- !source)
257
- return target;
258
- const result = Object.assign(Object.assign({}, source), target);
259
- for (const key of Object.keys(source)) {
260
- if (key in target &&
261
- typeof target[key] === 'object' &&
262
- typeof source[key] === 'object') {
263
- result[key] = deepMerge(target[key], source[key]);
264
- }
265
- }
266
- return result;
267
- };
268
- let merged = deepMerge(localJson, templateJson);
269
- yield fs.writeJson(destPath, merged, { spaces: 2 });
270
- }
271
- catch (e) {
272
- log.warn('cloudbaserc.json 合并失败,已跳过: ' + e.message);
273
- }
274
- continue;
275
- }
276
- if (!overwrite && (yield fs.pathExists(destPath))) {
277
- entry.autodrain();
278
- continue;
279
- }
280
- yield fs.ensureDir(path.dirname(destPath));
281
- yield new Promise((res, rej) => {
282
- entry.pipe(fs.createWriteStream(destPath)).on('finish', res).on('error', rej);
283
- });
284
- }
285
- finally {
286
- _e = true;
287
- }
288
- }
289
- }
290
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
291
- finally {
292
- try {
293
- if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
294
- }
295
- finally { if (e_1) throw e_1.error; }
296
- }
297
- yield fs.unlink(zipPath);
298
- });
299
- }
300
- validateTemplateIntegrity(templateType, extractDir, log) {
301
- return __awaiter(this, void 0, void 0, function* () {
302
- const fs = yield Promise.resolve().then(() => __importStar(require('fs-extra')));
303
- const path = yield Promise.resolve().then(() => __importStar(require('path')));
304
- const requiredFiles = {
305
- rules: ['.mcp.json', 'CLAUDE.md'],
306
- react: ['package.json', 'src/', 'public/', 'cloudbaserc.json'],
307
- vue: ['package.json', 'src/', 'public/', 'cloudbaserc.json'],
308
- miniprogram: ['app.js', 'app.json', 'pages/', 'cloudbaserc.json'],
309
- uniapp: ['pages.json', 'manifest.json', 'pages/', 'cloudbaserc.json']
310
- };
311
- const files = requiredFiles[templateType] || [];
312
- const missingFiles = [];
313
- for (const file of files) {
314
- const filePath = path.join(extractDir, file);
315
- if (!(yield fs.pathExists(filePath))) {
316
- missingFiles.push(file);
317
- }
318
- }
319
- if (missingFiles.length > 0) {
320
- log.warn(`⚠️ 模板解压不完整,缺失文件: ${missingFiles.join(', ')}`);
321
- log.info('💡 建议重新下载模板或检查网络连接');
322
- if (templateType === 'miniprogram' && missingFiles.some((f) => f.includes('pages/'))) {
323
- log.warn('⚠️ 小程序 pages 文件夹缺失,可能影响项目正常运行');
324
- log.info('💡 请重新运行 tcb ai 并选择下载 miniprogram 模板');
325
- }
326
- }
327
- else {
328
- log.info('✅ 模板完整性校验通过,所有必要文件已解压');
329
- }
330
- });
331
- }
332
- getInstallCommand(agent) {
333
- switch (agent) {
334
- case 'claude':
335
- return {
336
- success: true,
337
- command: 'npm',
338
- args: ['install', '-g', '@anthropic-ai/claude-code'],
339
- message: 'npm install -g @anthropic-ai/claude-cli'
340
- };
341
- case 'qwen':
342
- return {
343
- success: true,
344
- command: 'npm',
345
- args: ['install', '-g', '@qwen-code/qwen-code'],
346
- message: 'npm install -g @qwen-code/qwen-code'
347
- };
348
- case 'codex':
349
- return {
350
- success: true,
351
- command: 'npm',
352
- args: ['install', '-g', '@openai/codex-cli'],
353
- message: 'npm install -g @openai/codex-cli'
354
- };
355
- case 'gemini':
356
- return {
357
- success: true,
358
- command: 'npm',
359
- args: ['install', '-g', '@google/gemini-cli'],
360
- message: 'npm install -g @google/gemini-cli'
361
- };
362
- default:
363
- return { success: false, message: `# 请查阅 ${agent} 的官方安装文档` };
364
- }
365
- }
366
- shouldCheckOverwrite(templateType) {
367
- return __awaiter(this, void 0, void 0, function* () {
368
- const fs = yield Promise.resolve().then(() => __importStar(require('fs-extra')));
369
- return (yield fs.pathExists('.mcp.json')) || (yield fs.pathExists('CLAUDE.md'));
370
- });
371
- }
372
- executeCommand(command, args, env, log) {
373
- return __awaiter(this, void 0, void 0, function* () {
374
- return new Promise((resolve, reject) => {
375
- const child = (0, child_process_1.spawn)(command, args, {
376
- stdio: 'inherit',
377
- env,
378
- shell: process.platform === 'win32'
379
- });
380
- child.on('close', (code) => {
381
- if (code === 0) {
382
- resolve();
383
- }
384
- else {
385
- const errorMsg = `命令执行失败: ${command} ${args.join(' ')} (退出码: ${code})`;
386
- log.error(`❌ ${errorMsg}`);
387
- if (code === 127) {
388
- log.info('💡 命令未找到,请检查是否正确安装');
389
- log.info(`📦 安装命令: ${this.getInstallCommand(command).message}`);
390
- }
391
- else if (code === 1) {
392
- log.info('💡 可能是参数错误或配置问题');
393
- log.info('🔧 请检查 API 密钥、模型名称等配置是否正确');
394
- }
395
- reject(new Error(errorMsg));
396
- }
397
- });
398
- child.on('error', (err) => {
399
- const errorMsg = `启动失败: ${command} (${err.message})`;
400
- log.error(`❌ ${errorMsg}`);
401
- if (err.message.includes('ENOENT')) {
402
- log.info('💡 命令不存在,请先安装对应的 AI CLI 工具');
403
- log.info(`📦 安装命令: ${this.getInstallCommand(command).message}`);
404
- }
405
- else if (err.message.includes('EACCES')) {
406
- log.info('💡 权限不足,请检查文件权限或使用管理员权限');
407
- }
408
- reject(new Error(errorMsg));
409
- });
410
- });
411
- });
412
- }
413
- isToolAvailable(command) {
414
- return __awaiter(this, void 0, void 0, function* () {
415
- return new Promise((resolve) => {
416
- const child = (0, child_process_1.spawn)(command, ['--version'], {
417
- stdio: 'pipe',
418
- shell: true
419
- });
420
- child.on('close', (code) => {
421
- resolve(code === 0);
422
- });
423
- child.on('error', () => {
424
- resolve(false);
425
- });
426
- });
427
- });
428
- }
429
- validateAgentConfig(agent, agentConfig, log) {
430
- return __awaiter(this, void 0, void 0, function* () {
431
- if (!agentConfig) {
432
- log.error('❌ Agent 配置为空');
433
- return false;
434
- }
435
- const validateResult = (0, const_1.getAgentConfigValidator)(agent)(agentConfig);
436
- if (validateResult.success === true) {
437
- log.debug('✅ Agent 配置验证通过');
438
- return true;
439
- }
440
- else {
441
- log.error(`❌ ${agent} 配置无效: ${validateResult.error}`);
442
- return false;
443
- }
444
- });
445
- }
446
- executeAgentWithConfig(agent, agentConfig, additionalArgs, log) {
447
- return __awaiter(this, void 0, void 0, function* () {
448
- switch (agent) {
449
- case const_1.CLAUDE.value:
450
- if (agentConfig.type === 'cloudbase') {
451
- return yield this.executeClaudeCloudbaseAgent(agentConfig, additionalArgs, log);
452
- }
453
- else {
454
- return yield this.executeClaudeAgent(agentConfig, additionalArgs, log);
455
- }
456
- case const_1.QWEN.value:
457
- if (agentConfig.type === 'cloudbase') {
458
- return yield this.executeQwenCloudbaseAgent(agentConfig, additionalArgs, log);
459
- }
460
- else {
461
- return yield this.executeQwenAgent(agentConfig, additionalArgs, log);
462
- }
463
- case const_1.CODEX.value:
464
- if (agentConfig.type === 'cloudbase') {
465
- return yield this.executeCodexCloudbaseAgent(agentConfig, additionalArgs, log);
466
- }
467
- else {
468
- return yield this.executeCodexAgent(agentConfig, additionalArgs, log);
469
- }
470
- default:
471
- throw new Error(`不支持的 AI 工具: ${agent}`);
472
- }
473
- });
474
- }
475
- executeClaudeAgent({ apiKey, baseUrl }, additionalArgs, log) {
476
- return __awaiter(this, void 0, void 0, function* () {
477
- yield this.ensureClaudeCode(log);
478
- yield this.executeCommand('claude', additionalArgs, Object.assign(Object.assign({}, process.env), { ANTHROPIC_AUTH_TOKEN: apiKey, ANTHROPIC_BASE_URL: baseUrl }), log);
479
- });
480
- }
481
- executeQwenAgent({ apiKey, baseUrl, model }, additionalArgs, log) {
482
- return __awaiter(this, void 0, void 0, function* () {
483
- yield this.ensureQwenCode(log);
484
- yield this.executeCommand('qwen', additionalArgs, Object.assign(Object.assign({}, process.env), { OPENAI_API_KEY: apiKey, OPENAI_BASE_URL: baseUrl, OPENAI_MODEL: model }), log);
485
- });
486
- }
487
- executeQwenCloudbaseAgent({ provider, model }, additionalArgs, log) {
488
- return __awaiter(this, void 0, void 0, function* () {
489
- yield this.ensureQwenCode(log);
490
- const envId = yield (0, config_1.createConfigParser)().get('envId');
491
- yield (0, auth_1.checkLogin)();
492
- const credential = yield (0, utils_1.getCredential)({}, {});
493
- const accessToken = yield (0, utils_1.rawFetchAccessToken)({
494
- envId,
495
- secretId: credential.secretId,
496
- secretKey: credential.secretKey,
497
- token: credential.token
498
- });
499
- if (!accessToken.access_token) {
500
- log.error(`获取云开发 Access Token 失败,请运行 tcb login 后再重试,${JSON.stringify(accessToken)}`);
501
- process.exit(1);
502
- }
503
- const baseUrl = `https://${envId}.api.tcloudbasegateway.com/v1/ai/${provider}`;
504
- const apiKey = accessToken.access_token;
505
- yield this.executeCommand('qwen', additionalArgs, Object.assign(Object.assign({}, process.env), { OPENAI_API_KEY: apiKey, OPENAI_BASE_URL: baseUrl, OPENAI_MODEL: model }), log);
506
- });
507
- }
508
- executeClaudeCloudbaseAgent({ provider, model, transformer }, additionalArgs, log) {
509
- return __awaiter(this, void 0, void 0, function* () {
510
- yield this.ensureClaudeCodeRouter(log);
511
- yield this.ensureClaudeCode(log);
512
- yield this.configureClaudeCodeRouter(provider, model, transformer, log);
513
- yield this.restartClaudeCodeRouter(log);
514
- yield this.executeCommand('ccr', ['code', ...additionalArgs], Object.assign({}, process.env), log);
515
- });
516
- }
517
- restartClaudeCodeRouter(log) {
518
- return __awaiter(this, void 0, void 0, function* () {
519
- try {
520
- yield this.executeCommand('ccr', ['restart'], process.env, log);
521
- }
522
- catch (e) {
523
- log.error('执行 ccr restart 失败,尝试执行 ccr stop && ccr start');
524
- yield this.executeCommand('ccr', ['stop'], process.env, log);
525
- this.executeCommand('ccr', ['start'], process.env, log);
526
- const max = 3;
527
- let current = 0;
528
- while (current < max) {
529
- current++;
530
- if (current > max) {
531
- throw new Error('ccr 重启完成失败');
532
- }
533
- log.info(`${current}/${max}等待 ccr 重启完成...`);
534
- const isRunning = yield this.isClaudeCodeRouterRunning();
535
- if (isRunning) {
536
- log.info('ccr 重启完成');
537
- break;
538
- }
539
- }
540
- }
541
- });
542
- }
543
- isClaudeCodeRouterRunning() {
544
- return __awaiter(this, void 0, void 0, function* () {
545
- return new Promise((resolve) => {
546
- const child = (0, child_process_1.spawn)('ccr', ['status'], {
547
- stdio: 'pipe',
548
- env: process.env,
549
- shell: process.platform === 'win32'
550
- });
551
- child.stdout.on('data', (data) => {
552
- const str = data.toString();
553
- if (str.includes('Status: Running')) {
554
- resolve(true);
555
- child.kill();
556
- }
557
- });
558
- setTimeout(() => {
559
- resolve(false);
560
- child.kill();
561
- }, 3000);
562
- });
563
- });
564
- }
565
- configureClaudeCodeRouter(provider, model, transformer, log) {
566
- return __awaiter(this, void 0, void 0, function* () {
567
- const fs = yield Promise.resolve().then(() => __importStar(require('fs-extra')));
568
- const envId = yield (0, config_1.createConfigParser)().get('envId');
569
- yield (0, auth_1.checkLogin)();
570
- const credential = yield (0, utils_1.getCredential)({}, {});
571
- const accessToken = yield (0, utils_1.rawFetchAccessToken)({
572
- envId,
573
- secretId: credential.secretId,
574
- secretKey: credential.secretKey,
575
- token: credential.token
576
- });
577
- if (!accessToken.access_token) {
578
- log.error(`获取云开发 Access Token 失败,请运行 tcb login 后再重试,${JSON.stringify(accessToken)}`);
579
- process.exit(1);
580
- }
581
- const cloudbaseProvider = {
582
- name: 'cloudbase',
583
- api_base_url: `https://${envId}.api.tcloudbasegateway.com/v1/ai/${provider}/chat/completions`,
584
- api_key: accessToken.access_token,
585
- models: [model],
586
- transformer: { use: [transformer] }
587
- };
588
- yield fs.ensureFile(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH);
589
- const claudeCodeRouterConfig = yield fs.readFile(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, 'utf-8');
590
- if (claudeCodeRouterConfig.trim().length === 0) {
591
- log.debug('🛠️ claude-code-router 配置为空,写入 Cloudbase 配置...');
592
- yield fs.writeJson(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, { Providers: [cloudbaseProvider] });
593
- log.debug('✅ claude-code-router 配置完成');
594
- }
595
- else {
596
- const config = safeParseJson(claudeCodeRouterConfig);
597
- if (!config) {
598
- const { shouldOverwrite } = yield inquirer_1.default.prompt([
599
- {
600
- type: 'confirm',
601
- name: 'shouldOverwrite',
602
- message: 'claude-code-router 配置文件非合法 JSON ,是否覆盖?'
603
- }
604
- ]);
605
- if (shouldOverwrite) {
606
- yield fs.writeJson(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, {
607
- Providers: [cloudbaseProvider]
608
- }, { spaces: 2 });
609
- log.debug('✅ claude-code-router 配置完成');
610
- }
611
- else {
612
- log.error('❌ claude-code-router 配置文件非合法 JSON ,请手动修复');
613
- process.exit(1);
614
- }
615
- }
616
- if (typeof config !== 'object' || config === null) {
617
- log.error('❌ 未知 claude-code-router 配置文件格式请手动修复');
618
- process.exit(1);
619
- }
620
- if (!('Providers' in config)) {
621
- config.Providers = [cloudbaseProvider];
622
- }
623
- if (!Array.isArray(config.Providers)) {
624
- log.error('❌ claude-code-router 配置文件 Providers 字段非数组类型,请手动修复');
625
- process.exit(1);
626
- }
627
- const providers = config.Providers;
628
- const index = providers.findIndex((p) => typeof p === 'object' && (p === null || p === void 0 ? void 0 : p.name) === 'cloudbase');
629
- if (index !== -1) {
630
- providers[index] = cloudbaseProvider;
631
- }
632
- else {
633
- providers.push(cloudbaseProvider);
634
- }
635
- if (!('Router' in config) ||
636
- typeof config.Router !== 'object' ||
637
- config.Router === null) {
638
- config.Router = {};
639
- }
640
- config.Router.default = 'cloudbase,deepseek-v3';
641
- yield fs.writeJson(const_1.CLAUDE_CODE_ROUTER_CONFIG_PATH, config, { spaces: 2 });
642
- log.debug('✅ claude-code-router 配置完成');
643
- }
644
- });
645
- }
646
- parseArgs(args) {
647
- return args.filter((arg) => typeof arg === 'string' && arg.trim().length > 0);
648
- }
649
- ensureClaudeCodeRouter(log) {
650
- return __awaiter(this, void 0, void 0, function* () {
651
- const isAvailable = yield new Promise((resolve) => {
652
- const child = (0, child_process_1.spawn)('ccr', ['--version'], {
653
- stdio: 'pipe',
654
- shell: true
655
- });
656
- child.stdout.on('data', (data) => {
657
- data.toString().includes('Usage: ccr [command]') && resolve(true);
658
- });
659
- child.on('close', (code) => {
660
- resolve(code === 0);
661
- });
662
- child.on('error', () => {
663
- resolve(false);
664
- });
665
- });
666
- if (isAvailable) {
667
- log.debug('claude code router 已安装!');
668
- }
669
- else {
670
- const { shouldInstall } = yield inquirer_1.default.prompt([
671
- {
672
- type: 'confirm',
673
- name: 'shouldInstall',
674
- message: 'claude code router 未安装,是否安装?'
675
- }
676
- ]);
677
- if (shouldInstall) {
678
- yield this.executeCommand('npm', ['install', '-g', '@musistudio/claude-code-router'], process.env, log);
679
- log.debug('✅ claude-code-router 安装完成');
680
- }
681
- else {
682
- log.info('❌ claude code router 未安装,请手动安装');
683
- process.exit(1);
684
- }
685
- }
686
- });
687
- }
688
- ensureClaudeCode(log) {
689
- return __awaiter(this, void 0, void 0, function* () {
690
- if (yield this.isToolAvailable('claude')) {
691
- log.debug('claude code 已安装!');
692
- }
693
- else {
694
- const { shouldInstall } = yield inquirer_1.default.prompt([
695
- {
696
- type: 'confirm',
697
- name: 'shouldInstall',
698
- message: 'claude code 未安装,是否安装?'
699
- }
700
- ]);
701
- if (shouldInstall) {
702
- yield this.executeCommand('npm', ['install', '-g', '@anthropic-ai/claude-code'], process.env, log);
703
- log.debug('✅ claude code 安装完成');
704
- }
705
- else {
706
- log.info('❌ claude code 未安装,请手动安装');
707
- process.exit(1);
708
- }
709
- }
710
- });
711
- }
712
- ensureQwenCode(log) {
713
- return __awaiter(this, void 0, void 0, function* () {
714
- if (yield this.isToolAvailable('qwen')) {
715
- log.debug('qwen code 已安装!');
716
- }
717
- else {
718
- const { shouldInstall } = yield inquirer_1.default.prompt([
719
- {
720
- type: 'confirm',
721
- name: 'shouldInstall',
722
- message: 'qwen code 未安装,是否安装?'
723
- }
724
- ]);
725
- if (shouldInstall) {
726
- yield this.executeCommand('npm', ['install', '-g', '@qwen-code/qwen-code'], process.env, log);
727
- log.debug('✅ qwen code 安装完成');
728
- }
729
- else {
730
- log.info('❌ qwen code 未安装,请手动安装');
731
- process.exit(1);
732
- }
733
- }
734
- });
735
- }
736
- executeCodexAgent({ apiKey, baseUrl, model }, additionalArgs, log) {
737
- return __awaiter(this, void 0, void 0, function* () {
738
- yield this.ensureCodexCode(log);
739
- const codexArgs = [
740
- ...(model ? ['--config', `model=${model}`] : []),
741
- '--config', 'model_providers.custom.name=Custom OpenAI',
742
- ...(baseUrl ? ['--config', `model_providers.custom.base_url=${baseUrl}`] : []),
743
- '--config', 'model_providers.custom.env_key=OPENAI_API_KEY',
744
- '--config', 'model_provider=custom',
745
- ...additionalArgs
746
- ];
747
- yield this.executeCommand('codex', codexArgs, Object.assign(Object.assign({}, process.env), { OPENAI_API_KEY: apiKey }), log);
748
- });
749
- }
750
- executeCodexCloudbaseAgent({ provider, model }, additionalArgs, log) {
751
- return __awaiter(this, void 0, void 0, function* () {
752
- yield this.ensureCodexCode(log);
753
- const envId = yield (0, config_1.createConfigParser)().get('envId');
754
- yield (0, auth_1.checkLogin)();
755
- const credential = yield (0, utils_1.getCredential)({}, {});
756
- const accessToken = yield (0, utils_1.rawFetchAccessToken)({
757
- envId,
758
- secretId: credential.secretId,
759
- secretKey: credential.secretKey,
760
- token: credential.token
761
- });
762
- if (!accessToken.access_token) {
763
- log.error(`获取云开发 Access Token 失败,请运行 tcb login 后再重试,${JSON.stringify(accessToken)}`);
764
- process.exit(1);
765
- }
766
- const baseUrl = `https://${envId}.api.tcloudbasegateway.com/v1/ai/${provider}`;
767
- const apiKey = accessToken.access_token;
768
- const codexArgs = [
769
- '--config', `model=${model}`,
770
- '--config', 'model_providers.cloudbase.name=CloudBase AI',
771
- '--config', `model_providers.cloudbase.base_url=${baseUrl}`,
772
- '--config', 'model_providers.cloudbase.env_key=CLOUDBASE_ACCESS_TOKEN',
773
- '--config', 'model_provider=cloudbase',
774
- ...additionalArgs
775
- ];
776
- yield this.executeCommand('codex', codexArgs, Object.assign(Object.assign({}, process.env), { CLOUDBASE_ACCESS_TOKEN: apiKey }), log);
777
- });
778
- }
779
- ensureCodexCode(log) {
780
- return __awaiter(this, void 0, void 0, function* () {
781
- if (yield this.isToolAvailable('codex')) {
782
- log.debug('codex code 已安装!');
783
- }
784
- else {
785
- const { shouldInstall } = yield inquirer_1.default.prompt([
786
- {
787
- type: 'confirm',
788
- name: 'shouldInstall',
789
- message: 'codex code 未安装,是否安装?'
790
- }
791
- ]);
792
- if (shouldInstall) {
793
- yield this.executeCommand('npm', ['install', '-g', '@openai/codex'], process.env, log);
794
- log.debug('✅ codex code 安装完成');
795
- }
796
- else {
797
- log.info('❌ codex code 未安装,请手动安装');
798
- process.exit(1);
799
- }
800
- }
801
- });
802
- }
803
- }
804
- exports.AICommandRouter = AICommandRouter;
805
- function safeParseJson(json) {
806
- try {
807
- return JSON.parse(json);
808
- }
809
- catch (_) {
810
- return null;
811
- }
812
- }