@cloudbase/cli 2.8.0-beta.4 → 2.8.0-beta.6
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/.augment-guidelines +119 -0
- package/.claude/settings.local.json +6 -0
- package/.clinerules/cloudbase-rules.mdc +119 -0
- package/.cursor/rules/cloudbase-rules.mdc +119 -0
- package/.env.local +5 -0
- package/.mcp.json +11 -0
- package/CLAUDE.md +119 -0
- package/README.md +13 -1
- package/bin/cloudbase-mcp.js +24 -0
- package/bin/tcb.js +0 -2
- package/cloudbaserc.json +3 -0
- package/lib/commands/ai/index.js +172 -0
- package/lib/commands/cloudrun/base.js +2 -2
- package/lib/commands/index.js +1 -0
- package/lib/commands/utils.js +10 -4
- package/lib/utils/ai/banner.js +88 -0
- package/lib/utils/ai/config.js +254 -0
- package/lib/utils/ai/const.js +156 -0
- package/lib/utils/ai/ensureFiles.js +26 -0
- package/lib/utils/ai/envLocalManager.js +144 -0
- package/lib/utils/ai/router.js +1089 -0
- package/lib/utils/ai/setup.js +550 -0
- package/package.json +11 -3
- package/rules/cloudbase-platform.mdc +44 -0
- package/rules/database.mdc +25 -0
- package/rules/miniprogram-development.mdc +61 -0
- package/rules/ui-design.mdc +24 -0
- package/rules/web-development.mdc +44 -0
- package/rules/workflows.mdc +30 -0
- package/specs/mcp-global-bin/design.md +57 -0
- package/specs/mcp-global-bin/requirements.md +43 -0
- package/specs/mcp-global-bin/tasks.md +54 -0
- package/types/commands/ai/index.d.ts +23 -0
- package/types/commands/index.d.ts +1 -0
- package/types/commands/utils.d.ts +6 -0
- package/types/utils/ai/banner.d.ts +2 -0
- package/types/utils/ai/config.d.ts +79 -0
- package/types/utils/ai/const.d.ts +328 -0
- package/types/utils/ai/ensureFiles.d.ts +1 -0
- package/types/utils/ai/envLocalManager.d.ts +23 -0
- package/types/utils/ai/router.d.ts +45 -0
- package/types/utils/ai/setup.d.ts +23 -0
- package/types/utils/config.d.ts +1 -0
|
@@ -0,0 +1,550 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.AISetupWizard = void 0;
|
|
39
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
40
|
+
const path_1 = __importDefault(require("path"));
|
|
41
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
42
|
+
const error_1 = require("../../error");
|
|
43
|
+
const utils_1 = require("../../commands/utils");
|
|
44
|
+
const constants_1 = require("../../commands/constants");
|
|
45
|
+
const const_1 = require("./const");
|
|
46
|
+
const config_1 = require("./config");
|
|
47
|
+
const auth_1 = require("../../auth");
|
|
48
|
+
const output_1 = require("../output");
|
|
49
|
+
class AISetupWizard {
|
|
50
|
+
constructor(envId) {
|
|
51
|
+
this.aiConfigManager = new config_1.AIConfigManager();
|
|
52
|
+
this.envId = envId;
|
|
53
|
+
}
|
|
54
|
+
setUpDefault(log) {
|
|
55
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
+
log.info('🤖 欢迎使用 CloudBase AI ToolKit CLI 配置向导');
|
|
57
|
+
try {
|
|
58
|
+
const defaultAgent = yield this.selectAgent('选择默认使用的 AI CLI 工具:', false);
|
|
59
|
+
yield this.aiConfigManager.updateDefaultAgent(defaultAgent);
|
|
60
|
+
const currentAgent = defaultAgent;
|
|
61
|
+
yield this.configureAgent(currentAgent, log);
|
|
62
|
+
yield this.ensureGitignore();
|
|
63
|
+
log.info('✅ AI 配置完成!配置信息已保存到 .env.local 文件');
|
|
64
|
+
log.info('💡 提示:请确保 .env.local 文件已添加到 .gitignore 以保护敏感信息');
|
|
65
|
+
log.info('🚀 现在可以使用 tcb ai 命令了!');
|
|
66
|
+
return { defaultAgent };
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw new error_1.CloudBaseError('配置向导执行失败', { original: error });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
setUp(log) {
|
|
74
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
log.info('🤖 欢迎使用 CloudBase AI ToolKit CLI 配置向导');
|
|
76
|
+
try {
|
|
77
|
+
const currentAgent = yield this.selectCurrentAgent();
|
|
78
|
+
if (currentAgent !== const_1.NONE.value) {
|
|
79
|
+
yield this.configureAgent(currentAgent, log);
|
|
80
|
+
}
|
|
81
|
+
const defaultAgent = yield this.selectDefaultAgent(log);
|
|
82
|
+
yield this.aiConfigManager.updateDefaultAgent(defaultAgent);
|
|
83
|
+
yield this.ensureGitignore();
|
|
84
|
+
log.info('✅ AI 配置完成!配置信息已保存到 .env.local 文件');
|
|
85
|
+
log.info('💡 提示:请确保 .env.local 文件已添加到 .gitignore 以保护敏感信息');
|
|
86
|
+
log.info('🚀 现在可以使用 tcb ai 命令了!');
|
|
87
|
+
return { defaultAgent };
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
throw new error_1.CloudBaseError('配置向导执行失败', { original: error });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
configureEnvId(log, _envId) {
|
|
95
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
let envId;
|
|
97
|
+
if (_envId) {
|
|
98
|
+
log.info(`使用传入的 envId ${_envId}`);
|
|
99
|
+
envId = _envId;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
log.info('未传入 envId,从 cloudbaserc.json 中获取');
|
|
103
|
+
const parser = (0, config_1.createConfigParser)();
|
|
104
|
+
const configEnvId = yield parser.get('envId').catch(() => null);
|
|
105
|
+
if (!configEnvId || configEnvId === '{{env.ENV_ID}}') {
|
|
106
|
+
log.info('cloudbaserc.json 中无 envId 配置!');
|
|
107
|
+
const { authSupevisor } = yield Promise.resolve().then(() => __importStar(require('../../utils/auth')));
|
|
108
|
+
let loginState = yield authSupevisor.getLoginState();
|
|
109
|
+
if (!loginState) {
|
|
110
|
+
yield (0, auth_1.checkLogin)();
|
|
111
|
+
loginState = yield authSupevisor.getLoginState();
|
|
112
|
+
}
|
|
113
|
+
envId = yield (0, utils_1.selectEnv)({ source: [constants_1.EnvSource.MINIAPP, constants_1.EnvSource.QCLOUD] });
|
|
114
|
+
log.info(`已选择 envId: ${envId}`);
|
|
115
|
+
if (!configEnvId) {
|
|
116
|
+
parser.update('envId', '{{env.ENV_ID}}');
|
|
117
|
+
}
|
|
118
|
+
yield this.aiConfigManager.updateEnvId(envId);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
envId = configEnvId;
|
|
122
|
+
log.info(`使用 cloudbaserc.json 中的 envId: ${configEnvId}`);
|
|
123
|
+
const { shouldUpdateEnvId } = yield inquirer_1.default.prompt([
|
|
124
|
+
{
|
|
125
|
+
type: 'confirm',
|
|
126
|
+
name: 'shouldUpdateEnvId',
|
|
127
|
+
message: `当前使用的 envId 为 ${configEnvId},是否需要更新?`
|
|
128
|
+
}
|
|
129
|
+
]);
|
|
130
|
+
if (shouldUpdateEnvId) {
|
|
131
|
+
yield (0, auth_1.checkLogin)();
|
|
132
|
+
envId = yield (0, utils_1.selectEnv)({ source: [constants_1.EnvSource.MINIAPP, constants_1.EnvSource.QCLOUD] });
|
|
133
|
+
log.info(`已选择 envId: ${envId}`);
|
|
134
|
+
yield this.aiConfigManager.updateEnvId(envId);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return envId;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
selectDefaultAgent(log) {
|
|
142
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
const config = yield this.aiConfigManager.loadConfig().catch(() => null);
|
|
144
|
+
const configuredAgents = (config === null || config === void 0 ? void 0 : config.agents) ? Object.keys(config.agents) : [];
|
|
145
|
+
if (configuredAgents.length === 0) {
|
|
146
|
+
const errorMsg = '没有已配置的 AI 工具,请先运行 tcb ai --setup 进行配置';
|
|
147
|
+
log.error(errorMsg);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
if (configuredAgents.length === 1) {
|
|
151
|
+
const selectedAgent = configuredAgents[0];
|
|
152
|
+
const agentInfo = [const_1.CLAUDE, const_1.QWEN, const_1.CODEX, const_1.AIDER].find((a) => a.value === selectedAgent);
|
|
153
|
+
const agentName = (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.name) || selectedAgent.toUpperCase();
|
|
154
|
+
log.info(`🔧 自动选择已配置的唯一 AI 工具: ${agentName}`);
|
|
155
|
+
return selectedAgent;
|
|
156
|
+
}
|
|
157
|
+
const availableChoices = configuredAgents.map((agent) => {
|
|
158
|
+
const agentInfo = [const_1.CLAUDE, const_1.QWEN, const_1.CODEX, const_1.AIDER].find((a) => a.value === agent);
|
|
159
|
+
return agentInfo || { name: agent.toUpperCase(), value: agent };
|
|
160
|
+
});
|
|
161
|
+
const { agent } = yield inquirer_1.default.prompt([
|
|
162
|
+
{
|
|
163
|
+
type: 'list',
|
|
164
|
+
name: 'agent',
|
|
165
|
+
message: '选择默认使用的 AI CLI 工具:',
|
|
166
|
+
choices: availableChoices,
|
|
167
|
+
default: configuredAgents[0]
|
|
168
|
+
}
|
|
169
|
+
]);
|
|
170
|
+
return agent;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
selectCurrentAgent() {
|
|
174
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
175
|
+
return this.selectAgent('选择当前要配置的 AI CLI 工具:', true);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
selectAgent(message, includeNone = false) {
|
|
179
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
180
|
+
const { agent } = yield inquirer_1.default.prompt([
|
|
181
|
+
{
|
|
182
|
+
type: 'list',
|
|
183
|
+
name: 'agent',
|
|
184
|
+
message,
|
|
185
|
+
choices: [const_1.CLAUDE, const_1.QWEN, const_1.CODEX, const_1.AIDER, ...(includeNone ? [const_1.NONE] : [])],
|
|
186
|
+
default: const_1.CLAUDE.value
|
|
187
|
+
}
|
|
188
|
+
]);
|
|
189
|
+
return agent;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
configureAgent(agent, log) {
|
|
193
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
194
|
+
log.info(`\n📝 配置 ${agent.toUpperCase()}:`);
|
|
195
|
+
switch (agent) {
|
|
196
|
+
case const_1.CLAUDE.value:
|
|
197
|
+
return yield this.configureClaudeAgent(log);
|
|
198
|
+
case const_1.QWEN.value:
|
|
199
|
+
return yield this.configureQwenAgent(log);
|
|
200
|
+
case const_1.CODEX.value:
|
|
201
|
+
return yield this.configureCodexAgent(log);
|
|
202
|
+
case const_1.AIDER.value:
|
|
203
|
+
return yield this.configureAiderAgent(log);
|
|
204
|
+
default:
|
|
205
|
+
throw new Error(`不支持的 AI 工具: ${agent}`);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
configureClaudeAgent(log) {
|
|
210
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
211
|
+
log.info(`配置说明可参考 ${(0, output_1.genClickableLink)('https://docs.cloudbase.net/cli-v1/ai/claude')}`);
|
|
212
|
+
const { configMethod } = yield inquirer_1.default.prompt([
|
|
213
|
+
{
|
|
214
|
+
type: 'list',
|
|
215
|
+
name: 'configMethod',
|
|
216
|
+
message: '选择配置方式:',
|
|
217
|
+
choices: [
|
|
218
|
+
{ name: '使用 CloudBase 服务,一键登录,无需配置', value: 'cloudbase' },
|
|
219
|
+
{ name: '自配置 API KEY 和 Base URL', value: 'custom' }
|
|
220
|
+
],
|
|
221
|
+
default: 'cloudbase'
|
|
222
|
+
}
|
|
223
|
+
]);
|
|
224
|
+
if (configMethod === 'cloudbase') {
|
|
225
|
+
yield this.configureEnvId(log, this.envId);
|
|
226
|
+
const { provider, model, transformer } = yield this.selectCloudBaseProvider();
|
|
227
|
+
yield this.aiConfigManager.updateClaudeConfig('cloudbase', Object.assign({ provider,
|
|
228
|
+
model }, (transformer && { transformer })));
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
const { baseUrlChoice } = yield inquirer_1.default.prompt([
|
|
232
|
+
{
|
|
233
|
+
type: 'list',
|
|
234
|
+
name: 'baseUrlChoice',
|
|
235
|
+
message: '选择 API Base URL:',
|
|
236
|
+
choices: [
|
|
237
|
+
{ name: 'Kimi - https://api.moonshot.cn/anthropic', value: 'https://api.moonshot.cn/anthropic' },
|
|
238
|
+
{ name: '智谱 - https://open.bigmodel.cn/api/anthropic', value: 'https://open.bigmodel.cn/api/anthropic' },
|
|
239
|
+
{ name: '🛠️ 自定义 URL', value: 'custom' }
|
|
240
|
+
],
|
|
241
|
+
default: 'https://api.moonshot.cn/anthropic'
|
|
242
|
+
}
|
|
243
|
+
]);
|
|
244
|
+
let baseUrl;
|
|
245
|
+
if (baseUrlChoice === 'custom') {
|
|
246
|
+
const { customUrl } = yield inquirer_1.default.prompt([
|
|
247
|
+
{
|
|
248
|
+
type: 'input',
|
|
249
|
+
name: 'customUrl',
|
|
250
|
+
message: '请输入自定义 API Base URL:',
|
|
251
|
+
validate: (input) => input.trim().length > 0 || '请输入有效的 Base URL'
|
|
252
|
+
}
|
|
253
|
+
]);
|
|
254
|
+
baseUrl = customUrl;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
baseUrl = baseUrlChoice;
|
|
258
|
+
}
|
|
259
|
+
const { apikey } = yield inquirer_1.default.prompt([
|
|
260
|
+
{
|
|
261
|
+
type: 'password',
|
|
262
|
+
name: 'apikey',
|
|
263
|
+
message: 'Claude Auth Token:',
|
|
264
|
+
validate: (input) => input.length > 0 || '请输入有效的 Auth Token'
|
|
265
|
+
}
|
|
266
|
+
]);
|
|
267
|
+
yield this.aiConfigManager.updateClaudeConfig('custom', { baseUrl, apiKey: apikey });
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
configureQwenAgent(log) {
|
|
272
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
273
|
+
log.info(`配置说明可参考 ${(0, output_1.genClickableLink)('https://docs.cloudbase.net/cli-v1/ai/qwen')}`);
|
|
274
|
+
const { configMethod } = yield inquirer_1.default.prompt([
|
|
275
|
+
{
|
|
276
|
+
type: 'list',
|
|
277
|
+
name: 'configMethod',
|
|
278
|
+
message: '选择配置方式:',
|
|
279
|
+
choices: [
|
|
280
|
+
{ name: '使用 CloudBase 服务,一键登录,无需配置', value: 'cloudbase' },
|
|
281
|
+
{ name: '自配置 API KEY 和 Base URL', value: 'custom' }
|
|
282
|
+
],
|
|
283
|
+
default: 'cloudbase'
|
|
284
|
+
}
|
|
285
|
+
]);
|
|
286
|
+
if (configMethod === 'cloudbase') {
|
|
287
|
+
yield this.configureEnvId(log, this.envId);
|
|
288
|
+
const { provider, model } = yield this.selectCloudBaseProvider();
|
|
289
|
+
yield this.aiConfigManager.updateQwenConfig('cloudbase', {
|
|
290
|
+
provider,
|
|
291
|
+
model
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
const { apiKey, baseUrl, model } = yield inquirer_1.default.prompt([
|
|
296
|
+
{
|
|
297
|
+
type: 'input',
|
|
298
|
+
name: 'baseUrl',
|
|
299
|
+
message: 'API Base URL (留空使用默认):',
|
|
300
|
+
default: 'https://dashscope.aliyuncs.com/compatible-mode/v1'
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
type: 'password',
|
|
304
|
+
name: 'apiKey',
|
|
305
|
+
message: 'Qwen API Key:',
|
|
306
|
+
validate: (input) => input.length > 0 || '请输入有效的 API Key'
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
type: 'input',
|
|
310
|
+
name: 'model',
|
|
311
|
+
message: '模型名称 (留空使用默认):',
|
|
312
|
+
default: 'qwen-turbo'
|
|
313
|
+
}
|
|
314
|
+
]);
|
|
315
|
+
yield this.aiConfigManager.updateQwenConfig('custom', { baseUrl, apiKey, model });
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
configureCodexAgent(log) {
|
|
320
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
321
|
+
log.info(`配置说明可参考 ${(0, output_1.genClickableLink)('https://docs.cloudbase.net/cli-v1/ai/codex')}`);
|
|
322
|
+
const { configMethod } = yield inquirer_1.default.prompt([
|
|
323
|
+
{
|
|
324
|
+
type: 'list',
|
|
325
|
+
name: 'configMethod',
|
|
326
|
+
message: '选择配置方式:',
|
|
327
|
+
choices: [
|
|
328
|
+
{ name: '使用 CloudBase 服务,一键登录,无需配置', value: 'cloudbase' },
|
|
329
|
+
{ name: '自配置 API KEY 和 Base URL', value: 'custom' }
|
|
330
|
+
],
|
|
331
|
+
default: 'cloudbase'
|
|
332
|
+
}
|
|
333
|
+
]);
|
|
334
|
+
if (configMethod === 'cloudbase') {
|
|
335
|
+
yield this.configureEnvId(log, this.envId);
|
|
336
|
+
const { provider, model } = yield this.selectCloudBaseProvider();
|
|
337
|
+
yield this.aiConfigManager.updateCodexConfig('cloudbase', {
|
|
338
|
+
provider,
|
|
339
|
+
model
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
const { baseUrlChoice } = yield inquirer_1.default.prompt([
|
|
344
|
+
{
|
|
345
|
+
type: 'list',
|
|
346
|
+
name: 'baseUrlChoice',
|
|
347
|
+
message: '选择 API Base URL:',
|
|
348
|
+
choices: [
|
|
349
|
+
{ name: 'Kimi - https://api.moonshot.cn/v1', value: 'https://api.moonshot.cn/v1' },
|
|
350
|
+
{ name: '智谱 - https://open.bigmodel.cn/api/paas/v4', value: 'https://open.bigmodel.cn/api/paas/v4' },
|
|
351
|
+
{ name: '🛠️ 自定义 URL', value: 'custom' }
|
|
352
|
+
],
|
|
353
|
+
default: 'https://api.moonshot.cn/v1'
|
|
354
|
+
}
|
|
355
|
+
]);
|
|
356
|
+
let baseUrl;
|
|
357
|
+
if (baseUrlChoice === 'custom') {
|
|
358
|
+
const { customUrl } = yield inquirer_1.default.prompt([
|
|
359
|
+
{
|
|
360
|
+
type: 'input',
|
|
361
|
+
name: 'customUrl',
|
|
362
|
+
message: '请输入自定义 API Base URL:',
|
|
363
|
+
validate: (input) => input.trim().length > 0 || '请输入有效的 Base URL'
|
|
364
|
+
}
|
|
365
|
+
]);
|
|
366
|
+
baseUrl = customUrl;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
baseUrl = baseUrlChoice;
|
|
370
|
+
}
|
|
371
|
+
const { apiKey, model } = yield inquirer_1.default.prompt([
|
|
372
|
+
{
|
|
373
|
+
type: 'password',
|
|
374
|
+
name: 'apiKey',
|
|
375
|
+
message: 'API Key:',
|
|
376
|
+
validate: (input) => input.length > 0 || '请输入有效的 API Key'
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
type: 'input',
|
|
380
|
+
name: 'model',
|
|
381
|
+
message: '模型名称 (留空使用默认):',
|
|
382
|
+
default: (0, const_1.getDefaultModelByBaseUrl)(baseUrl)
|
|
383
|
+
}
|
|
384
|
+
]);
|
|
385
|
+
yield this.aiConfigManager.updateCodexConfig('custom', { baseUrl, apiKey, model });
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
configureAiderAgent(log) {
|
|
390
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
391
|
+
log.info(`配置说明可参考 ${(0, output_1.genClickableLink)('https://docs.cloudbase.net/cli-v1/ai/aider')}`);
|
|
392
|
+
const { configMethod } = yield inquirer_1.default.prompt([
|
|
393
|
+
{
|
|
394
|
+
type: 'list',
|
|
395
|
+
name: 'configMethod',
|
|
396
|
+
message: '选择配置方式:',
|
|
397
|
+
choices: [
|
|
398
|
+
{ name: '使用 CloudBase 服务,一键登录,无需配置', value: 'cloudbase' },
|
|
399
|
+
{ name: '自配置 API KEY 和 Base URL', value: 'custom' }
|
|
400
|
+
],
|
|
401
|
+
default: 'cloudbase'
|
|
402
|
+
}
|
|
403
|
+
]);
|
|
404
|
+
if (configMethod === 'cloudbase') {
|
|
405
|
+
yield this.configureEnvId(log, this.envId);
|
|
406
|
+
const { provider, model } = yield this.selectCloudBaseProvider();
|
|
407
|
+
yield this.aiConfigManager.updateAiderConfig('cloudbase', {
|
|
408
|
+
provider,
|
|
409
|
+
model
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
const { baseUrlChoice } = yield inquirer_1.default.prompt([
|
|
414
|
+
{
|
|
415
|
+
type: 'list',
|
|
416
|
+
name: 'baseUrlChoice',
|
|
417
|
+
message: '选择 API Base URL:',
|
|
418
|
+
choices: [
|
|
419
|
+
{ name: 'Kimi - https://api.moonshot.cn/v1', value: 'https://api.moonshot.cn/v1' },
|
|
420
|
+
{ name: '智谱 - https://open.bigmodel.cn/api/paas/v4', value: 'https://open.bigmodel.cn/api/paas/v4' },
|
|
421
|
+
{ name: '🛠️ 自定义 URL', value: 'custom' }
|
|
422
|
+
],
|
|
423
|
+
default: 'https://api.moonshot.cn/v1'
|
|
424
|
+
}
|
|
425
|
+
]);
|
|
426
|
+
let baseUrl;
|
|
427
|
+
if (baseUrlChoice === 'custom') {
|
|
428
|
+
const { customUrl } = yield inquirer_1.default.prompt([
|
|
429
|
+
{
|
|
430
|
+
type: 'input',
|
|
431
|
+
name: 'customUrl',
|
|
432
|
+
message: '请输入自定义 API Base URL:',
|
|
433
|
+
validate: (input) => input.trim().length > 0 || '请输入有效的 Base URL'
|
|
434
|
+
}
|
|
435
|
+
]);
|
|
436
|
+
baseUrl = customUrl;
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
baseUrl = baseUrlChoice;
|
|
440
|
+
}
|
|
441
|
+
const { apiKey, model } = yield inquirer_1.default.prompt([
|
|
442
|
+
{
|
|
443
|
+
type: 'password',
|
|
444
|
+
name: 'apiKey',
|
|
445
|
+
message: 'API Key:',
|
|
446
|
+
validate: (input) => input.length > 0 || '请输入有效的 API Key'
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
type: 'input',
|
|
450
|
+
name: 'model',
|
|
451
|
+
message: '模型名称:',
|
|
452
|
+
default: (0, const_1.getDefaultModelByBaseUrl)(baseUrl),
|
|
453
|
+
validate: (input) => input.length > 0 || '请输入有效的模型名称'
|
|
454
|
+
}
|
|
455
|
+
]);
|
|
456
|
+
yield this.aiConfigManager.updateAiderConfig('custom', { baseUrl, apiKey, model });
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
ensureGitignore() {
|
|
461
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
462
|
+
const gitignorePath = path_1.default.join(process.cwd(), '.gitignore');
|
|
463
|
+
try {
|
|
464
|
+
let gitignoreContent = '';
|
|
465
|
+
if (yield fs_extra_1.default.pathExists(gitignorePath)) {
|
|
466
|
+
gitignoreContent = yield fs_extra_1.default.readFile(gitignorePath, 'utf8');
|
|
467
|
+
}
|
|
468
|
+
const patterns = ['.env.local', '.env'];
|
|
469
|
+
let needsUpdate = false;
|
|
470
|
+
for (const pattern of patterns) {
|
|
471
|
+
if (!gitignoreContent.includes(pattern)) {
|
|
472
|
+
if (!needsUpdate) {
|
|
473
|
+
gitignoreContent += '\n# Environment variables\n';
|
|
474
|
+
needsUpdate = true;
|
|
475
|
+
}
|
|
476
|
+
gitignoreContent += `${pattern}\n`;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (needsUpdate) {
|
|
480
|
+
yield fs_extra_1.default.writeFile(gitignorePath, gitignoreContent);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
selectCloudBaseProvider() {
|
|
488
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
489
|
+
const { selectedProvider } = yield inquirer_1.default.prompt([
|
|
490
|
+
{
|
|
491
|
+
type: 'list',
|
|
492
|
+
name: 'selectedProvider',
|
|
493
|
+
message: '选择大模型供应商:',
|
|
494
|
+
choices: const_1.CLOUDBASE_PROVIDERS.map(p => ({ name: p.name, value: p.value })),
|
|
495
|
+
default: 'deepseek'
|
|
496
|
+
}
|
|
497
|
+
]);
|
|
498
|
+
if (selectedProvider === 'custom') {
|
|
499
|
+
const { provider, model } = yield inquirer_1.default.prompt([
|
|
500
|
+
{
|
|
501
|
+
type: 'input',
|
|
502
|
+
name: 'provider',
|
|
503
|
+
message: '请输入自定义供应商名称:',
|
|
504
|
+
validate: (input) => input.trim().length > 0 || '供应商名称不能为空'
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
type: 'input',
|
|
508
|
+
name: 'model',
|
|
509
|
+
message: '请输入模型名称:',
|
|
510
|
+
validate: (input) => input.trim().length > 0 || '模型名称不能为空'
|
|
511
|
+
}
|
|
512
|
+
]);
|
|
513
|
+
return { provider, model, isCustom: true, transformer: undefined };
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
const selectedConfig = const_1.CLOUDBASE_PROVIDERS.find(p => p.value === selectedProvider);
|
|
517
|
+
const modelChoices = [
|
|
518
|
+
...selectedConfig.models.map(m => ({ name: m, value: m })),
|
|
519
|
+
{ name: '🛠️ 自定义', value: 'custom' }
|
|
520
|
+
];
|
|
521
|
+
const { selectedModel } = yield inquirer_1.default.prompt([
|
|
522
|
+
{
|
|
523
|
+
type: 'list',
|
|
524
|
+
name: 'selectedModel',
|
|
525
|
+
message: '选择模型:',
|
|
526
|
+
choices: modelChoices,
|
|
527
|
+
default: selectedConfig.models[0]
|
|
528
|
+
}
|
|
529
|
+
]);
|
|
530
|
+
let model;
|
|
531
|
+
if (selectedModel === 'custom') {
|
|
532
|
+
const { customModel } = yield inquirer_1.default.prompt([
|
|
533
|
+
{
|
|
534
|
+
type: 'input',
|
|
535
|
+
name: 'customModel',
|
|
536
|
+
message: '请输入自定义模型名称:',
|
|
537
|
+
validate: (input) => input.trim().length > 0 || '模型名称不能为空'
|
|
538
|
+
}
|
|
539
|
+
]);
|
|
540
|
+
model = customModel;
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
model = selectedModel;
|
|
544
|
+
}
|
|
545
|
+
return { provider: selectedProvider, model, isCustom: false, transformer: selectedConfig.transformer };
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
exports.AISetupWizard = AISetupWizard;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbase/cli",
|
|
3
|
-
"version": "2.8.0-beta.
|
|
3
|
+
"version": "2.8.0-beta.6",
|
|
4
4
|
"description": "cli tool for cloudbase",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
"bin": {
|
|
23
23
|
"cloudbase": "bin/cloudbase.js",
|
|
24
|
-
"tcb": "bin/tcb.js"
|
|
24
|
+
"tcb": "bin/tcb.js",
|
|
25
|
+
"cloudbase-mcp": "bin/cloudbase-mcp.js"
|
|
25
26
|
},
|
|
26
27
|
"husky": {
|
|
27
28
|
"hooks": {
|
|
@@ -32,12 +33,14 @@
|
|
|
32
33
|
"license": "ISC",
|
|
33
34
|
"dependencies": {
|
|
34
35
|
"@cloudbase/cloud-api": "^0.5.5",
|
|
36
|
+
"@cloudbase/cloudbase-mcp": "^1.8.17",
|
|
35
37
|
"@cloudbase/framework-core": "^1.9.7",
|
|
36
38
|
"@cloudbase/functions-framework": "1.16.0",
|
|
37
39
|
"@cloudbase/iac-core": "0.0.3-alpha.11",
|
|
38
40
|
"@cloudbase/lowcode-cli": "^0.22.2",
|
|
39
41
|
"@cloudbase/manager-node": "4.3.3",
|
|
40
42
|
"@cloudbase/toolbox": "^0.7.5",
|
|
43
|
+
"@dotenvx/dotenvx": "^1.48.3",
|
|
41
44
|
"address": "^1.1.2",
|
|
42
45
|
"camelcase-keys": "^7.0.2",
|
|
43
46
|
"chalk": "^2.4.2",
|
|
@@ -47,7 +50,9 @@
|
|
|
47
50
|
"didyoumean": "^1.2.2",
|
|
48
51
|
"enquirer": "^2.3.6",
|
|
49
52
|
"execa": "^4.0.3",
|
|
53
|
+
"figlet": "^1.7.0",
|
|
50
54
|
"fs-extra": "^8.1.0",
|
|
55
|
+
"gradient-string": "^2.0.2",
|
|
51
56
|
"https-proxy-agent": "^5.0.1",
|
|
52
57
|
"inquirer": "^6.5.0",
|
|
53
58
|
"json-schema-to-typescript": "^14.0.5",
|
|
@@ -66,11 +71,13 @@
|
|
|
66
71
|
"semver": "^7.3.7",
|
|
67
72
|
"tar-fs": "^2.0.1",
|
|
68
73
|
"terminal-link": "^2.1.1",
|
|
74
|
+
"toml": "^3.0.0",
|
|
69
75
|
"unzipper": "^0.10.10",
|
|
70
76
|
"update-notifier": "^4.0.0",
|
|
71
77
|
"xdg-basedir": "^4.0.0",
|
|
72
78
|
"yargs": "^16.2.0",
|
|
73
|
-
"yargs-parser": "^21.0.1"
|
|
79
|
+
"yargs-parser": "^21.0.1",
|
|
80
|
+
"zod": "^4.0.13"
|
|
74
81
|
},
|
|
75
82
|
"devDependencies": {
|
|
76
83
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -81,6 +88,7 @@
|
|
|
81
88
|
"@types/node-fetch": "^2.5.4",
|
|
82
89
|
"@types/react": "^17.0.37",
|
|
83
90
|
"@types/semver": "^7.3.9",
|
|
91
|
+
"@types/unzipper": "^0.10.11",
|
|
84
92
|
"@types/webpack-dev-server": "^3.11.1",
|
|
85
93
|
"@typescript-eslint/eslint-plugin": "^4.8.1",
|
|
86
94
|
"@typescript-eslint/parser": "^4.8.1",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: CloudBase 平台知识和最佳实践
|
|
3
|
+
globs: ["**/cloudbaserc.json", "**/cloudfunctions/**/*"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# CloudBase 平台知识
|
|
8
|
+
|
|
9
|
+
## 存储和托管
|
|
10
|
+
1. 云开发的静态托管和云存储是两个不同的桶,一般公开可访问的可以存放在静态托管,可以获得一个公开的网页地址,同时支持配置自定义域名(需要到控制台操作),云存储适合放一些有私密性的文件,可以通过获取临时文件来获取一个临时访问地址
|
|
11
|
+
2. 云开发的静态托管域名可以通过 getWebsiteConfig 来获取,然后结合静态托管文件的路径可以拼出最终访问地址,记住如果访问地址是个目录,最后必须带有 /
|
|
12
|
+
|
|
13
|
+
## 环境和认证
|
|
14
|
+
1. 云开发的 SDK 初始化时都需要填写环境 id,可以通过 envQuery 工具来查询环境 id,然后进行登录,例如使用匿名登录
|
|
15
|
+
|
|
16
|
+
## 云函数
|
|
17
|
+
1. Node.js 的云函数中需要包含package.json,声明所需的依赖,可以使用 createFunction 来创建函数,使用 updateFunctionCode 来部署云函数,优先采用云端安装依赖,不上传 node_modules,functionRootPath 指的是函数目录的父目录,例如 cloudfuncitons 这个目录
|
|
18
|
+
|
|
19
|
+
## 数据库权限
|
|
20
|
+
1. 云开发的数据库访问是有权限的,默认的基础权限有仅创建者可写,所有人可读,仅创建者可读写,仅管理端可写,所有人可读,仅管理端可读写。如果直接从 web 端或者小程序端请求数据库,需要考虑配置合适的数据库权限,在云函数中,默认没有权限控制
|
|
21
|
+
2. 如用户无特殊要求,涉及到跨数据库集合的操作必须通过云函数实现
|
|
22
|
+
3. 如果用涉及到云函数,在保证安全的情况下,可以尽可能可能缩减云函数的数量,例如实现一个面向 c 端请求的云函数,实现一个初始化数据的云函数
|
|
23
|
+
|
|
24
|
+
## 数据模型
|
|
25
|
+
1. 获取数据模型操作对象:
|
|
26
|
+
- 小程序:需要 `@cloudbase/wx-cloud-client-sdk`,初始化 `const client = initHTTPOverCallFunction(wx.cloud)`,使用 `client.models`
|
|
27
|
+
- 云函数:需要 `@cloudbase/node-sdk@3.10+`,初始化 `const app = cloudbase.init({env})`,使用 `app.models`
|
|
28
|
+
- Web:需要 `@cloudbase/js-sdk`,初始化 `const app = cloudbase.init({env})`,登录后使用 `app.models`
|
|
29
|
+
2. 数据模型查询:可调用 MCP manageDataModel 工具查询模型列表、获取模型详细信息(含Schema字段)、获取具体的 models SDK使用文档
|
|
30
|
+
3. MySQL 数据模型调用规则
|
|
31
|
+
- MySQL 数据模型不能使用 collection 方式调用,必须使用数据模型 SDK
|
|
32
|
+
- 错误:`db.collection('model_name').get()`
|
|
33
|
+
- 正确:`app.models.model_name.list({ filter: { where: {} } })`
|
|
34
|
+
- 使用 manageDataModel 工具的 docs 方法获取具体 SDK 用法
|
|
35
|
+
|
|
36
|
+
## 控制台管理
|
|
37
|
+
创建/部署资源后,提供对应的控制台管理页面链接:
|
|
38
|
+
|
|
39
|
+
1. 静态托管:https://console.cloud.tencent.com/tcb/hosting
|
|
40
|
+
2. 云函数:https://tcb.cloud.tencent.com/dev?envId=${envId}#/scf/detail?id=${functionName}&NameSpace=${envId}
|
|
41
|
+
3. 数据库集合:https://tcb.cloud.tencent.com/dev?envId=${envId}#/db/doc/collection/${collectionName}
|
|
42
|
+
4. 数据模型:https://tcb.cloud.tencent.com/dev?envId=${envId}#/db/doc/model/${modelName}
|
|
43
|
+
|
|
44
|
+
使用方式:创建对应资源后,将变量替换为实际值,提供给用户进行管理操作。
|