@cloudbase/cli 2.9.8 → 2.9.10-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cloudbase.js +2 -1
- package/bin/tcb.js +35 -28
- package/cloudbase-cli-2.7.8.tgz +0 -0
- package/lib/auth/login.js +21 -13
- package/lib/commands/account/login.js +43 -42
- package/lib/commands/account/logout.js +3 -2
- package/lib/commands/ai/index.js +13 -12
- package/lib/commands/cloudfunction/base.js +24 -26
- package/lib/commands/cloudrun/base.js +87 -96
- package/lib/commands/common.js +11 -10
- package/lib/commands/config/delete.js +72 -0
- package/lib/commands/config/get.js +69 -0
- package/lib/commands/config/index.js +21 -0
- package/lib/commands/config/interface.js +24 -0
- package/lib/commands/config/list.js +72 -0
- package/lib/commands/config/set.js +89 -0
- package/lib/commands/db/base.js +33 -32
- package/lib/commands/env/base.js +12 -11
- package/lib/commands/env/domain.js +24 -23
- package/lib/commands/env/login.js +31 -30
- package/lib/commands/fun/base.js +37 -36
- package/lib/commands/functions/alias/getRoute.js +5 -4
- package/lib/commands/functions/alias/setRoute.js +7 -6
- package/lib/commands/functions/code-download.js +15 -11
- package/lib/commands/functions/code-update.js +8 -7
- package/lib/commands/functions/concurrency/delete.js +5 -4
- package/lib/commands/functions/concurrency/list.js +6 -5
- package/lib/commands/functions/concurrency/set.js +5 -4
- package/lib/commands/functions/config-update.js +8 -7
- package/lib/commands/functions/copy.js +7 -6
- package/lib/commands/functions/delete.js +8 -7
- package/lib/commands/functions/deploy.js +35 -34
- package/lib/commands/functions/detail.js +32 -31
- package/lib/commands/functions/invoke.js +16 -15
- package/lib/commands/functions/layer/bind.js +26 -25
- package/lib/commands/functions/layer/common.js +2 -1
- package/lib/commands/functions/layer/create.js +7 -6
- package/lib/commands/functions/layer/delete.js +9 -8
- package/lib/commands/functions/layer/download.js +10 -9
- package/lib/commands/functions/layer/list.js +10 -9
- package/lib/commands/functions/layer/sort.js +9 -8
- package/lib/commands/functions/list.js +9 -8
- package/lib/commands/functions/log.js +28 -27
- package/lib/commands/functions/run.js +21 -20
- package/lib/commands/functions/trigger-create.js +9 -8
- package/lib/commands/functions/trigger-delete.js +10 -9
- package/lib/commands/functions/version/list.js +9 -8
- package/lib/commands/functions/version/publish.js +5 -4
- package/lib/commands/gateway/create.js +14 -13
- package/lib/commands/gateway/delete.js +10 -9
- package/lib/commands/gateway/domain.js +25 -24
- package/lib/commands/gateway/list.js +11 -10
- package/lib/commands/gateway/switch.js +22 -19
- package/lib/commands/helpers/init.js +38 -37
- package/lib/commands/helpers/new.js +7 -6
- package/lib/commands/helpers/open.js +7 -6
- package/lib/commands/hosting/hosting.js +45 -44
- package/lib/commands/index.js +1 -0
- package/lib/commands/pull/pull.js +12 -11
- package/lib/commands/run/delete.js +16 -15
- package/lib/commands/run/image/common.js +2 -1
- package/lib/commands/run/image/delete.js +10 -9
- package/lib/commands/run/image/download.js +8 -7
- package/lib/commands/run/image/list.js +11 -10
- package/lib/commands/run/image/upload.js +9 -8
- package/lib/commands/run/list.js +10 -9
- package/lib/commands/run/service/config.js +13 -12
- package/lib/commands/run/service/deploy.js +24 -23
- package/lib/commands/run/service/list.js +10 -9
- package/lib/commands/run/service/update.js +22 -21
- package/lib/commands/run/standalonegateway/common.js +2 -1
- package/lib/commands/run/standalonegateway/create.js +14 -13
- package/lib/commands/run/standalonegateway/destroy.js +9 -8
- package/lib/commands/run/standalonegateway/list.js +7 -6
- package/lib/commands/run/standalonegateway/package.js +7 -6
- package/lib/commands/run/standalonegateway/turn.js +10 -9
- package/lib/commands/run/version/common.js +2 -1
- package/lib/commands/run/version/create.js +41 -40
- package/lib/commands/run/version/delete.js +10 -9
- package/lib/commands/run/version/list.js +13 -12
- package/lib/commands/run/version/modify.js +13 -12
- package/lib/commands/run/version/update.js +57 -54
- package/lib/commands/runf/base.js +42 -40
- package/lib/commands/self-update.js +11 -10
- package/lib/commands/smart.js +5 -4
- package/lib/commands/storage/storage.js +61 -56
- package/lib/commands/third/thirdAttach.js +6 -5
- package/lib/commands/utils.js +8 -5
- package/lib/constant.js +18 -17
- package/lib/decorators/deprecate.js +2 -1
- package/lib/function/alias.js +3 -2
- package/lib/function/base.js +9 -8
- package/lib/function/concurrency.js +4 -3
- package/lib/function/create.js +5 -4
- package/lib/function/delete.js +2 -1
- package/lib/function/layer/attach.js +2 -1
- package/lib/function/layer/create.js +2 -1
- package/lib/function/layer/download.js +2 -1
- package/lib/function/trigger.js +6 -5
- package/lib/function/update.js +3 -2
- package/lib/function/version.js +3 -2
- package/lib/help.js +22 -20
- package/lib/hosting.js +18 -17
- package/lib/i18n/index.js +81 -0
- package/lib/run/service/common.js +14 -13
- package/lib/run/service/config.js +4 -3
- package/lib/run/service/deployPackage.js +6 -5
- package/lib/run/service/showLogs.js +3 -2
- package/lib/run/service/update.js +7 -6
- package/lib/run/standalonegateway/list.js +5 -1
- package/lib/storage.js +2 -1
- package/lib/utils/ai/banner.js +12 -11
- package/lib/utils/ai/claudeWindows.js +5 -3
- package/lib/utils/ai/config.js +3 -2
- package/lib/utils/ai/const.js +4 -3
- package/lib/utils/ai/env.js +3 -2
- package/lib/utils/ai/envLocalManager.js +6 -5
- package/lib/utils/ai/nodeVersion.js +5 -1
- package/lib/utils/ai/router.js +129 -106
- package/lib/utils/ai/setup.js +95 -91
- package/lib/utils/checkTcbrEnv.js +22 -21
- package/lib/utils/cli-table.js +2 -1
- package/lib/utils/commonParamsCheck.js +4 -3
- package/lib/utils/debug-logger.js +2 -1
- package/lib/utils/dts.js +4 -3
- package/lib/utils/env.js +23 -21
- package/lib/utils/fs/index.js +5 -4
- package/lib/utils/function-packer.js +3 -2
- package/lib/utils/index.js +1 -0
- package/lib/utils/mcp-config-modifier.js +18 -17
- package/lib/utils/net/cloud-api-request.js +2 -1
- package/lib/utils/net/credential.js +2 -1
- package/lib/utils/net/http-request.js +2 -1
- package/lib/utils/progress-bar.js +2 -1
- package/lib/utils/prompt/select.js +8 -6
- package/lib/utils/store/auth.js +3 -2
- package/lib/utils/store/config.js +49 -0
- package/lib/utils/store/db.js +5 -0
- package/lib/utils/store/index.js +1 -1
- package/lib/utils/tcbrApi/callTcbrApi.js +2 -1
- package/lib/utils/template-manager.js +37 -36
- package/lib/utils/template.js +10 -9
- package/lib/utils/tools/common.js +3 -2
- package/lib/utils/url.js +19 -0
- package/lib/utils/validator.js +12 -4
- package/locales/README.md +160 -0
- package/locales/i18next-scanner.config.js +206 -0
- package/locales/langs/en.json +1235 -0
- package/locales/langs/zh.json +1235 -0
- package/locales/mapping.json +1235 -0
- package/package.json +10 -3
- package/types/commands/config/delete.d.ts +13 -0
- package/types/commands/config/get.d.ts +13 -0
- package/types/commands/config/index.d.ts +5 -0
- package/types/commands/config/interface.d.ts +17 -0
- package/types/commands/config/list.d.ts +13 -0
- package/types/commands/config/set.d.ts +13 -0
- package/types/commands/index.d.ts +1 -0
- package/types/i18n/index.d.ts +3 -0
- package/types/utils/ai/const.d.ts +1 -1
- package/types/utils/index.d.ts +1 -0
- package/types/utils/store/config.d.ts +8 -0
- package/types/utils/store/db.d.ts +2 -0
- package/types/utils/store/index.d.ts +1 -1
- package/types/utils/url.d.ts +18 -0
- package/dist/standalone/ccr.js +0 -78668
- package/dist/standalone/cli.js +0 -626422
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.callTcbrApi = void 0;
|
|
13
13
|
const toolbox_1 = require("@cloudbase/toolbox");
|
|
14
14
|
const net_1 = require("../net");
|
|
15
|
+
const i18n_1 = require("../../i18n");
|
|
15
16
|
const tcbrService = net_1.CloudApiService.getInstance('tcbr');
|
|
16
17
|
function callTcbrApi(action, data) {
|
|
17
18
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -26,7 +27,7 @@ function callTcbrApi(action, data) {
|
|
|
26
27
|
catch (e) {
|
|
27
28
|
if (e.code === 'AuthFailure.UnauthorizedOperation') {
|
|
28
29
|
console.log('\n', `requestId: ${e.requestId}`);
|
|
29
|
-
throw new toolbox_1.CloudBaseError('您没有权限执行此操作,请检查 CAM 策略\n');
|
|
30
|
+
throw new toolbox_1.CloudBaseError((0, i18n_1.t)('您没有权限执行此操作,请检查 CAM 策略\n'));
|
|
30
31
|
}
|
|
31
32
|
else if (e.code === 'LimitExceeded') {
|
|
32
33
|
throw new toolbox_1.CloudBaseError(`${e.original.Message}\n`);
|
|
@@ -41,26 +41,27 @@ const path_1 = __importDefault(require("path"));
|
|
|
41
41
|
const simple_git_1 = require("simple-git");
|
|
42
42
|
const error_1 = require("../error");
|
|
43
43
|
const mcp_config_modifier_1 = require("./mcp-config-modifier");
|
|
44
|
+
const i18n_1 = require("../i18n");
|
|
44
45
|
const BUILTIN_TEMPLATES = {
|
|
45
46
|
miniprogram: {
|
|
46
47
|
url: 'https://static.cloudbase.net/cloudbase-examples/miniprogram-cloudbase-miniprogram-template.zip',
|
|
47
|
-
name: '微信小程序 + CloudBase
|
|
48
|
+
name: `${(0, i18n_1.t)('微信小程序')} + CloudBase`
|
|
48
49
|
},
|
|
49
50
|
react: {
|
|
50
51
|
url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-react-template.zip',
|
|
51
|
-
name: 'Web 应用 - React + CloudBase
|
|
52
|
+
name: `${(0, i18n_1.t)('Web 应用')} - React + CloudBase`
|
|
52
53
|
},
|
|
53
54
|
vue: {
|
|
54
55
|
url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-vue-template.zip',
|
|
55
|
-
name: 'Web 应用 - Vue + CloudBase
|
|
56
|
+
name: `${(0, i18n_1.t)('Web 应用')} - Vue + CloudBase`
|
|
56
57
|
},
|
|
57
58
|
uniapp: {
|
|
58
59
|
url: 'https://static.cloudbase.net/cloudbase-examples/universal-cloudbase-uniapp-template.zip',
|
|
59
|
-
name: '跨端应用 - UniApp + CloudBase
|
|
60
|
+
name: `${(0, i18n_1.t)('跨端应用')} - UniApp + CloudBase`
|
|
60
61
|
},
|
|
61
62
|
rules: {
|
|
62
63
|
url: 'https://static.cloudbase.net/cloudbase-examples/web-cloudbase-project.zip',
|
|
63
|
-
name: 'AI 规则和配置'
|
|
64
|
+
name: `${(0, i18n_1.t)('AI 规则和配置')}`
|
|
64
65
|
}
|
|
65
66
|
};
|
|
66
67
|
class TemplateManager {
|
|
@@ -86,10 +87,10 @@ class TemplateManager {
|
|
|
86
87
|
yield this.downloadGitTemplateToTemp(gitInfo, tempDir, log);
|
|
87
88
|
}
|
|
88
89
|
else {
|
|
89
|
-
throw new error_1.CloudBaseError(
|
|
90
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('不支持的模板来源: {{source}}', { source: typeof source === 'string' ? source : JSON.stringify(source) }));
|
|
90
91
|
}
|
|
91
92
|
yield this.copyFromTempToTarget(tempDir, targetPath, force, log);
|
|
92
|
-
log.info(
|
|
93
|
+
log.info((0, i18n_1.t)('✅ 模板拉取完成: {{targetPath}}', { targetPath }));
|
|
93
94
|
}
|
|
94
95
|
finally {
|
|
95
96
|
yield fs_extra_1.default.remove(tempDir);
|
|
@@ -100,7 +101,7 @@ class TemplateManager {
|
|
|
100
101
|
return Object.keys(BUILTIN_TEMPLATES).includes(source);
|
|
101
102
|
}
|
|
102
103
|
isZipUrl(source) {
|
|
103
|
-
console.log(
|
|
104
|
+
console.log('zip source', source);
|
|
104
105
|
if (!source || typeof source !== 'string') {
|
|
105
106
|
return false;
|
|
106
107
|
}
|
|
@@ -179,7 +180,7 @@ class TemplateManager {
|
|
|
179
180
|
branch: 'main'
|
|
180
181
|
};
|
|
181
182
|
}
|
|
182
|
-
throw new error_1.CloudBaseError(
|
|
183
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('无法解析 Git URL: {{url}}', { url }));
|
|
183
184
|
}
|
|
184
185
|
buildGitUrl(gitInfo) {
|
|
185
186
|
if (gitInfo.platform === 'github') {
|
|
@@ -191,7 +192,7 @@ class TemplateManager {
|
|
|
191
192
|
else if (gitInfo.platform === 'cnb') {
|
|
192
193
|
return `https://cnb.cool/${gitInfo.owner}/${gitInfo.repo}.git`;
|
|
193
194
|
}
|
|
194
|
-
throw new error_1.CloudBaseError(
|
|
195
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('不支持的 Git 平台: {{platform}}', { platform: gitInfo.platform }));
|
|
195
196
|
}
|
|
196
197
|
cloneWithSubpathOptimized(gitUrl, tempDir, gitInfo, log) {
|
|
197
198
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -204,7 +205,7 @@ class TemplateManager {
|
|
|
204
205
|
]);
|
|
205
206
|
const subpathFull = path_1.default.join(tempDir, gitInfo.subpath);
|
|
206
207
|
if (!(yield fs_extra_1.default.pathExists(subpathFull))) {
|
|
207
|
-
throw new error_1.CloudBaseError(
|
|
208
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('子目录不存在: {{subpath}}', { subpath: gitInfo.subpath }));
|
|
208
209
|
}
|
|
209
210
|
const tempContentDir = path_1.default.join(tempDir, '.temp-content-' + Date.now());
|
|
210
211
|
yield fs_extra_1.default.move(subpathFull, tempContentDir);
|
|
@@ -221,7 +222,7 @@ class TemplateManager {
|
|
|
221
222
|
yield fs_extra_1.default.remove(tempContentDir);
|
|
222
223
|
}
|
|
223
224
|
catch (error) {
|
|
224
|
-
throw new error_1.CloudBaseError(
|
|
225
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('克隆子目录失败: {{error}}', { error: error.message }), { original: error });
|
|
225
226
|
}
|
|
226
227
|
});
|
|
227
228
|
}
|
|
@@ -233,7 +234,7 @@ class TemplateManager {
|
|
|
233
234
|
yield tempGit.checkout(gitInfo.branch);
|
|
234
235
|
const subpathFull = path_1.default.join(tempDir, gitInfo.subpath);
|
|
235
236
|
if (!(yield fs_extra_1.default.pathExists(subpathFull))) {
|
|
236
|
-
throw new error_1.CloudBaseError(
|
|
237
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('子目录不存在: {{subpath}}', { subpath: gitInfo.subpath }));
|
|
237
238
|
}
|
|
238
239
|
const tempContentDir = path_1.default.join(tempDir, '.temp-content-' + Date.now());
|
|
239
240
|
yield fs_extra_1.default.move(subpathFull, tempContentDir);
|
|
@@ -250,7 +251,7 @@ class TemplateManager {
|
|
|
250
251
|
yield fs_extra_1.default.remove(tempContentDir);
|
|
251
252
|
}
|
|
252
253
|
catch (error) {
|
|
253
|
-
throw new error_1.CloudBaseError(
|
|
254
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('克隆子目录失败: {{error}}', { error: error.message }), { original: error });
|
|
254
255
|
}
|
|
255
256
|
});
|
|
256
257
|
}
|
|
@@ -258,36 +259,36 @@ class TemplateManager {
|
|
|
258
259
|
return __awaiter(this, void 0, void 0, function* () {
|
|
259
260
|
const template = BUILTIN_TEMPLATES[templateId];
|
|
260
261
|
if (!template) {
|
|
261
|
-
throw new error_1.CloudBaseError(
|
|
262
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('未知的内置模板: {{templateId}}', { templateId }));
|
|
262
263
|
}
|
|
263
|
-
log.info(
|
|
264
|
+
log.info((0, i18n_1.t)('📦 正在下载 {{templateName}} 模板到临时目录...', { templateName: template.name }));
|
|
264
265
|
try {
|
|
265
266
|
const { downloadAndExtractRemoteZip } = yield Promise.resolve().then(() => __importStar(require('./tools/common')));
|
|
266
267
|
yield downloadAndExtractRemoteZip(template.url, tempDir);
|
|
267
|
-
log.info(
|
|
268
|
+
log.info((0, i18n_1.t)('✅ {{templateName}} 模板下载完成到临时目录', { templateName: template.name }));
|
|
268
269
|
}
|
|
269
270
|
catch (error) {
|
|
270
|
-
throw new error_1.CloudBaseError(
|
|
271
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('下载内置模板失败: {{error}}', { error: error.message }), { original: error });
|
|
271
272
|
}
|
|
272
273
|
});
|
|
273
274
|
}
|
|
274
275
|
downloadZipTemplateToTemp(zipUrl, tempDir, log) {
|
|
275
276
|
return __awaiter(this, void 0, void 0, function* () {
|
|
276
|
-
log.info(
|
|
277
|
+
log.info((0, i18n_1.t)('📦 正在下载 zip 文件模板到临时目录: {{zipUrl}}', { zipUrl }));
|
|
277
278
|
try {
|
|
278
279
|
const { downloadAndExtractRemoteZip } = yield Promise.resolve().then(() => __importStar(require('./tools/common')));
|
|
279
280
|
yield downloadAndExtractRemoteZip(zipUrl, tempDir);
|
|
280
|
-
log.info(
|
|
281
|
+
log.info((0, i18n_1.t)('✅ zip 文件模板下载完成到临时目录'));
|
|
281
282
|
}
|
|
282
283
|
catch (error) {
|
|
283
|
-
throw new error_1.CloudBaseError(
|
|
284
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('下载 zip 文件模板失败: {{error}}', { error: error.message }), { original: error });
|
|
284
285
|
}
|
|
285
286
|
});
|
|
286
287
|
}
|
|
287
288
|
downloadGitTemplateToTemp(gitInfo, tempDir, log) {
|
|
288
289
|
return __awaiter(this, void 0, void 0, function* () {
|
|
289
290
|
const gitUrl = this.buildGitUrl(gitInfo);
|
|
290
|
-
log.info(
|
|
291
|
+
log.info((0, i18n_1.t)('📦 正在从 {{platform}} 下载模板到临时目录...', { platform: gitInfo.platform }));
|
|
291
292
|
try {
|
|
292
293
|
if (gitInfo.subpath) {
|
|
293
294
|
yield this.cloneWithSubpathOptimized(gitUrl, tempDir, gitInfo, log);
|
|
@@ -295,22 +296,22 @@ class TemplateManager {
|
|
|
295
296
|
else {
|
|
296
297
|
yield this.git.clone(gitUrl, tempDir, ['--depth', '1', '--single-branch', '--branch', gitInfo.branch]);
|
|
297
298
|
}
|
|
298
|
-
log.info(
|
|
299
|
+
log.info((0, i18n_1.t)('✅ Git 模板下载完成到临时目录'));
|
|
299
300
|
}
|
|
300
301
|
catch (error) {
|
|
301
|
-
throw new error_1.CloudBaseError(
|
|
302
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('Git 模板下载失败: {{error}}', { error: error.message }), { original: error });
|
|
302
303
|
}
|
|
303
304
|
});
|
|
304
305
|
}
|
|
305
306
|
copyFromTempToTarget(tempDir, targetPath, force, log) {
|
|
306
307
|
return __awaiter(this, void 0, void 0, function* () {
|
|
307
|
-
log.info(
|
|
308
|
+
log.info((0, i18n_1.t)('📦 正在从临时目录复制到目标目录 {{targetPath}}...', { targetPath }));
|
|
308
309
|
try {
|
|
309
310
|
if (force) {
|
|
310
311
|
if (yield fs_extra_1.default.pathExists(targetPath)) {
|
|
311
312
|
const files = yield fs_extra_1.default.readdir(targetPath);
|
|
312
313
|
if (files.length > 0) {
|
|
313
|
-
log.info(
|
|
314
|
+
log.info((0, i18n_1.t)('⚠️ 使用 --force 参数,将覆盖目标目录中的现有文件'));
|
|
314
315
|
}
|
|
315
316
|
}
|
|
316
317
|
yield this.copyFilesWithOverwrite(tempDir, targetPath, log);
|
|
@@ -319,23 +320,23 @@ class TemplateManager {
|
|
|
319
320
|
if (yield fs_extra_1.default.pathExists(targetPath)) {
|
|
320
321
|
const files = yield fs_extra_1.default.readdir(targetPath);
|
|
321
322
|
if (files.length > 0) {
|
|
322
|
-
log.info(
|
|
323
|
+
log.info((0, i18n_1.t)('ℹ️ 目标目录不为空,将跳过已存在的文件,只复制新文件'));
|
|
323
324
|
}
|
|
324
325
|
}
|
|
325
326
|
yield this.copyFilesSkipExisting(tempDir, targetPath, log);
|
|
326
327
|
}
|
|
327
|
-
log.info(
|
|
328
|
+
log.info((0, i18n_1.t)('✅ 模板复制完成'));
|
|
328
329
|
try {
|
|
329
|
-
log.debug('🔧 开始调用 MCP 配置修改器...');
|
|
330
|
+
log.debug((0, i18n_1.t)('🔧 开始调用 MCP 配置修改器...'));
|
|
330
331
|
yield this.mcpConfigModifier.modifyMCPConfigs(targetPath, log);
|
|
331
|
-
log.debug('✅ MCP 配置修改器调用完成');
|
|
332
|
+
log.debug((0, i18n_1.t)('✅ MCP 配置修改器调用完成'));
|
|
332
333
|
}
|
|
333
334
|
catch (error) {
|
|
334
|
-
log.warn(
|
|
335
|
+
log.warn((0, i18n_1.t)('⚠️ MCP 配置修改失败: {{error}}', { error: error.message }));
|
|
335
336
|
}
|
|
336
337
|
}
|
|
337
338
|
catch (error) {
|
|
338
|
-
throw new error_1.CloudBaseError(
|
|
339
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('复制模板失败: {{error}}', { error: error.message }), { original: error });
|
|
339
340
|
}
|
|
340
341
|
});
|
|
341
342
|
}
|
|
@@ -373,7 +374,7 @@ class TemplateManager {
|
|
|
373
374
|
}
|
|
374
375
|
}
|
|
375
376
|
catch (error) {
|
|
376
|
-
log.warn(
|
|
377
|
+
log.warn((0, i18n_1.t)('⚠️ 复制失败: {{path}} - {{error}}', { path: path_1.default.relative(sourceDir, srcPath), error: error.message }));
|
|
377
378
|
}
|
|
378
379
|
});
|
|
379
380
|
const entries = yield fs_extra_1.default.readdir(sourceDir, { withFileTypes: true });
|
|
@@ -386,7 +387,7 @@ class TemplateManager {
|
|
|
386
387
|
for (const chunk of chunks) {
|
|
387
388
|
yield Promise.all(chunk);
|
|
388
389
|
}
|
|
389
|
-
log.info(
|
|
390
|
+
log.info((0, i18n_1.t)('📊 复制统计: {{copiedCount}} 个新文件复制, {{overwrittenCount}} 个文件覆盖', { copiedCount, overwrittenCount }));
|
|
390
391
|
});
|
|
391
392
|
}
|
|
392
393
|
copyFilesSkipExisting(sourceDir, targetDir, log) {
|
|
@@ -423,7 +424,7 @@ class TemplateManager {
|
|
|
423
424
|
}
|
|
424
425
|
}
|
|
425
426
|
catch (error) {
|
|
426
|
-
log.warn(
|
|
427
|
+
log.warn((0, i18n_1.t)('⚠️ 复制失败: {{path}} - {{error}}', { path: path_1.default.relative(sourceDir, srcPath), error: error.message }));
|
|
427
428
|
}
|
|
428
429
|
});
|
|
429
430
|
const entries = yield fs_extra_1.default.readdir(sourceDir, { withFileTypes: true });
|
|
@@ -436,7 +437,7 @@ class TemplateManager {
|
|
|
436
437
|
for (const chunk of chunks) {
|
|
437
438
|
yield Promise.all(chunk);
|
|
438
439
|
}
|
|
439
|
-
log.info(
|
|
440
|
+
log.info((0, i18n_1.t)('📊 复制统计: {{copiedCount}} 个文件复制完成, {{skippedCount}} 个文件跳过', { copiedCount, skippedCount }));
|
|
440
441
|
});
|
|
441
442
|
}
|
|
442
443
|
chunkArray(array, chunkSize) {
|
package/lib/utils/template.js
CHANGED
|
@@ -24,13 +24,14 @@ const log_1 = require("./log");
|
|
|
24
24
|
const fs_2 = require("./fs");
|
|
25
25
|
const output_1 = require("./output");
|
|
26
26
|
const reporter_1 = require("./reporter");
|
|
27
|
+
const i18n_1 = require("../i18n");
|
|
27
28
|
const listUrl = 'https://tcli.service.tcloudbase.com/templates';
|
|
28
29
|
const getTemplateAddress = (templatePath) => `https://7463-tcli-1258016615.tcb.qcloud.la/cloudbase-templates/${templatePath}.zip`;
|
|
29
30
|
function downloadTemplate(options = {}) {
|
|
30
31
|
return __awaiter(this, void 0, void 0, function* () {
|
|
31
32
|
let { templateUri, appName, newProject, projectPath = process.cwd() } = options;
|
|
32
33
|
const templates = yield (0, output_1.execWithLoading)(() => (0, net_1.fetch)(listUrl), {
|
|
33
|
-
startTip: '获取应用模板列表中'
|
|
34
|
+
startTip: (0, i18n_1.t)('获取应用模板列表中')
|
|
34
35
|
});
|
|
35
36
|
let templateName;
|
|
36
37
|
let tempateId;
|
|
@@ -41,7 +42,7 @@ function downloadTemplate(options = {}) {
|
|
|
41
42
|
const { selectTemplateName } = yield (0, enquirer_1.prompt)({
|
|
42
43
|
type: 'select',
|
|
43
44
|
name: 'selectTemplateName',
|
|
44
|
-
message: '请选择应用模板',
|
|
45
|
+
message: (0, i18n_1.t)('请选择应用模板'),
|
|
45
46
|
choices: templates.map((item) => item.name)
|
|
46
47
|
});
|
|
47
48
|
templateName = selectTemplateName;
|
|
@@ -50,7 +51,7 @@ function downloadTemplate(options = {}) {
|
|
|
50
51
|
? templates.find((item) => item.name === templateName)
|
|
51
52
|
: templates.find((item) => item.path === tempateId);
|
|
52
53
|
if (!selectedTemplate) {
|
|
53
|
-
log_1.logger.info(
|
|
54
|
+
log_1.logger.info((0, i18n_1.t)('模板 `{{template}}` 不存在', { template: templateName || tempateId }));
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
56
57
|
if (newProject) {
|
|
@@ -58,7 +59,7 @@ function downloadTemplate(options = {}) {
|
|
|
58
59
|
const { projectName } = yield (0, enquirer_1.prompt)({
|
|
59
60
|
type: 'input',
|
|
60
61
|
name: 'projectName',
|
|
61
|
-
message: '请输入项目名称',
|
|
62
|
+
message: (0, i18n_1.t)('请输入项目名称'),
|
|
62
63
|
initial: selectedTemplate.path
|
|
63
64
|
});
|
|
64
65
|
appName = projectName;
|
|
@@ -68,11 +69,11 @@ function downloadTemplate(options = {}) {
|
|
|
68
69
|
const { cover } = yield (0, enquirer_1.prompt)({
|
|
69
70
|
type: 'confirm',
|
|
70
71
|
name: 'cover',
|
|
71
|
-
message:
|
|
72
|
+
message: (0, i18n_1.t)('已存在同名文件夹:{{name}},是否覆盖?', { name: appName }),
|
|
72
73
|
initial: false
|
|
73
74
|
});
|
|
74
75
|
if (!cover) {
|
|
75
|
-
throw new error_1.CloudBaseError('操作终止!');
|
|
76
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('操作终止!'));
|
|
76
77
|
}
|
|
77
78
|
else {
|
|
78
79
|
fs_extra_1.default.removeSync(projectPath);
|
|
@@ -83,7 +84,7 @@ function downloadTemplate(options = {}) {
|
|
|
83
84
|
yield (0, reporter_1.templateDownloadReport)(selectedTemplate.path, selectedTemplate.name);
|
|
84
85
|
yield extractTemplate(projectPath, selectedTemplate.path, selectedTemplate.url);
|
|
85
86
|
}), {
|
|
86
|
-
startTip: '下载文件中'
|
|
87
|
+
startTip: (0, i18n_1.t)('下载文件中')
|
|
87
88
|
});
|
|
88
89
|
return projectPath;
|
|
89
90
|
});
|
|
@@ -94,10 +95,10 @@ function extractTemplate(projectPath, templatePath, remoteUrl) {
|
|
|
94
95
|
const url = remoteUrl || getTemplateAddress(templatePath);
|
|
95
96
|
return (0, net_1.fetchStream)(url).then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
96
97
|
if (!res) {
|
|
97
|
-
throw new error_1.CloudBaseError('请求异常');
|
|
98
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('请求异常'));
|
|
98
99
|
}
|
|
99
100
|
if (res.status !== 200) {
|
|
100
|
-
throw new error_1.CloudBaseError('未找到文件');
|
|
101
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('未找到文件'));
|
|
101
102
|
}
|
|
102
103
|
yield (0, toolbox_1.unzipStream)(res.body, projectPath);
|
|
103
104
|
}));
|
|
@@ -17,14 +17,15 @@ const toolbox_1 = require("@cloudbase/toolbox");
|
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
19
19
|
const unzipper_1 = __importDefault(require("unzipper"));
|
|
20
|
+
const i18n_1 = require("../../i18n");
|
|
20
21
|
function downloadAndExtractRemoteZip(downloadUrl, targetPath) {
|
|
21
22
|
return __awaiter(this, void 0, void 0, function* () {
|
|
22
23
|
const downloadResponse = yield (0, toolbox_1.fetchStream)(downloadUrl);
|
|
23
24
|
if (!downloadResponse.ok) {
|
|
24
|
-
throw new Error(
|
|
25
|
+
throw new Error((0, i18n_1.t)('下载失败,状态码: {{status}}', { status: downloadResponse.status }));
|
|
25
26
|
}
|
|
26
27
|
if (!downloadResponse.body) {
|
|
27
|
-
throw new Error('下载响应中没有数据流');
|
|
28
|
+
throw new Error((0, i18n_1.t)('下载响应中没有数据流'));
|
|
28
29
|
}
|
|
29
30
|
const dirPath = path_1.default.resolve(targetPath);
|
|
30
31
|
yield fs_extra_1.default.ensureDir(dirPath);
|
package/lib/utils/url.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getGatewayUrl = exports.EUrl = exports.EDomain = void 0;
|
|
4
|
+
const i18n_1 = require("../i18n");
|
|
5
|
+
exports.EDomain = {
|
|
6
|
+
Console: (0, i18n_1.isIntl)() ? 'console.tencentcloud.com' : 'console.cloud.tencent.com',
|
|
7
|
+
Official: (0, i18n_1.isIntl)() ? 'www.tencentcloud.com' : 'cloud.tencent.com',
|
|
8
|
+
Gateway: (0, i18n_1.isIntl)() ? 'api.intl.tcloudbasegateway.com' : 'api.tcloudbasegateway.com',
|
|
9
|
+
Tcb: (0, i18n_1.isIntl)() ? 'tcb.tencentcloud.com' : 'tcb.cloud.tencent.com',
|
|
10
|
+
Docs: 'docs.cloudbase.net'
|
|
11
|
+
};
|
|
12
|
+
exports.EUrl = Object.assign(Object.assign({}, ((domains) => Object.keys(domains).reduce((acc, key) => {
|
|
13
|
+
acc[key] = `https://${domains[key]}`;
|
|
14
|
+
return acc;
|
|
15
|
+
}, {}))(exports.EDomain)), { Docs: `https://${exports.EDomain.Docs}` + ((0, i18n_1.isIntl)() ? '/en' : ''), DevPlatform: `https://${exports.EDomain.Tcb}/dev`, TcbConsole: `https://${exports.EDomain.Console}/tcb`, CloudAdmin: `https://${exports.EDomain.Tcb}/cloud-admin` });
|
|
16
|
+
function getGatewayUrl(envId) {
|
|
17
|
+
return `https://${envId}.${exports.EDomain.Gateway}`;
|
|
18
|
+
}
|
|
19
|
+
exports.getGatewayUrl = getGatewayUrl;
|
package/lib/utils/validator.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.validateCpuMem = exports.validateIp = exports.assertHas = exports.assert
|
|
|
4
4
|
const error_1 = require("../error");
|
|
5
5
|
const constant_1 = require("../constant");
|
|
6
6
|
const run_1 = require("../run");
|
|
7
|
+
const i18n_1 = require("../i18n");
|
|
7
8
|
function assertTruthy(val, errMsg) {
|
|
8
9
|
let ok;
|
|
9
10
|
if (Array.isArray(val)) {
|
|
@@ -39,8 +40,11 @@ const validateCpuMem = (cpuInput, memInput) => {
|
|
|
39
40
|
let memSet = (0, run_1.convertNumber)(memInput);
|
|
40
41
|
let validMemSet = constant_1.CPU_MEM_OPTS.find(({ cpu }) => cpu === cpuSet);
|
|
41
42
|
if (!validMemSet || !validMemSet.mems.length || !validMemSet.mems.includes(memSet)) {
|
|
42
|
-
throw new error_1.CloudBaseError(
|
|
43
|
-
|
|
43
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('cpu 与 mem 规格不匹配,当前规格:cpu: {{cpu}}, mem: {{mem}}\n请使用下列规格组合之一:{{options}}', {
|
|
44
|
+
cpu: cpuInput,
|
|
45
|
+
mem: memInput,
|
|
46
|
+
options: constant_1.CPU_MEM_OPTS.map(({ cpu, mems }) => `${cpu}-${mems.join('/')}`).join(',')
|
|
47
|
+
}));
|
|
44
48
|
}
|
|
45
49
|
return { cpuOutput: cpuSet, memOutput: memSet };
|
|
46
50
|
}
|
|
@@ -48,7 +52,9 @@ const validateCpuMem = (cpuInput, memInput) => {
|
|
|
48
52
|
let cpuSet = (0, run_1.convertNumber)(cpuInput);
|
|
49
53
|
let validSet = constant_1.CPU_MEM_OPTS.find(({ cpu }) => cpu === cpuSet);
|
|
50
54
|
if (!validSet) {
|
|
51
|
-
throw new error_1.CloudBaseError(
|
|
55
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('不支持当前 cpu 规格,请使用下列 cpu 规格之一:{{options}}', {
|
|
56
|
+
options: constant_1.CPU_MEM_OPTS.map(({ cpu }) => cpu).join(',')
|
|
57
|
+
}));
|
|
52
58
|
}
|
|
53
59
|
return { cpuOutput: cpuSet, memOutput: constant_1.CPU_MEM_OPTS.find(({ cpu }) => cpu === cpuSet).mems[0] };
|
|
54
60
|
}
|
|
@@ -56,7 +62,9 @@ const validateCpuMem = (cpuInput, memInput) => {
|
|
|
56
62
|
let memSet = (0, run_1.convertNumber)(memInput);
|
|
57
63
|
let validSet = constant_1.CPU_MEM_OPTS.find(({ mems }) => mems.includes(memSet));
|
|
58
64
|
if (!validSet) {
|
|
59
|
-
throw new error_1.CloudBaseError(
|
|
65
|
+
throw new error_1.CloudBaseError((0, i18n_1.t)('不支持当前 mem 规格,请使用下列 mem 规格之一:{{options}}', {
|
|
66
|
+
options: constant_1.CPU_MEM_OPTS.map(({ mems }) => mems.join('/')).join(',')
|
|
67
|
+
}));
|
|
60
68
|
}
|
|
61
69
|
return { cpuOutput: validSet.cpu, memOutput: memSet };
|
|
62
70
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# 国际化功能使用说明
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
本项目使用 i18next 实现国际化功能,支持中文和英文两种语言。
|
|
6
|
+
|
|
7
|
+
## 使用方法
|
|
8
|
+
|
|
9
|
+
### 1. 在代码中使用
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { t } from 'src/i18n'
|
|
13
|
+
|
|
14
|
+
// 使用 t() 函数包装需要国际化的文本
|
|
15
|
+
console.log(t('你好')) // 你好
|
|
16
|
+
// 插值
|
|
17
|
+
console.log(t('你好, {{name}}', { name: 'TCB' })) // 你好, TCB
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 2. 扫描和生成标识
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm run i18n:scan
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 工作流程
|
|
27
|
+
|
|
28
|
+
1. **开发阶段**:在代码中使用 `t('文本内容')` 包装需要国际化的文本
|
|
29
|
+
2. **扫描阶段**:运行 `npm run i18n:scan` 扫描代码并提取文本
|
|
30
|
+
3. **生成阶段**:自动为每个文本生成唯一的标识(格式:`k_${hash}`)
|
|
31
|
+
4. **增量管理**:在 `locales/inc/` 目录下生成增量词条文件
|
|
32
|
+
5. **翻译阶段**:开发者在 `inc/en.json` 中填写英文翻译
|
|
33
|
+
6. **同步阶段**:运行 `npm run i18n:sync` 将翻译同步到正式语言文件,并清理增量目录(可选)
|
|
34
|
+
|
|
35
|
+
## 文件结构
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
locales/
|
|
39
|
+
├── scripts/ # 脚本目录
|
|
40
|
+
│ ├── generate.js # 标识生成脚本
|
|
41
|
+
│ └── sync.js # 翻译同步脚本
|
|
42
|
+
├── inc/ # 增量词条目录(临时)
|
|
43
|
+
│ ├── zh.json # 增量中文词条
|
|
44
|
+
│ └── en.json # 增量英文翻译
|
|
45
|
+
├── langs/ # 正式语言文件
|
|
46
|
+
│ ├── zh.json # 中文语言文件
|
|
47
|
+
│ └── en.json # 英文语言文件
|
|
48
|
+
├── i18next-scanner.config.js # 扫描配置
|
|
49
|
+
├── mapping.json # 文本到标识的映射文件
|
|
50
|
+
└── README.md # 本说明文档
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 标识生成规则
|
|
54
|
+
|
|
55
|
+
- 标识格式:`k_${hash}`
|
|
56
|
+
- 相同文本生成相同标识
|
|
57
|
+
- 使用简单的字符串哈希算法
|
|
58
|
+
- 支持增量更新,只处理新增文本
|
|
59
|
+
|
|
60
|
+
## 核心特性
|
|
61
|
+
|
|
62
|
+
- 已有英文翻译不会被覆盖
|
|
63
|
+
- 自动识别和保留现有翻译
|
|
64
|
+
- 增量词条单独放在 `inc` 目录下
|
|
65
|
+
- 与正式语言文件分离,便于管理
|
|
66
|
+
- 自动化同步增量词条
|
|
67
|
+
- 只处理新增或修改的文本
|
|
68
|
+
- 自动移除不再使用的词条
|
|
69
|
+
|
|
70
|
+
## 命令说明
|
|
71
|
+
|
|
72
|
+
- **`npm run i18n:scan`**:完整的国际化处理流程
|
|
73
|
+
- 扫描代码中的 `t()` 函数调用
|
|
74
|
+
- 提取需要国际化的文本
|
|
75
|
+
- 生成唯一标识
|
|
76
|
+
- 更新语言文件
|
|
77
|
+
- 生成增量词条文件
|
|
78
|
+
|
|
79
|
+
- **`npm run i18n:sync`**:同步增量翻译
|
|
80
|
+
- 将 `inc/en.json` 中的翻译同步到正式语言文件
|
|
81
|
+
- 只同步有效的翻译(跳过 `__STRING_NOT_TRANSLATED__`)
|
|
82
|
+
- 自动更新 `langs/en.json`
|
|
83
|
+
|
|
84
|
+
## 增量词条翻译流程
|
|
85
|
+
|
|
86
|
+
### 步骤1:生成增量文件
|
|
87
|
+
```bash
|
|
88
|
+
npm run i18n
|
|
89
|
+
```
|
|
90
|
+
- 扫描代码,生成增量词条
|
|
91
|
+
- 在 `locales/inc/` 目录下创建文件
|
|
92
|
+
|
|
93
|
+
### 步骤2:编辑翻译
|
|
94
|
+
编辑 `locales/inc/en.json` 文件:
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"k_4293de80": "Test Text 001",
|
|
98
|
+
"k_62b2fe13": "Welcome to CloudBase CLI",
|
|
99
|
+
"k_28810860": "Hehe Haha",
|
|
100
|
+
"k_29c6c120": "Available configurations:"
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 步骤3:同步翻译
|
|
105
|
+
```bash
|
|
106
|
+
npm run i18n:sync
|
|
107
|
+
```
|
|
108
|
+
- 将翻译同步到正式语言文件
|
|
109
|
+
- 显示同步进度和结果
|
|
110
|
+
|
|
111
|
+
## 注意事项
|
|
112
|
+
|
|
113
|
+
1. 确保在代码中使用 `t()` 函数包装需要国际化的文本
|
|
114
|
+
2. 运行 `npm run i18n:scan` 后会自动生成增量词条文件
|
|
115
|
+
3. 在 `inc/en.json` 中填写英文翻译
|
|
116
|
+
4. 翻译完成后运行 `npm run i18n:sync` 同步到正式文件
|
|
117
|
+
5. 同步完成后 `inc` 目录会自动清理,无需手动删除
|
|
118
|
+
6. 语言文件中的 key 会自动更新为生成的标识
|
|
119
|
+
|
|
120
|
+
## 示例
|
|
121
|
+
|
|
122
|
+
### 源码
|
|
123
|
+
```typescript
|
|
124
|
+
// 代码中
|
|
125
|
+
console.log(t('欢迎使用云开发 CLI'))
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 扫描后
|
|
129
|
+
```json
|
|
130
|
+
// mapping.json
|
|
131
|
+
{
|
|
132
|
+
"欢迎使用云开发 CLI": "k_62b2fe13"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// inc/zh.json
|
|
136
|
+
{
|
|
137
|
+
"k_62b2fe13": "欢迎使用云开发 CLI"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// inc/en.json
|
|
141
|
+
{
|
|
142
|
+
"k_62b2fe13": "__STRING_NOT_TRANSLATED__"
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 翻译后
|
|
147
|
+
```json
|
|
148
|
+
// inc/en.json
|
|
149
|
+
{
|
|
150
|
+
"k_62b2fe13": "Welcome to CloudBase CLI"
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 同步后
|
|
155
|
+
```json
|
|
156
|
+
// langs/en.json
|
|
157
|
+
{
|
|
158
|
+
"k_62b2fe13": "Welcome to CloudBase CLI"
|
|
159
|
+
}
|
|
160
|
+
```
|