@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.
@@ -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
- let layers = yield (0, function_1.listLayers)({
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
- layers = layers.map((item) => ({
71
+ const newLayers = layers.map((item) => ({
72
72
  name: `[${LayerStatusMap[item.Status] || '异常'}] ${item.LayerName}`,
73
73
  value: item.LayerName
74
74
  }));
75
- if (!layers.length) {
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: layers,
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 versions = yield (0, function_1.listLayerVersions)({
88
+ let listLayerVersionsRes = yield functionService.listLayerVersions({
89
89
  name: layer
90
90
  });
91
- versions = versions.map((item) => String(item.LayerVersion));
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 (0, function_1.attachLayer)({
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 (0, function_1.unAttachLayer)({
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 layers = yield (0, function_1.listLayers)({
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 (0, function_1.createLayer)({
66
- envId,
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 layers = yield (0, function_1.listLayers)({
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 versions = yield (0, function_1.listLayerVersions)({
61
+ let listLayerVersionsRes = yield functionService.listLayerVersions({
61
62
  name: layer
62
63
  });
63
- versions = versions.map((item) => String(item.LayerVersion));
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 (0, function_1.deleteLayer)({
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 layers = yield (0, function_1.listLayers)({
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 versions = yield (0, function_1.listLayerVersions)({
71
+ let listLayerVersionsRes = yield functionService.listLayerVersions({
71
72
  name: layer
72
73
  });
73
- versions = versions.map((item) => String(item.LayerVersion));
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 (0, function_1.downloadLayer)({
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
- data = yield (0, function_1.listLayerVersions)({
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
- data = yield (0, function_1.listLayers)({
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 (0, function_1.sortLayer)({
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 (0, function_1.batchCreateTriggers)({
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 (0, function_1.createFunctionTriggers)({
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 (0, function_1.batchDeleteTriggers)({
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 (0, function_1.batchDeleteTriggers)({
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 (0, function_1.deleteFunctionTrigger)({
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://github.com/user/repo/tree/main/examples --output ./my-project',
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');
@@ -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';
@@ -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.CLAUDE, const_1.CODEBUDDY, const_1.QWEN, const_1.CODEX, const_1.CURSOR, const_1.AIDER, ...(includeNone ? [const_1.NONE] : [])],
198
- default: const_1.CLAUDE.value
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: 'Claude Auth Token:',
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: 'Qwen API Key:',
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: '请输入 CodeBuddy API Key:',
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', {
@@ -22,6 +22,7 @@ const getArgs = () => {
22
22
  .help(false)
23
23
  .alias('e', 'envId')
24
24
  .alias('r', 'region')
25
+ .alias('v', 'version')
25
26
  .argv;
26
27
  };
27
28
  exports.getArgs = getArgs;
@@ -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@') || source.includes('github.com') || source.includes('gitee.com');
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.cloneWithSubpath(gitUrl, tempDir, gitInfo, log);
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
- for (const entry of entries) {
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
- yield copyFile(entrySrcPath, entryDestPath);
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
- for (const entry of entries) {
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
- yield copyFile(srcPath, destPath);
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
- for (const entry of entries) {
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
- yield copyFile(entrySrcPath, entryDestPath);
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
- for (const entry of entries) {
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
- yield copyFile(srcPath, destPath);
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",
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.5.0",
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",