@cloudbase/cli 2.9.3 → 2.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/standalone/cli.js +495 -83
- package/lib/commands/functions/layer/bind.js +11 -10
- package/lib/commands/functions/layer/create.js +5 -5
- package/lib/commands/functions/layer/delete.js +7 -5
- package/lib/commands/functions/layer/download.js +10 -5
- package/lib/commands/functions/layer/list.js +4 -9
- package/lib/commands/functions/layer/sort.js +1 -1
- package/lib/commands/functions/trigger-create.js +3 -6
- package/lib/commands/functions/trigger-delete.js +4 -7
- package/lib/commands/pull/pull.js +2 -1
- package/lib/utils/ai/const.js +8 -1
- package/lib/utils/ai/setup.js +21 -6
- package/lib/utils/config.js +1 -0
- package/lib/utils/template-manager.js +97 -12
- package/package.json +4 -4
- package/specs/tcb-pull-cnb-support/design.md +134 -0
- package/specs/tcb-pull-cnb-support/requirements.md +53 -0
- package/specs/tcb-pull-cnb-support/tasks.md +98 -0
- package/specs/tcb-pull-mcp-integration/design.md +5 -0
- package/specs/tcb-pull-mcp-integration/implementation-summary.md +5 -0
- package/specs/tcb-pull-mcp-integration/requirements.md +5 -0
- package/types/utils/ai/const.d.ts +6 -0
- package/types/utils/template-manager.d.ts +2 -0
|
@@ -52,9 +52,9 @@ let AttachFileLayer = class AttachFileLayer extends common_1.Command {
|
|
|
52
52
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53
53
|
const { codeSecret } = options;
|
|
54
54
|
const fnName = params === null || params === void 0 ? void 0 : params[0];
|
|
55
|
+
const functionService = yield (0, function_1.getFunctionService)(envId);
|
|
55
56
|
const loading = (0, utils_1.loadingFactory)();
|
|
56
57
|
loading.start('数据加载中...');
|
|
57
|
-
const functionService = yield (0, function_1.getFunctionService)(envId);
|
|
58
58
|
const envFunctions = yield functionService.listAllFunctions({
|
|
59
59
|
envId
|
|
60
60
|
});
|
|
@@ -62,33 +62,34 @@ let AttachFileLayer = class AttachFileLayer extends common_1.Command {
|
|
|
62
62
|
if (!exist) {
|
|
63
63
|
throw new error_1.CloudBaseError(`当前环境不存在此函数 [${fnName}]`);
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
const listLayersRes = yield functionService.listLayers({
|
|
66
66
|
offset: 0,
|
|
67
67
|
limit: 200,
|
|
68
|
-
searchSrc: `TCB_${envId}`
|
|
69
68
|
});
|
|
69
|
+
const layers = listLayersRes.Layers || [];
|
|
70
70
|
loading.stop();
|
|
71
|
-
|
|
71
|
+
const newLayers = layers.map((item) => ({
|
|
72
72
|
name: `[${LayerStatusMap[item.Status] || '异常'}] ${item.LayerName}`,
|
|
73
73
|
value: item.LayerName
|
|
74
74
|
}));
|
|
75
|
-
if (!
|
|
75
|
+
if (!newLayers.length) {
|
|
76
76
|
throw new error_1.CloudBaseError('没有可用的文件层,请先创建文件层!');
|
|
77
77
|
}
|
|
78
78
|
const { layer } = yield (0, enquirer_1.prompt)({
|
|
79
79
|
type: 'select',
|
|
80
80
|
name: 'layer',
|
|
81
81
|
message: '选择文件层名称',
|
|
82
|
-
choices:
|
|
82
|
+
choices: newLayers,
|
|
83
83
|
result(choices) {
|
|
84
84
|
const keys = Object.values(this.map(choices));
|
|
85
85
|
return keys[0];
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
|
-
let
|
|
88
|
+
let listLayerVersionsRes = yield functionService.listLayerVersions({
|
|
89
89
|
name: layer
|
|
90
90
|
});
|
|
91
|
-
|
|
91
|
+
let layerVersions = listLayerVersionsRes.LayerVersions || [];
|
|
92
|
+
const versions = layerVersions.map((item) => String(item.LayerVersion));
|
|
92
93
|
if (!versions.length) {
|
|
93
94
|
throw new error_1.CloudBaseError('没有可用的文件层版本,请先创建文件层版本!');
|
|
94
95
|
}
|
|
@@ -99,7 +100,7 @@ let AttachFileLayer = class AttachFileLayer extends common_1.Command {
|
|
|
99
100
|
choices: versions
|
|
100
101
|
});
|
|
101
102
|
loading.start('文件层绑定中...');
|
|
102
|
-
yield
|
|
103
|
+
yield functionService.attachLayer({
|
|
103
104
|
envId,
|
|
104
105
|
functionName: fnName,
|
|
105
106
|
layerName: layer,
|
|
@@ -163,7 +164,7 @@ let UnAttachFileLayer = class UnAttachFileLayer extends common_1.Command {
|
|
|
163
164
|
}
|
|
164
165
|
});
|
|
165
166
|
loading.start('文件层解绑中...');
|
|
166
|
-
yield
|
|
167
|
+
yield functionService.unAttachLayer({
|
|
167
168
|
envId,
|
|
168
169
|
functionName: fnName,
|
|
169
170
|
layerName: layer.LayerName,
|
|
@@ -51,20 +51,20 @@ let CreateFileLayer = class CreateFileLayer extends common_1.Command {
|
|
|
51
51
|
const alias = params === null || params === void 0 ? void 0 : params[0];
|
|
52
52
|
const { file } = options;
|
|
53
53
|
const layerName = `${alias}_${envId}`;
|
|
54
|
-
const
|
|
54
|
+
const listLayerRes = yield functionsService.listLayers({
|
|
55
55
|
offset: 0,
|
|
56
56
|
limit: 200
|
|
57
57
|
});
|
|
58
|
+
const layers = listLayerRes.Layers || [];
|
|
58
59
|
if (layers.find(({ LayerName }) => LayerName === layerName)) {
|
|
59
60
|
throw new toolbox_1.CloudBaseError(`层名称 ${layerName} 已被您的当前环境或其他环境占用,请换用别的名称`);
|
|
60
61
|
}
|
|
61
62
|
const filePath = path_1.default.resolve(file);
|
|
62
|
-
const runtimes = ['Nodejs12.16', 'Nodejs8.9', 'Php7', 'Java8'];
|
|
63
|
+
const runtimes = ['Nodejs14.18', 'Nodejs12.16', 'Nodejs8.9', 'Php7', 'Java8'];
|
|
63
64
|
const loading = (0, utils_1.loadingFactory)();
|
|
64
65
|
loading.start('文件层创建中...');
|
|
65
|
-
yield
|
|
66
|
-
|
|
67
|
-
layerName,
|
|
66
|
+
yield functionsService.createLayer({
|
|
67
|
+
name: layerName,
|
|
68
68
|
runtimes,
|
|
69
69
|
contentPath: filePath
|
|
70
70
|
});
|
|
@@ -42,12 +42,13 @@ let DeleteFileLayer = class DeleteFileLayer extends common_1.Command {
|
|
|
42
42
|
return __awaiter(this, void 0, void 0, function* () {
|
|
43
43
|
const loading = (0, utils_1.loadingFactory)();
|
|
44
44
|
loading.start('数据加载中...');
|
|
45
|
-
const
|
|
45
|
+
const functionService = yield (0, function_1.getFunctionService)(envId);
|
|
46
|
+
const listLayersRes = yield functionService.listLayers({
|
|
46
47
|
offset: 0,
|
|
47
48
|
limit: 200,
|
|
48
|
-
searchSrc: `TCB_${envId}`
|
|
49
49
|
});
|
|
50
50
|
loading.stop();
|
|
51
|
+
const layers = listLayersRes.Layers || [];
|
|
51
52
|
if (!layers.length) {
|
|
52
53
|
throw new error_1.CloudBaseError('当前环境没有可用的文件层,请先创建文件层!');
|
|
53
54
|
}
|
|
@@ -57,10 +58,11 @@ let DeleteFileLayer = class DeleteFileLayer extends common_1.Command {
|
|
|
57
58
|
message: '选择文件层名称',
|
|
58
59
|
choices: layers.map((item) => item.LayerName)
|
|
59
60
|
});
|
|
60
|
-
let
|
|
61
|
+
let listLayerVersionsRes = yield functionService.listLayerVersions({
|
|
61
62
|
name: layer
|
|
62
63
|
});
|
|
63
|
-
|
|
64
|
+
const layerVersions = listLayerVersionsRes.LayerVersions || [];
|
|
65
|
+
const versions = layerVersions.map((item) => String(item.LayerVersion));
|
|
64
66
|
const { version } = yield (0, enquirer_1.prompt)({
|
|
65
67
|
type: 'select',
|
|
66
68
|
name: 'version',
|
|
@@ -68,7 +70,7 @@ let DeleteFileLayer = class DeleteFileLayer extends common_1.Command {
|
|
|
68
70
|
choices: versions
|
|
69
71
|
});
|
|
70
72
|
loading.start('文件层删除中...');
|
|
71
|
-
yield
|
|
73
|
+
yield functionService.deleteLayerVersion({
|
|
72
74
|
name: layer,
|
|
73
75
|
version: Number(version)
|
|
74
76
|
});
|
|
@@ -50,13 +50,14 @@ let DownloadFileLayer = class DownloadFileLayer extends common_1.Command {
|
|
|
50
50
|
execute(envId, options) {
|
|
51
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52
52
|
const { dest } = options;
|
|
53
|
+
const functionService = yield (0, function_1.getFunctionService)(envId);
|
|
53
54
|
const loading = (0, utils_1.loadingFactory)();
|
|
54
55
|
loading.start('数据加载中...');
|
|
55
|
-
const
|
|
56
|
+
const listLayersRes = yield functionService.listLayers({
|
|
56
57
|
offset: 0,
|
|
57
58
|
limit: 200,
|
|
58
|
-
searchSrc: `TCB_${envId}`
|
|
59
59
|
});
|
|
60
|
+
const layers = listLayersRes.Layers || [];
|
|
60
61
|
loading.stop();
|
|
61
62
|
if (!layers.length) {
|
|
62
63
|
throw new error_1.CloudBaseError('当前环境没有可用的文件层,请先创建文件层!');
|
|
@@ -67,10 +68,11 @@ let DownloadFileLayer = class DownloadFileLayer extends common_1.Command {
|
|
|
67
68
|
message: '选择文件层名称',
|
|
68
69
|
choices: layers.map((item) => item.LayerName)
|
|
69
70
|
});
|
|
70
|
-
let
|
|
71
|
+
let listLayerVersionsRes = yield functionService.listLayerVersions({
|
|
71
72
|
name: layer
|
|
72
73
|
});
|
|
73
|
-
|
|
74
|
+
const layerVersions = listLayerVersionsRes.LayerVersions || [];
|
|
75
|
+
const versions = layerVersions.map((item) => String(item.LayerVersion));
|
|
74
76
|
const { version } = yield (0, enquirer_1.prompt)({
|
|
75
77
|
type: 'select',
|
|
76
78
|
name: 'version',
|
|
@@ -81,9 +83,12 @@ let DownloadFileLayer = class DownloadFileLayer extends common_1.Command {
|
|
|
81
83
|
if (!dest) {
|
|
82
84
|
destPath = path_1.default.resolve(process.cwd(), 'layers');
|
|
83
85
|
}
|
|
86
|
+
else {
|
|
87
|
+
destPath = path_1.default.resolve(dest);
|
|
88
|
+
}
|
|
84
89
|
loading.start('文件下载中...');
|
|
85
90
|
yield fs_extra_1.default.ensureDir(destPath);
|
|
86
|
-
yield
|
|
91
|
+
yield functionService.downloadLayer({
|
|
87
92
|
destPath,
|
|
88
93
|
version: Number(version),
|
|
89
94
|
name: layer
|
|
@@ -57,26 +57,21 @@ let ListFileLayer = class ListFileLayer extends common_1.Command {
|
|
|
57
57
|
const functionService = yield (0, function_1.getFunctionService)(envId);
|
|
58
58
|
if (layer && typeof layer === 'string') {
|
|
59
59
|
const layerName = `${layer}_${envId}`;
|
|
60
|
-
|
|
60
|
+
const listLayerVersionsRes = yield functionService.listLayerVersions({
|
|
61
61
|
name: layerName
|
|
62
62
|
});
|
|
63
|
+
data = listLayerVersionsRes.LayerVersions || [];
|
|
63
64
|
}
|
|
64
65
|
else if (name && typeof name === 'string') {
|
|
65
66
|
const res = yield functionService.getFunctionDetail(name, codeSecret);
|
|
66
67
|
data = (res === null || res === void 0 ? void 0 : res.Layers) || [];
|
|
67
68
|
}
|
|
68
|
-
else if (envId) {
|
|
69
|
-
data = yield (0, function_1.listLayers)({
|
|
70
|
-
offset: 0,
|
|
71
|
-
limit: 200,
|
|
72
|
-
searchSrc: `TCB_${envId}`
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
69
|
else {
|
|
76
|
-
|
|
70
|
+
const listLayersRes = yield functionService.listLayers({
|
|
77
71
|
offset: 0,
|
|
78
72
|
limit: 200
|
|
79
73
|
});
|
|
74
|
+
data = listLayersRes.Layers || [];
|
|
80
75
|
}
|
|
81
76
|
loading.stop();
|
|
82
77
|
const head = ['优先级', '名称', '状态', '版本', '证书', '支持运行时', '创建时间'];
|
|
@@ -75,7 +75,7 @@ let SortFileLayer = class SortFileLayer extends common_1.Command {
|
|
|
75
75
|
});
|
|
76
76
|
sortLayers = sortLayers.map((item) => lodash_1.default.pick(item, ['LayerName', 'LayerVersion']));
|
|
77
77
|
loading.start('文件层排序中...');
|
|
78
|
-
yield
|
|
78
|
+
yield functionService.updateFunctionLayer({
|
|
79
79
|
envId,
|
|
80
80
|
functionName: fnName,
|
|
81
81
|
layers: sortLayers
|
|
@@ -51,6 +51,7 @@ let CreateTrigger = class CreateTrigger extends common_1.Command {
|
|
|
51
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52
52
|
const { envId, config: { functions } } = ctx;
|
|
53
53
|
const name = params === null || params === void 0 ? void 0 : params[0];
|
|
54
|
+
const functionService = yield (0, function_1.getFunctionService)(envId);
|
|
54
55
|
let isBatchCreateTrigger = false;
|
|
55
56
|
if (!name) {
|
|
56
57
|
const { isBatch } = yield inquirer_1.default.prompt({
|
|
@@ -65,7 +66,7 @@ let CreateTrigger = class CreateTrigger extends common_1.Command {
|
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
if (isBatchCreateTrigger) {
|
|
68
|
-
return
|
|
69
|
+
return functionService.batchCreateTriggers({
|
|
69
70
|
envId,
|
|
70
71
|
functions
|
|
71
72
|
});
|
|
@@ -78,11 +79,7 @@ let CreateTrigger = class CreateTrigger extends common_1.Command {
|
|
|
78
79
|
if (!triggers || !triggers.length) {
|
|
79
80
|
throw new error_1.CloudBaseError('触发器配置不能为空');
|
|
80
81
|
}
|
|
81
|
-
yield (
|
|
82
|
-
envId,
|
|
83
|
-
functionName: name,
|
|
84
|
-
triggers
|
|
85
|
-
});
|
|
82
|
+
yield functionService.createFunctionTriggers(name, triggers);
|
|
86
83
|
(0, logger_1.successLog)(`[${name}] 创建云函数触发器成功!`);
|
|
87
84
|
});
|
|
88
85
|
}
|
|
@@ -51,6 +51,7 @@ let DeleteTrigger = class DeleteTrigger extends common_1.Command {
|
|
|
51
51
|
const { envId, config: { functions } } = ctx;
|
|
52
52
|
const name = params === null || params === void 0 ? void 0 : params[0];
|
|
53
53
|
const triggerName = params === null || params === void 0 ? void 0 : params[1];
|
|
54
|
+
const functionService = yield (0, function_1.getFunctionService)(envId);
|
|
54
55
|
let isBatchDeleteTriggers;
|
|
55
56
|
let isBatchDeleteFunctionTriggers = false;
|
|
56
57
|
if (!name) {
|
|
@@ -74,7 +75,7 @@ let DeleteTrigger = class DeleteTrigger extends common_1.Command {
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
if (isBatchDeleteTriggers) {
|
|
77
|
-
return
|
|
78
|
+
return functionService.batchDeleteTriggers({
|
|
78
79
|
envId,
|
|
79
80
|
functions
|
|
80
81
|
});
|
|
@@ -93,7 +94,7 @@ let DeleteTrigger = class DeleteTrigger extends common_1.Command {
|
|
|
93
94
|
}
|
|
94
95
|
if (isBatchDeleteFunctionTriggers) {
|
|
95
96
|
const func = functions.find((item) => item.name === name);
|
|
96
|
-
return
|
|
97
|
+
return functionService.batchDeleteTriggers({
|
|
97
98
|
envId,
|
|
98
99
|
functions: [func]
|
|
99
100
|
});
|
|
@@ -101,11 +102,7 @@ let DeleteTrigger = class DeleteTrigger extends common_1.Command {
|
|
|
101
102
|
if (!triggerName) {
|
|
102
103
|
throw new error_1.CloudBaseError('触发器名称不能为空');
|
|
103
104
|
}
|
|
104
|
-
yield (
|
|
105
|
-
envId,
|
|
106
|
-
functionName: name,
|
|
107
|
-
triggerName
|
|
108
|
-
});
|
|
105
|
+
yield functionService.deleteFunctionTrigger(name, triggerName);
|
|
109
106
|
});
|
|
110
107
|
}
|
|
111
108
|
};
|
|
@@ -45,7 +45,7 @@ let PullCommand = class PullCommand extends common_1.Command {
|
|
|
45
45
|
desc: '强制清理目标输出目录(慎用)'
|
|
46
46
|
}
|
|
47
47
|
],
|
|
48
|
-
desc: '拉取项目模板\n\n支持的内置模板:\n miniprogram - 微信小程序 + CloudBase\n react - Web 应用 - React + CloudBase\n vue - Web 应用 - Vue + CloudBase\n uniapp - 跨端应用 - UniApp + CloudBase\n rules - AI 规则和配置\n\n支持的 Git 仓库:\n GitHub: https://github.com/user/repo\n Gitee: https://gitee.com/user/repo\n SSH: git@github.com:user/repo.git\n\n支持 Git 子目录:\n https://github.com/user/repo/tree/main/src/templates\n\n示例:\n tcb pull miniprogram\n tcb pull https://github.com/user/repo\n tcb pull https://
|
|
48
|
+
desc: '拉取项目模板\n\n支持的内置模板:\n miniprogram - 微信小程序 + CloudBase\n react - Web 应用 - React + CloudBase\n vue - Web 应用 - Vue + CloudBase\n uniapp - 跨端应用 - UniApp + CloudBase\n rules - AI 规则和配置\n\n支持的 Git 仓库:\n GitHub: https://github.com/user/repo\n Gitee: https://gitee.com/user/repo\n CNB: https://cnb.cool/user/repo\n SSH: git@github.com:user/repo.git\n\n支持 Git 子目录:\n https://github.com/user/repo/tree/main/src/templates\n https://cnb.cool/user/repo/tree/main/examples\n\n示例:\n tcb pull miniprogram\n tcb pull https://github.com/user/repo\n tcb pull https://cnb.cool/user/repo\n tcb pull https://cnb.cool/user/repo/tree/main/examples --output ./my-project',
|
|
49
49
|
requiredEnvId: false,
|
|
50
50
|
withoutAuth: true
|
|
51
51
|
};
|
|
@@ -84,6 +84,7 @@ let PullCommand = class PullCommand extends common_1.Command {
|
|
|
84
84
|
log.info('\n🌐 支持的 Git 仓库格式:');
|
|
85
85
|
log.info(' GitHub: https://github.com/user/repo');
|
|
86
86
|
log.info(' Gitee: https://gitee.com/user/repo');
|
|
87
|
+
log.info(' CNB: https://cnb.cool/user/repo');
|
|
87
88
|
log.info(' SSH: git@github.com:user/repo.git');
|
|
88
89
|
log.info('\n📁 支持 Git 子目录:');
|
|
89
90
|
log.info(' https://github.com/user/repo/tree/main/src/templates');
|
package/lib/utils/ai/const.js
CHANGED
|
@@ -132,6 +132,12 @@ exports.CLOUDBASE_PROVIDERS = [
|
|
|
132
132
|
models: ['deepseek-v3'],
|
|
133
133
|
transformer: 'deepseek'
|
|
134
134
|
},
|
|
135
|
+
{
|
|
136
|
+
name: 'LongCat',
|
|
137
|
+
value: 'longcat',
|
|
138
|
+
models: ['LongCat-Flash-Chat'],
|
|
139
|
+
transformer: undefined
|
|
140
|
+
},
|
|
135
141
|
];
|
|
136
142
|
function getDefaultConfig(agent) {
|
|
137
143
|
const agentConfig = exports.AGENTS.find((a) => a.value === agent);
|
|
@@ -157,7 +163,8 @@ function getAgentConfigValidator(agent) {
|
|
|
157
163
|
exports.getAgentConfigValidator = getAgentConfigValidator;
|
|
158
164
|
exports.BASE_URL_MODEL_MAPPING = {
|
|
159
165
|
'https://api.moonshot.cn/v1': 'kimi-k2-0711-preview',
|
|
160
|
-
'https://open.bigmodel.cn/api/paas/v4': 'glm-4.5'
|
|
166
|
+
'https://open.bigmodel.cn/api/paas/v4': 'glm-4.5',
|
|
167
|
+
'https://api.longcat.chat/openai': 'LongCat-Flash-Chat'
|
|
161
168
|
};
|
|
162
169
|
function getDefaultModelByBaseUrl(baseUrl) {
|
|
163
170
|
return exports.BASE_URL_MODEL_MAPPING[baseUrl] || 'gpt-4';
|
package/lib/utils/ai/setup.js
CHANGED
|
@@ -194,8 +194,8 @@ class AISetupWizard {
|
|
|
194
194
|
type: 'list',
|
|
195
195
|
name: 'agent',
|
|
196
196
|
message: `${message} ${const_1.LIST_HINT}`,
|
|
197
|
-
choices: [const_1.
|
|
198
|
-
default: const_1.
|
|
197
|
+
choices: [const_1.CODEBUDDY, const_1.CLAUDE, const_1.QWEN, const_1.CODEX, const_1.CURSOR, const_1.AIDER, ...(includeNone ? [const_1.NONE] : [])],
|
|
198
|
+
default: const_1.CODEBUDDY.value
|
|
199
199
|
}
|
|
200
200
|
]);
|
|
201
201
|
return agent;
|
|
@@ -271,6 +271,10 @@ class AISetupWizard {
|
|
|
271
271
|
name: 'DeepSeek - https://api.deepseek.com/anthropic',
|
|
272
272
|
value: 'https://api.deepseek.com/anthropic'
|
|
273
273
|
},
|
|
274
|
+
{
|
|
275
|
+
name: 'LongCat - https://api.longcat.chat/anthropic',
|
|
276
|
+
value: 'https://api.longcat.chat/anthropic'
|
|
277
|
+
},
|
|
274
278
|
{ name: '🛠️ 自定义 URL', value: 'custom' }
|
|
275
279
|
],
|
|
276
280
|
default: 'https://api.moonshot.cn/anthropic'
|
|
@@ -295,7 +299,7 @@ class AISetupWizard {
|
|
|
295
299
|
{
|
|
296
300
|
type: 'password',
|
|
297
301
|
name: 'apiKey',
|
|
298
|
-
message: '
|
|
302
|
+
message: 'Auth Token:',
|
|
299
303
|
validate: (input) => input.length > 0 || '请输入有效的 Auth Token'
|
|
300
304
|
}
|
|
301
305
|
]);
|
|
@@ -312,6 +316,9 @@ class AISetupWizard {
|
|
|
312
316
|
else if (baseUrl === 'https://api.deepseek.com/anthropic') {
|
|
313
317
|
defaultModel = 'deepseek-chat';
|
|
314
318
|
}
|
|
319
|
+
else if (baseUrl === 'https://api.longcat.chat/anthropic') {
|
|
320
|
+
defaultModel = 'LongCat-Flash-Chat';
|
|
321
|
+
}
|
|
315
322
|
else {
|
|
316
323
|
defaultModel = '';
|
|
317
324
|
}
|
|
@@ -370,7 +377,7 @@ class AISetupWizard {
|
|
|
370
377
|
{
|
|
371
378
|
type: 'password',
|
|
372
379
|
name: 'apiKey',
|
|
373
|
-
message: '
|
|
380
|
+
message: 'API Key:',
|
|
374
381
|
validate: (input) => input.length > 0 || '请输入有效的 API Key'
|
|
375
382
|
},
|
|
376
383
|
{
|
|
@@ -426,6 +433,10 @@ class AISetupWizard {
|
|
|
426
433
|
name: '智谱 - https://open.bigmodel.cn/api/paas/v4',
|
|
427
434
|
value: 'https://open.bigmodel.cn/api/paas/v4'
|
|
428
435
|
},
|
|
436
|
+
{
|
|
437
|
+
name: 'LongCat - https://api.longcat.chat/openai',
|
|
438
|
+
value: 'https://api.longcat.chat/openai'
|
|
439
|
+
},
|
|
429
440
|
{ name: '🛠️ 自定义 URL', value: 'custom' }
|
|
430
441
|
],
|
|
431
442
|
default: 'https://api.moonshot.cn/v1'
|
|
@@ -505,6 +516,10 @@ class AISetupWizard {
|
|
|
505
516
|
name: '智谱 - https://open.bigmodel.cn/api/paas/v4',
|
|
506
517
|
value: 'https://open.bigmodel.cn/api/paas/v4'
|
|
507
518
|
},
|
|
519
|
+
{
|
|
520
|
+
name: 'LongCat - https://api.longcat.chat/openai',
|
|
521
|
+
value: 'https://api.longcat.chat/openai'
|
|
522
|
+
},
|
|
508
523
|
{ name: '🛠️ 自定义 URL', value: 'custom' }
|
|
509
524
|
],
|
|
510
525
|
default: 'https://api.moonshot.cn/v1'
|
|
@@ -669,8 +684,8 @@ class AISetupWizard {
|
|
|
669
684
|
{
|
|
670
685
|
type: 'password',
|
|
671
686
|
name: 'apiKey',
|
|
672
|
-
message: '
|
|
673
|
-
validate: (input) => input.trim().length > 0 || 'API Key
|
|
687
|
+
message: 'API Key:',
|
|
688
|
+
validate: (input) => input.trim().length > 0 || '请输入有效的 API Key'
|
|
674
689
|
}
|
|
675
690
|
]);
|
|
676
691
|
yield this.aiConfigManager.updateCodebuddyConfig('custom', {
|
package/lib/utils/config.js
CHANGED
|
@@ -100,7 +100,8 @@ class TemplateManager {
|
|
|
100
100
|
if (!source || typeof source !== 'string') {
|
|
101
101
|
return false;
|
|
102
102
|
}
|
|
103
|
-
return source.startsWith('http') || source.startsWith('git@') ||
|
|
103
|
+
return source.startsWith('http') || source.startsWith('git@') ||
|
|
104
|
+
source.includes('github.com') || source.includes('gitee.com') || source.includes('cnb.cool');
|
|
104
105
|
}
|
|
105
106
|
parseGitUrl(url) {
|
|
106
107
|
const githubMatch = url.match(/https?:\/\/github\.com\/([^\/]+)\/([^\/]+)(?:\/tree\/([^\/]+)\/(.+))?/);
|
|
@@ -123,10 +124,36 @@ class TemplateManager {
|
|
|
123
124
|
subpath: giteeMatch[4]
|
|
124
125
|
};
|
|
125
126
|
}
|
|
127
|
+
const cnbMatchWithDash = url.match(/https?:\/\/cnb\.cool\/(.+?)\/-\/tree\/([^\/]+)\/(.+)$/);
|
|
128
|
+
if (cnbMatchWithDash) {
|
|
129
|
+
const pathParts = cnbMatchWithDash[1].split('/').filter(p => p.length > 0);
|
|
130
|
+
if (pathParts.length >= 2) {
|
|
131
|
+
return {
|
|
132
|
+
platform: 'cnb',
|
|
133
|
+
owner: pathParts[0],
|
|
134
|
+
repo: pathParts.slice(1).join('/'),
|
|
135
|
+
branch: cnbMatchWithDash[2] || 'main',
|
|
136
|
+
subpath: cnbMatchWithDash[3]
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const cnbMatch = url.match(/https?:\/\/cnb\.cool\/(.+?)(?:\/tree\/([^\/]+)\/(.+))?$/);
|
|
141
|
+
if (cnbMatch) {
|
|
142
|
+
const pathParts = cnbMatch[1].split('/').filter(p => p.length > 0);
|
|
143
|
+
if (pathParts.length >= 2) {
|
|
144
|
+
return {
|
|
145
|
+
platform: 'cnb',
|
|
146
|
+
owner: pathParts[0],
|
|
147
|
+
repo: pathParts.slice(1).join('/'),
|
|
148
|
+
branch: cnbMatch[2] || 'main',
|
|
149
|
+
subpath: cnbMatch[3]
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
126
153
|
const sshMatch = url.match(/git@([^:]+):([^\/]+)\/([^\/]+)\.git/);
|
|
127
154
|
if (sshMatch) {
|
|
128
155
|
return {
|
|
129
|
-
platform: sshMatch[1] === 'github.com' ? 'github' : 'gitee',
|
|
156
|
+
platform: sshMatch[1] === 'github.com' ? 'github' : (sshMatch[1] === 'cnb.cool' ? 'cnb' : 'gitee'),
|
|
130
157
|
owner: sshMatch[2],
|
|
131
158
|
repo: sshMatch[3],
|
|
132
159
|
branch: 'main'
|
|
@@ -141,8 +168,43 @@ class TemplateManager {
|
|
|
141
168
|
else if (gitInfo.platform === 'gitee') {
|
|
142
169
|
return `https://gitee.com/${gitInfo.owner}/${gitInfo.repo}.git`;
|
|
143
170
|
}
|
|
171
|
+
else if (gitInfo.platform === 'cnb') {
|
|
172
|
+
return `https://cnb.cool/${gitInfo.owner}/${gitInfo.repo}.git`;
|
|
173
|
+
}
|
|
144
174
|
throw new error_1.CloudBaseError(`不支持的 Git 平台: ${gitInfo.platform}`);
|
|
145
175
|
}
|
|
176
|
+
cloneWithSubpathOptimized(gitUrl, tempDir, gitInfo, log) {
|
|
177
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
try {
|
|
179
|
+
yield this.git.clone(gitUrl, tempDir, [
|
|
180
|
+
'--depth', '1',
|
|
181
|
+
'--single-branch',
|
|
182
|
+
'--branch', gitInfo.branch,
|
|
183
|
+
'--filter=blob:none'
|
|
184
|
+
]);
|
|
185
|
+
const subpathFull = path_1.default.join(tempDir, gitInfo.subpath);
|
|
186
|
+
if (!(yield fs_extra_1.default.pathExists(subpathFull))) {
|
|
187
|
+
throw new error_1.CloudBaseError(`子目录不存在: ${gitInfo.subpath}`);
|
|
188
|
+
}
|
|
189
|
+
const tempContentDir = path_1.default.join(tempDir, '.temp-content-' + Date.now());
|
|
190
|
+
yield fs_extra_1.default.move(subpathFull, tempContentDir);
|
|
191
|
+
const tempFiles = yield fs_extra_1.default.readdir(tempDir);
|
|
192
|
+
for (const file of tempFiles) {
|
|
193
|
+
if (file !== path_1.default.basename(tempContentDir)) {
|
|
194
|
+
yield fs_extra_1.default.remove(path_1.default.join(tempDir, file));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const contentFiles = yield fs_extra_1.default.readdir(tempContentDir);
|
|
198
|
+
for (const file of contentFiles) {
|
|
199
|
+
yield fs_extra_1.default.move(path_1.default.join(tempContentDir, file), path_1.default.join(tempDir, file));
|
|
200
|
+
}
|
|
201
|
+
yield fs_extra_1.default.remove(tempContentDir);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
throw new error_1.CloudBaseError(`克隆子目录失败: ${error.message}`, { original: error });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
146
208
|
cloneWithSubpath(gitUrl, tempDir, gitInfo, log) {
|
|
147
209
|
return __awaiter(this, void 0, void 0, function* () {
|
|
148
210
|
try {
|
|
@@ -195,10 +257,10 @@ class TemplateManager {
|
|
|
195
257
|
log.info(`📦 正在从 ${gitInfo.platform} 下载模板到临时目录...`);
|
|
196
258
|
try {
|
|
197
259
|
if (gitInfo.subpath) {
|
|
198
|
-
yield this.
|
|
260
|
+
yield this.cloneWithSubpathOptimized(gitUrl, tempDir, gitInfo, log);
|
|
199
261
|
}
|
|
200
262
|
else {
|
|
201
|
-
yield this.git.clone(gitUrl, tempDir);
|
|
263
|
+
yield this.git.clone(gitUrl, tempDir, ['--depth', '1', '--single-branch', '--branch', gitInfo.branch]);
|
|
202
264
|
}
|
|
203
265
|
log.info(`✅ Git 模板下载完成到临时目录`);
|
|
204
266
|
}
|
|
@@ -254,10 +316,14 @@ class TemplateManager {
|
|
|
254
316
|
if (stat.isDirectory()) {
|
|
255
317
|
yield fs_extra_1.default.ensureDir(destPath);
|
|
256
318
|
const entries = yield fs_extra_1.default.readdir(srcPath, { withFileTypes: true });
|
|
257
|
-
|
|
319
|
+
const copyPromises = entries.map(entry => {
|
|
258
320
|
const entrySrcPath = path_1.default.join(srcPath, entry.name);
|
|
259
321
|
const entryDestPath = path_1.default.join(destPath, entry.name);
|
|
260
|
-
|
|
322
|
+
return copyFile(entrySrcPath, entryDestPath);
|
|
323
|
+
});
|
|
324
|
+
const chunks = this.chunkArray(copyPromises, 10);
|
|
325
|
+
for (const chunk of chunks) {
|
|
326
|
+
yield Promise.all(chunk);
|
|
261
327
|
}
|
|
262
328
|
}
|
|
263
329
|
else {
|
|
@@ -278,10 +344,14 @@ class TemplateManager {
|
|
|
278
344
|
}
|
|
279
345
|
});
|
|
280
346
|
const entries = yield fs_extra_1.default.readdir(sourceDir, { withFileTypes: true });
|
|
281
|
-
|
|
347
|
+
const copyPromises = entries.map(entry => {
|
|
282
348
|
const srcPath = path_1.default.join(sourceDir, entry.name);
|
|
283
349
|
const destPath = path_1.default.join(targetDir, entry.name);
|
|
284
|
-
|
|
350
|
+
return copyFile(srcPath, destPath);
|
|
351
|
+
});
|
|
352
|
+
const chunks = this.chunkArray(copyPromises, 5);
|
|
353
|
+
for (const chunk of chunks) {
|
|
354
|
+
yield Promise.all(chunk);
|
|
285
355
|
}
|
|
286
356
|
log.info(`📊 复制统计: ${copiedCount} 个新文件复制, ${overwrittenCount} 个文件覆盖`);
|
|
287
357
|
});
|
|
@@ -296,10 +366,14 @@ class TemplateManager {
|
|
|
296
366
|
if (stat.isDirectory()) {
|
|
297
367
|
yield fs_extra_1.default.ensureDir(destPath);
|
|
298
368
|
const entries = yield fs_extra_1.default.readdir(srcPath, { withFileTypes: true });
|
|
299
|
-
|
|
369
|
+
const copyPromises = entries.map(entry => {
|
|
300
370
|
const entrySrcPath = path_1.default.join(srcPath, entry.name);
|
|
301
371
|
const entryDestPath = path_1.default.join(destPath, entry.name);
|
|
302
|
-
|
|
372
|
+
return copyFile(entrySrcPath, entryDestPath);
|
|
373
|
+
});
|
|
374
|
+
const chunks = this.chunkArray(copyPromises, 10);
|
|
375
|
+
for (const chunk of chunks) {
|
|
376
|
+
yield Promise.all(chunk);
|
|
303
377
|
}
|
|
304
378
|
}
|
|
305
379
|
else {
|
|
@@ -320,14 +394,25 @@ class TemplateManager {
|
|
|
320
394
|
}
|
|
321
395
|
});
|
|
322
396
|
const entries = yield fs_extra_1.default.readdir(sourceDir, { withFileTypes: true });
|
|
323
|
-
|
|
397
|
+
const copyPromises = entries.map(entry => {
|
|
324
398
|
const srcPath = path_1.default.join(sourceDir, entry.name);
|
|
325
399
|
const destPath = path_1.default.join(targetDir, entry.name);
|
|
326
|
-
|
|
400
|
+
return copyFile(srcPath, destPath);
|
|
401
|
+
});
|
|
402
|
+
const chunks = this.chunkArray(copyPromises, 5);
|
|
403
|
+
for (const chunk of chunks) {
|
|
404
|
+
yield Promise.all(chunk);
|
|
327
405
|
}
|
|
328
406
|
log.info(`📊 复制统计: ${copiedCount} 个文件复制完成, ${skippedCount} 个文件跳过`);
|
|
329
407
|
});
|
|
330
408
|
}
|
|
409
|
+
chunkArray(array, chunkSize) {
|
|
410
|
+
const chunks = [];
|
|
411
|
+
for (let i = 0; i < array.length; i += chunkSize) {
|
|
412
|
+
chunks.push(array.slice(i, i + chunkSize));
|
|
413
|
+
}
|
|
414
|
+
return chunks;
|
|
415
|
+
}
|
|
331
416
|
getBuiltinTemplates() {
|
|
332
417
|
const templates = {};
|
|
333
418
|
for (const [key, template] of Object.entries(BUILTIN_TEMPLATES)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbase/cli",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.5",
|
|
4
4
|
"description": "cli tool for cloudbase",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"url": "https://github.com/TencentCloudBase/cloud-base-cli.git"
|
|
35
35
|
},
|
|
36
36
|
"bin": {
|
|
37
|
+
"ccr": "bin/ccr.js",
|
|
37
38
|
"cloudbase": "bin/cloudbase.js",
|
|
38
39
|
"cloudbase-mcp": "bin/cloudbase-mcp.cjs",
|
|
39
|
-
"tcb": "bin/tcb.js"
|
|
40
|
-
"ccr": "bin/ccr.js"
|
|
40
|
+
"tcb": "bin/tcb.js"
|
|
41
41
|
},
|
|
42
42
|
"husky": {
|
|
43
43
|
"hooks": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@cloudbase/functions-framework": "1.16.0",
|
|
54
54
|
"@cloudbase/iac-core": "0.0.3-alpha.11",
|
|
55
55
|
"@cloudbase/lowcode-cli": "^0.22.2",
|
|
56
|
-
"@cloudbase/manager-node": "4.
|
|
56
|
+
"@cloudbase/manager-node": "4.6.1",
|
|
57
57
|
"@cloudbase/toolbox": "^0.7.9",
|
|
58
58
|
"@dotenvx/dotenvx": "^1.48.3",
|
|
59
59
|
"@musistudio/claude-code-router": "1.0.36",
|