@cloudbase/cloudbase-mcp 2.24.0 → 2.25.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/README.md +71 -515
- package/README.zh-CN.md +175 -0
- package/dist/cli.cjs +3 -3
- package/dist/index.cjs +1545 -501
- package/dist/index.d.ts +9 -0
- package/dist/index.js +431 -298
- package/package.json +13 -6
package/dist/index.cjs
CHANGED
|
@@ -340,7 +340,7 @@ class CloudRunService {
|
|
|
340
340
|
*/
|
|
341
341
|
async deploy(params) {
|
|
342
342
|
var _a;
|
|
343
|
-
const { serverName, targetPath = process.cwd(), serverConfig } = params;
|
|
343
|
+
const { serverName, targetPath = process.cwd(), serverConfig, vpcInfo, imageUrl } = params;
|
|
344
344
|
/**
|
|
345
345
|
* 参数校验和默认值设置
|
|
346
346
|
*/
|
|
@@ -350,30 +350,41 @@ class CloudRunService {
|
|
|
350
350
|
// 获取当前环境配置(包含EnvId)
|
|
351
351
|
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
352
352
|
/**
|
|
353
|
-
*
|
|
353
|
+
* 根据部署类型构建 deployInfo
|
|
354
354
|
*/
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
355
|
+
let deployInfo;
|
|
356
|
+
if (imageUrl) {
|
|
357
|
+
// 镜像部署模式:直接使用镜像地址,无需上传代码包
|
|
358
|
+
deployInfo = {
|
|
359
|
+
DeployType: 'image',
|
|
360
|
+
ImageUrl: imageUrl
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
// 代码包部署模式:获取上传信息 → 打包 → 上传
|
|
365
|
+
const { UploadUrl: uploadUrl, UploadHeaders: uploadHeaders, PackageName: packageName, PackageVersion: packageVersion } = await this.tcbService.request('DescribeCloudBaseBuildService', {
|
|
366
|
+
EnvId: envConfig.EnvId,
|
|
367
|
+
ServiceName: serverName
|
|
368
|
+
});
|
|
369
|
+
deployInfo = {
|
|
370
|
+
DeployType: 'package',
|
|
371
|
+
PackageName: packageName,
|
|
372
|
+
PackageVersion: packageVersion
|
|
373
|
+
};
|
|
374
|
+
/**
|
|
375
|
+
* 上传部署包
|
|
376
|
+
*/
|
|
377
|
+
const zipFile = await codeToZip(targetPath, { installDependency: (serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.InstallDependency) !== undefined ? serverConfig.InstallDependency : true });
|
|
378
|
+
await (0, utils_1.upload)({
|
|
379
|
+
url: uploadUrl,
|
|
380
|
+
file: zipFile,
|
|
381
|
+
headers: (uploadHeaders || []).reduce((map, item) => {
|
|
382
|
+
map[item.Key] = item.Value;
|
|
383
|
+
return map;
|
|
384
|
+
}, {}) || {},
|
|
385
|
+
method: 'PUT'
|
|
386
|
+
});
|
|
387
|
+
}
|
|
377
388
|
/**
|
|
378
389
|
* 执行部署
|
|
379
390
|
*/
|
|
@@ -398,18 +409,18 @@ class CloudRunService {
|
|
|
398
409
|
else {
|
|
399
410
|
// 创建
|
|
400
411
|
/**
|
|
401
|
-
*
|
|
412
|
+
* 判断是容器还是函数型(镜像部署固定为 container)
|
|
402
413
|
*/
|
|
403
|
-
let type = 'function';
|
|
404
|
-
if (serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.Dockerfile) {
|
|
414
|
+
let type = imageUrl ? 'container' : 'function';
|
|
415
|
+
if (!imageUrl && (serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.Dockerfile)) {
|
|
405
416
|
type = 'container';
|
|
406
417
|
}
|
|
407
|
-
else {
|
|
418
|
+
else if (!imageUrl) {
|
|
408
419
|
if (await (0, fs_extra_1.pathExists)(path_1.default.join(targetPath, 'Dockerfile'))) {
|
|
409
420
|
type = 'container';
|
|
410
421
|
}
|
|
411
422
|
}
|
|
412
|
-
if (type === 'function') {
|
|
423
|
+
if (type === 'function' && !imageUrl) {
|
|
413
424
|
deployInfo.BuildPacks = {
|
|
414
425
|
LanguageVersion: '20.18',
|
|
415
426
|
RepoLanguage: 'Node.js'
|
|
@@ -425,7 +436,8 @@ class CloudRunService {
|
|
|
425
436
|
return this._upsertFunction(true, {
|
|
426
437
|
name: serverName,
|
|
427
438
|
deployInfo,
|
|
428
|
-
serverConfig: _serverConfig
|
|
439
|
+
serverConfig: _serverConfig,
|
|
440
|
+
vpcInfo
|
|
429
441
|
});
|
|
430
442
|
}
|
|
431
443
|
}
|
|
@@ -488,15 +500,20 @@ class CloudRunService {
|
|
|
488
500
|
}
|
|
489
501
|
}
|
|
490
502
|
_upsertFunction(isNew, data) {
|
|
491
|
-
const { name, deployInfo, serverConfig } = data;
|
|
503
|
+
const { name, deployInfo, serverConfig, vpcInfo } = data;
|
|
492
504
|
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
493
505
|
const Items = parseObjectToDiffConfigItem(serverConfig);
|
|
494
|
-
|
|
506
|
+
const basePayload = {
|
|
495
507
|
EnvId: envConfig.EnvId,
|
|
496
508
|
ServerName: name,
|
|
497
509
|
DeployInfo: deployInfo,
|
|
498
510
|
Items,
|
|
499
|
-
}
|
|
511
|
+
};
|
|
512
|
+
// CreateCloudRunServer 支持传入 VpcInfo(仅创建时有效)
|
|
513
|
+
if (isNew && vpcInfo) {
|
|
514
|
+
basePayload.VpcInfo = vpcInfo;
|
|
515
|
+
}
|
|
516
|
+
return this.tcbrService.request(isNew ? 'CreateCloudRunServer' : 'UpdateCloudRunServer', basePayload);
|
|
500
517
|
}
|
|
501
518
|
}
|
|
502
519
|
exports.CloudRunService = CloudRunService;
|
|
@@ -640,6 +657,15 @@ function parseObjectToDiffConfigItem(data) {
|
|
|
640
657
|
else if (['TimerScale'].includes(k)) {
|
|
641
658
|
Items.push({ Key, TimerScale: v });
|
|
642
659
|
}
|
|
660
|
+
else if (['VpcConf'].includes(k)) {
|
|
661
|
+
Items.push({ Key, VpcConf: v });
|
|
662
|
+
}
|
|
663
|
+
else if (['VolumesConf'].includes(k)) {
|
|
664
|
+
Items.push({ Key, VolumesConf: v });
|
|
665
|
+
}
|
|
666
|
+
else if (['PublicNetConf'].includes(k)) {
|
|
667
|
+
Items.push({ Key, PublicNetConf: v });
|
|
668
|
+
}
|
|
643
669
|
});
|
|
644
670
|
return Items;
|
|
645
671
|
}
|
|
@@ -1761,6 +1787,8 @@ warn.forProperties(exports, 'deprecated', ['emitErrs', 'levelLength']);
|
|
|
1761
1787
|
"use strict";
|
|
1762
1788
|
|
|
1763
1789
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1790
|
+
exports.DEFAULT_CREATE_ENV_RESOURCES = exports.CREATE_ENV_RESOURCE_VALUES = void 0;
|
|
1791
|
+
exports.resolveCreateEnvResources = resolveCreateEnvResources;
|
|
1764
1792
|
exports.simplifyEnvList = simplifyEnvList;
|
|
1765
1793
|
exports.registerEnvTools = registerEnvTools;
|
|
1766
1794
|
const toolbox_1 = __webpack_require__(25901);
|
|
@@ -1770,6 +1798,26 @@ const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
|
1770
1798
|
const logger_js_1 = __webpack_require__(13039);
|
|
1771
1799
|
const tool_result_js_1 = __webpack_require__(9835);
|
|
1772
1800
|
const env_setup_js_1 = __webpack_require__(6978);
|
|
1801
|
+
/**
|
|
1802
|
+
* Resources accepted by CreateEnv. Matches Cloud API / Manager SDK contract.
|
|
1803
|
+
* `postgresql` enables CloudBase PostgreSQL (PG mode) when supported by the package.
|
|
1804
|
+
*/
|
|
1805
|
+
exports.CREATE_ENV_RESOURCE_VALUES = [
|
|
1806
|
+
"flexdb",
|
|
1807
|
+
"storage",
|
|
1808
|
+
"function",
|
|
1809
|
+
"postgresql",
|
|
1810
|
+
];
|
|
1811
|
+
/** Default Resources when callers omit the field (schema promises "all four"). */
|
|
1812
|
+
exports.DEFAULT_CREATE_ENV_RESOURCES = [
|
|
1813
|
+
...exports.CREATE_ENV_RESOURCE_VALUES,
|
|
1814
|
+
];
|
|
1815
|
+
function resolveCreateEnvResources(resources) {
|
|
1816
|
+
if (Array.isArray(resources) && resources.length > 0) {
|
|
1817
|
+
return [...resources];
|
|
1818
|
+
}
|
|
1819
|
+
return [...exports.DEFAULT_CREATE_ENV_RESOURCES];
|
|
1820
|
+
}
|
|
1773
1821
|
/**
|
|
1774
1822
|
* Simplify environment list data by keeping only essential fields for AI assistant
|
|
1775
1823
|
* This reduces token consumption when returning environment lists via MCP tools
|
|
@@ -2393,8 +2441,8 @@ async function enrichEnvInfoWithRuntimeMode(result, manager) {
|
|
|
2393
2441
|
: "No legacy NoSQL Database/Storage observed in this env.",
|
|
2394
2442
|
MysqlNotAvailable: hasMysql
|
|
2395
2443
|
? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
|
|
2396
|
-
: "No MySQL instance is provisioned for this env. Do NOT use `manageMysqlDatabase` / `queryMysqlDatabase` (those are MySQL-specific) and do NOT read the `relational-database-
|
|
2397
|
-
RecommendedSkills: "Read `postgresql-development` first for new PG code. `
|
|
2444
|
+
: "No MySQL instance is provisioned for this env. Do NOT use `manageMysqlDatabase` / `queryMysqlDatabase` (those are MySQL-specific) and do NOT read the `relational-database-mcp-cloudbase` skill — that family targets MySQL, not CloudBase PG.",
|
|
2445
|
+
RecommendedSkills: "Read `postgresql-development-cloudbase` first for new PG code. `cloudbase-document-database-web-sdk` is still applicable for existing NoSQL collections in this env. Skip `relational-database-mcp-cloudbase` (MySQL-only) entirely.",
|
|
2398
2446
|
}
|
|
2399
2447
|
: {
|
|
2400
2448
|
PrimaryBackend: "PostgreSQL is NOT provisioned in this env — this is a legacy NoSQL CloudBase backend.",
|
|
@@ -2403,8 +2451,8 @@ async function enrichEnvInfoWithRuntimeMode(result, manager) {
|
|
|
2403
2451
|
Storage: "Browser uploads use `app.uploadFile()` against the bucket exposed in `EnvInfo.Storages[].Bucket`.",
|
|
2404
2452
|
MysqlNotAvailable: hasMysql
|
|
2405
2453
|
? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
|
|
2406
|
-
: "No MySQL instance in this env. `manageMysqlDatabase` / `queryMysqlDatabase` and the `relational-database-
|
|
2407
|
-
RecommendedSkills: "Read `
|
|
2454
|
+
: "No MySQL instance in this env. `manageMysqlDatabase` / `queryMysqlDatabase` and the `relational-database-mcp-cloudbase` skill are not applicable.",
|
|
2455
|
+
RecommendedSkills: "Read `cloudbase-document-database-web-sdk` (and `cloud-storage-web` for uploads). `postgresql-development-cloudbase` and `relational-database-mcp-cloudbase` are not applicable to this env.",
|
|
2408
2456
|
};
|
|
2409
2457
|
return {
|
|
2410
2458
|
...result,
|
|
@@ -2535,7 +2583,13 @@ function resolveToolAuthOptions(server, overrides) {
|
|
|
2535
2583
|
function registerEnvTools(server) {
|
|
2536
2584
|
// 获取 cloudBaseOptions,如果没有则为 undefined
|
|
2537
2585
|
const cloudBaseOptions = server.cloudBaseOptions;
|
|
2538
|
-
|
|
2586
|
+
// Default: env-scoped tools need a bound envId (domains / domain management use SDK this.envId).
|
|
2587
|
+
// manageEnv create/listPackages are account-level — use requireEnvId: false there only.
|
|
2588
|
+
const getManager = (options) => (0, cloudbase_manager_js_1.getCloudBaseManager)({
|
|
2589
|
+
cloudBaseOptions,
|
|
2590
|
+
requireEnvId: options?.requireEnvId ?? true,
|
|
2591
|
+
mcpServer: server,
|
|
2592
|
+
});
|
|
2539
2593
|
const getManagerForEnvQuery = (targetEnvId, requireEnvId = true) => (0, cloudbase_manager_js_1.getCloudBaseManager)({
|
|
2540
2594
|
cloudBaseOptions: targetEnvId && targetEnvId !== cloudBaseOptions?.envId
|
|
2541
2595
|
? {
|
|
@@ -3319,7 +3373,7 @@ function registerEnvTools(server) {
|
|
|
3319
3373
|
// Register primary tool name (queryEnv)
|
|
3320
3374
|
const queryEnvToolSchema = {
|
|
3321
3375
|
title: "CloudBase 环境查询",
|
|
3322
|
-
description: "查询 CloudBase 环境相关信息,支持查询环境列表、指定环境详情和安全域名。(曾用名:envQuery、listEnvs、getEnvInfo、getEnvAuthDomains)当 action=list 时,会按 DescribeEnvs 语义做列表/筛选,标准返回字段为 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,并支持通过 fields 白名单裁剪这些字段;aliasExact=true 时会按别名精确筛选,避免把前缀相近的环境误当作候选;即使传入 envId,action=list 也只返回摘要,不会返回完整资源明细或 expiry。如需查询某个已知 EnvId 对应环境的详细信息(包括资源字段和计费信息),必须使用 action=info 并传入目标环境的 envId 参数。action=info 会在可用时补充 BillingInfo(如 ExpireTime、PayMode、IsAutoRenew 等计费字段)。\n\n🔍 action=info 还会派生三个用于后端选型的字段:\n- `EnvInfo.RuntimeMode`:'postgresql' 或 'nosql',表示新业务建议默认使用的后端(PG 已开通时为 postgresql,否则为 nosql)。\n- `EnvInfo.RuntimeBackends`:`{postgresql, nosql, mysql}` 三个布尔值,描述当前环境实际并存的后端。\n- `EnvInfo.RuntimeModeHints`:每个后端对应的 API/工具/skill 提示。\n\nAI 在写业务/权限/存储代码前必须先看这三项:PG 模式下新业务推荐 `app.rdb()` + RLS(`managePgDatabase action=execute` 跑 `CREATE POLICY`)+ pgstore;已存在的 NoSQL 集合 / 旧 storage / `managePermissions(resourceType=\"noSqlDatabase\")` 在 PG 环境下仍然有效。真正不适用的是 MySQL:当 `RuntimeBackends.mysql === false` 时,`manageMysqlDatabase` / `queryMysqlDatabase` / `relational-database-
|
|
3376
|
+
description: "查询 CloudBase 环境相关信息,支持查询环境列表、指定环境详情和安全域名。(曾用名:envQuery、listEnvs、getEnvInfo、getEnvAuthDomains)当 action=list 时,会按 DescribeEnvs 语义做列表/筛选,标准返回字段为 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,并支持通过 fields 白名单裁剪这些字段;aliasExact=true 时会按别名精确筛选,避免把前缀相近的环境误当作候选;即使传入 envId,action=list 也只返回摘要,不会返回完整资源明细或 expiry。如需查询某个已知 EnvId 对应环境的详细信息(包括资源字段和计费信息),必须使用 action=info 并传入目标环境的 envId 参数。action=info 会在可用时补充 BillingInfo(如 ExpireTime、PayMode、IsAutoRenew 等计费字段)。\n\n🔍 action=info 还会派生三个用于后端选型的字段:\n- `EnvInfo.RuntimeMode`:'postgresql' 或 'nosql',表示新业务建议默认使用的后端(PG 已开通时为 postgresql,否则为 nosql)。\n- `EnvInfo.RuntimeBackends`:`{postgresql, nosql, mysql}` 三个布尔值,描述当前环境实际并存的后端。\n- `EnvInfo.RuntimeModeHints`:每个后端对应的 API/工具/skill 提示。\n\nAI 在写业务/权限/存储代码前必须先看这三项:PG 模式下新业务推荐 `app.rdb()` + RLS(`managePgDatabase action=execute` 跑 `CREATE POLICY`)+ pgstore;已存在的 NoSQL 集合 / 旧 storage / `managePermissions(resourceType=\"noSqlDatabase\")` 在 PG 环境下仍然有效。真正不适用的是 MySQL:当 `RuntimeBackends.mysql === false` 时,`manageMysqlDatabase` / `queryMysqlDatabase` / `relational-database-mcp-cloudbase` skill 都不该使用。",
|
|
3323
3377
|
inputSchema: {
|
|
3324
3378
|
action: zod_1.z
|
|
3325
3379
|
.enum(["list", "info", "domains"])
|
|
@@ -3416,14 +3470,10 @@ function registerEnvTools(server) {
|
|
|
3416
3470
|
.string()
|
|
3417
3471
|
.optional()
|
|
3418
3472
|
.describe("套餐 ID(action=create/modifyPlan 时必填)。可选值如 baas_personal(个人版)、baas_pf_standard(标准版)、baas_pf_enterprise(企业版)"),
|
|
3419
|
-
region: zod_1.z
|
|
3420
|
-
.string()
|
|
3421
|
-
.optional()
|
|
3422
|
-
.describe("环境地域(action=create 时可选)。默认 ap-shanghai,可选 ap-guangzhou、ap-beijing 等"),
|
|
3423
3473
|
resources: zod_1.z
|
|
3424
|
-
.array(zod_1.z.enum(
|
|
3474
|
+
.array(zod_1.z.enum(exports.CREATE_ENV_RESOURCE_VALUES))
|
|
3425
3475
|
.optional()
|
|
3426
|
-
.describe("启用的资源类型(action=create
|
|
3476
|
+
.describe("启用的资源类型(action=create 时可选)。省略时默认全部四项:flexdb(文档数据库)、storage(存储)、function(云函数)、postgresql(PostgreSQL)。CreateEnv 要求 Resources 非空,MCP 会始终下发该字段。"),
|
|
3427
3477
|
duration: zod_1.z
|
|
3428
3478
|
.number()
|
|
3429
3479
|
.int()
|
|
@@ -3451,17 +3501,19 @@ function registerEnvTools(server) {
|
|
|
3451
3501
|
const action = rawArgs.action ?? "";
|
|
3452
3502
|
const alias = normalizeOptionalToolString(rawArgs.alias);
|
|
3453
3503
|
const packageId = normalizeOptionalToolString(rawArgs.packageId);
|
|
3454
|
-
const
|
|
3455
|
-
const resources = rawArgs.resources;
|
|
3504
|
+
const resolvedResources = resolveCreateEnvResources(rawArgs.resources);
|
|
3456
3505
|
const duration = rawArgs.duration ?? 1;
|
|
3457
3506
|
const envId = normalizeOptionalToolString(rawArgs.envId);
|
|
3458
3507
|
const confirmed = rawArgs.confirm === "yes";
|
|
3459
3508
|
try {
|
|
3460
|
-
const cloudbase = await getManager();
|
|
3509
|
+
const cloudbase = await getManager({ requireEnvId: false });
|
|
3461
3510
|
switch (action) {
|
|
3462
3511
|
case "listPackages": {
|
|
3463
|
-
//
|
|
3464
|
-
const result = await cloudbase.env.describeBaasPackageList({
|
|
3512
|
+
// Query packages for new purchase. Source aligns with billing-side Baas package APIs.
|
|
3513
|
+
const result = await cloudbase.env.describeBaasPackageList({
|
|
3514
|
+
TargetAction: "new",
|
|
3515
|
+
Source: "qcloud",
|
|
3516
|
+
});
|
|
3465
3517
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
3466
3518
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
3467
3519
|
ok: true,
|
|
@@ -3471,12 +3523,13 @@ function registerEnvTools(server) {
|
|
|
3471
3523
|
});
|
|
3472
3524
|
}
|
|
3473
3525
|
case "create": {
|
|
3474
|
-
//
|
|
3526
|
+
// CreateEnv (Manager SDK / Cloud API) accepts Alias, PackageId, Resources, Period, etc.
|
|
3527
|
+
// It does NOT accept Region — region is determined by account/package, not this call.
|
|
3475
3528
|
if (!confirmed) {
|
|
3476
3529
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
3477
3530
|
ok: false,
|
|
3478
3531
|
code: "CONFIRM_REQUIRED",
|
|
3479
|
-
message: `创建环境需要您确认。请确认以下配置信息后传入 confirm="yes":\n别名: ${alias}\n套餐: ${packageId}\n
|
|
3532
|
+
message: `创建环境需要您确认。请确认以下配置信息后传入 confirm="yes":\n别名: ${alias}\n套餐: ${packageId}\n资源类型: ${resolvedResources.join(", ")}\n时长: ${duration} 个月\n说明: CreateEnv 不接受 Region;环境地域由账号与套餐侧决定。`,
|
|
3480
3533
|
next_step: {
|
|
3481
3534
|
tool: "manageEnv",
|
|
3482
3535
|
action: "create",
|
|
@@ -3493,12 +3546,9 @@ function registerEnvTools(server) {
|
|
|
3493
3546
|
const createParams = {
|
|
3494
3547
|
Alias: alias,
|
|
3495
3548
|
PackageId: packageId,
|
|
3496
|
-
|
|
3549
|
+
Resources: resolvedResources,
|
|
3497
3550
|
Period: duration,
|
|
3498
3551
|
};
|
|
3499
|
-
if (resources && resources.length > 0) {
|
|
3500
|
-
createParams.Resources = resources;
|
|
3501
|
-
}
|
|
3502
3552
|
const result = await cloudbase.env.createEnv(createParams);
|
|
3503
3553
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
3504
3554
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
@@ -3506,6 +3556,7 @@ function registerEnvTools(server) {
|
|
|
3506
3556
|
code: "ENV_CREATED",
|
|
3507
3557
|
message: `环境创建成功!新环境 ID: ${result.EnvId}。环境初始化可能需要几分钟,请通过 queryEnv(action="info", envId="${result.EnvId}") 轮询直到 Status 为正常。`,
|
|
3508
3558
|
envId: result.EnvId,
|
|
3559
|
+
resources: resolvedResources,
|
|
3509
3560
|
});
|
|
3510
3561
|
}
|
|
3511
3562
|
case "modifyPlan": {
|
|
@@ -3583,11 +3634,19 @@ function registerEnvTools(server) {
|
|
|
3583
3634
|
if (toolPayloadResult) {
|
|
3584
3635
|
return toolPayloadResult;
|
|
3585
3636
|
}
|
|
3637
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3638
|
+
if (action === "listPackages" && /billTags/i.test(errorMessage)) {
|
|
3639
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
3640
|
+
ok: false,
|
|
3641
|
+
code: "PACKAGE_LIST_FAILED",
|
|
3642
|
+
message: `查询套餐列表失败(计费标签 BillTags):${errorMessage}。可改用控制台查看套餐,或直接使用已知 packageId(如 baas_personal)调用 manageEnv(action="create")。`,
|
|
3643
|
+
});
|
|
3644
|
+
}
|
|
3586
3645
|
return {
|
|
3587
3646
|
content: [
|
|
3588
3647
|
{
|
|
3589
3648
|
type: "text",
|
|
3590
|
-
text: `环境管理操作失败: ${
|
|
3649
|
+
text: `环境管理操作失败: ${errorMessage}`,
|
|
3591
3650
|
},
|
|
3592
3651
|
],
|
|
3593
3652
|
};
|
|
@@ -79979,6 +80038,32 @@ function parallel(list, iterator, callback)
|
|
|
79979
80038
|
}
|
|
79980
80039
|
|
|
79981
80040
|
|
|
80041
|
+
/***/ }),
|
|
80042
|
+
|
|
80043
|
+
/***/ 18944:
|
|
80044
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
80045
|
+
|
|
80046
|
+
"use strict";
|
|
80047
|
+
|
|
80048
|
+
/**
|
|
80049
|
+
* 集成中心(UserKey)类型定义
|
|
80050
|
+
*
|
|
80051
|
+
* 字段与 dev-platform(weda-alternative/apps/dev-platform)对齐:
|
|
80052
|
+
* - 云 API 出入参为 PascalCase(EnvId / KeyID / AuthTypeCode / Ext / NeedPwd 等)
|
|
80053
|
+
* - Ext 为 JSON 字符串,承载动态配置字段(含加密后的敏感值)
|
|
80054
|
+
*/
|
|
80055
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
80056
|
+
exports.UserKeyBindServiceType = void 0;
|
|
80057
|
+
/** 密钥对绑定的服务类型 */
|
|
80058
|
+
var UserKeyBindServiceType;
|
|
80059
|
+
(function (UserKeyBindServiceType) {
|
|
80060
|
+
UserKeyBindServiceType["MCP"] = "Mcp";
|
|
80061
|
+
UserKeyBindServiceType["APIS"] = "Apis";
|
|
80062
|
+
UserKeyBindServiceType["OTHER"] = "Other";
|
|
80063
|
+
UserKeyBindServiceType["CLOUD_FUNCTION"] = "CloudFunction";
|
|
80064
|
+
})(UserKeyBindServiceType || (exports.UserKeyBindServiceType = UserKeyBindServiceType = {}));
|
|
80065
|
+
|
|
80066
|
+
|
|
79982
80067
|
/***/ }),
|
|
79983
80068
|
|
|
79984
80069
|
/***/ 18959:
|
|
@@ -84563,6 +84648,102 @@ exports.colors = {
|
|
|
84563
84648
|
})));
|
|
84564
84649
|
|
|
84565
84650
|
|
|
84651
|
+
/***/ }),
|
|
84652
|
+
|
|
84653
|
+
/***/ 22573:
|
|
84654
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
84655
|
+
|
|
84656
|
+
"use strict";
|
|
84657
|
+
|
|
84658
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
84659
|
+
exports.getAesKey = getAesKey;
|
|
84660
|
+
exports.__clearAesKeyCache = __clearAesKeyCache;
|
|
84661
|
+
/**
|
|
84662
|
+
* 集成中心 AES key 获取(七彩石)
|
|
84663
|
+
*
|
|
84664
|
+
* 通过 QueryRainbowConfig 拉取 group=tcb-config(国内)/
|
|
84665
|
+
* frontend.tcb-config(国际)下字段 dev-platform-apis 的值作为 AES-256-CBC 密钥。
|
|
84666
|
+
*
|
|
84667
|
+
* 安全约束:
|
|
84668
|
+
* - key 仅在进程内存使用,按 group 维度缓存;
|
|
84669
|
+
* - 绝不写入任何本地文件、绝不打印日志。
|
|
84670
|
+
*/
|
|
84671
|
+
const http_request_1 = __webpack_require__(72088);
|
|
84672
|
+
const error_1 = __webpack_require__(40430);
|
|
84673
|
+
/** 七彩石配置 */
|
|
84674
|
+
const RAINBOW_CONFIG = {
|
|
84675
|
+
ENV_TYPE: 'Default',
|
|
84676
|
+
GROUP_CN: 'tcb-config',
|
|
84677
|
+
GROUP_INTL: 'frontend.tcb-config',
|
|
84678
|
+
/** AES key 在 kvs 中的字段名 */
|
|
84679
|
+
CRYPTO_KEY_FIELD: 'dev-platform-apis'
|
|
84680
|
+
};
|
|
84681
|
+
const RAINBOW_HOST_CN = 'wedaapi.tcbautomation.cn';
|
|
84682
|
+
const RAINBOW_HOST_INTL = 'gray.decisiontcb.tencentcloud.com';
|
|
84683
|
+
/** 无 EnvId 时的占位 Uin */
|
|
84684
|
+
const FALLBACK_UIN = '3309033';
|
|
84685
|
+
/** 进程内缓存(按 group 维度),key 不落盘、不打印 */
|
|
84686
|
+
const keyCache = {};
|
|
84687
|
+
/**
|
|
84688
|
+
* 获取集成中心 AES key(进程内缓存)
|
|
84689
|
+
* @throws CloudBaseError 拉取或解析失败
|
|
84690
|
+
*/
|
|
84691
|
+
async function getAesKey(params = {}) {
|
|
84692
|
+
var _a, _b;
|
|
84693
|
+
const { envId, isIntl = false } = params;
|
|
84694
|
+
const group = isIntl ? RAINBOW_CONFIG.GROUP_INTL : RAINBOW_CONFIG.GROUP_CN;
|
|
84695
|
+
if (keyCache[group]) {
|
|
84696
|
+
return keyCache[group];
|
|
84697
|
+
}
|
|
84698
|
+
const host = isIntl ? RAINBOW_HOST_INTL : RAINBOW_HOST_CN;
|
|
84699
|
+
const url = `https://${host}/Graydecision/V1/QueryRainbowConfig?group=${group}`;
|
|
84700
|
+
const payload = {
|
|
84701
|
+
EnvType: RAINBOW_CONFIG.ENV_TYPE,
|
|
84702
|
+
Group: group
|
|
84703
|
+
};
|
|
84704
|
+
if (envId) {
|
|
84705
|
+
payload.EnvId = envId;
|
|
84706
|
+
}
|
|
84707
|
+
else {
|
|
84708
|
+
payload.Uin = FALLBACK_UIN;
|
|
84709
|
+
}
|
|
84710
|
+
let resJson;
|
|
84711
|
+
try {
|
|
84712
|
+
resJson = await (0, http_request_1.fetch)(url, {
|
|
84713
|
+
method: 'POST',
|
|
84714
|
+
headers: { 'content-type': 'application/json' },
|
|
84715
|
+
body: JSON.stringify(payload)
|
|
84716
|
+
});
|
|
84717
|
+
}
|
|
84718
|
+
catch (e) {
|
|
84719
|
+
throw new error_1.CloudBaseError('获取集成加解密密钥失败:七彩石配置拉取请求异常');
|
|
84720
|
+
}
|
|
84721
|
+
const data = (_a = resJson === null || resJson === void 0 ? void 0 : resJson.Response) === null || _a === void 0 ? void 0 : _a.Data;
|
|
84722
|
+
if (!data || data.ErrorCode !== '0') {
|
|
84723
|
+
throw new error_1.CloudBaseError(`获取集成加解密密钥失败:七彩石返回异常(ErrorCode=${data === null || data === void 0 ? void 0 : data.ErrorCode})`);
|
|
84724
|
+
}
|
|
84725
|
+
let config;
|
|
84726
|
+
try {
|
|
84727
|
+
config = JSON.parse(data.ConfigValue)[0];
|
|
84728
|
+
}
|
|
84729
|
+
catch (e) {
|
|
84730
|
+
throw new error_1.CloudBaseError('获取集成加解密密钥失败:七彩石配置内容解析失败');
|
|
84731
|
+
}
|
|
84732
|
+
const kvs = ((_b = config === null || config === void 0 ? void 0 : config.kvs) === null || _b === void 0 ? void 0 : _b.kvs) || [];
|
|
84733
|
+
const target = kvs.find(kv => kv.key === RAINBOW_CONFIG.CRYPTO_KEY_FIELD);
|
|
84734
|
+
const key = target === null || target === void 0 ? void 0 : target.value;
|
|
84735
|
+
if (!key) {
|
|
84736
|
+
throw new error_1.CloudBaseError(`获取集成加解密密钥失败:七彩石配置中缺少 ${RAINBOW_CONFIG.CRYPTO_KEY_FIELD} 字段`);
|
|
84737
|
+
}
|
|
84738
|
+
keyCache[group] = key;
|
|
84739
|
+
return key;
|
|
84740
|
+
}
|
|
84741
|
+
/** 清空进程内缓存(仅供测试使用) */
|
|
84742
|
+
function __clearAesKeyCache() {
|
|
84743
|
+
Object.keys(keyCache).forEach(k => delete keyCache[k]);
|
|
84744
|
+
}
|
|
84745
|
+
|
|
84746
|
+
|
|
84566
84747
|
/***/ }),
|
|
84567
84748
|
|
|
84568
84749
|
/***/ 22738:
|
|
@@ -87655,6 +87836,116 @@ __decorate([
|
|
|
87655
87836
|
], PermissionService.prototype, "modifyEnvAuthzConfig", null);
|
|
87656
87837
|
|
|
87657
87838
|
|
|
87839
|
+
/***/ }),
|
|
87840
|
+
|
|
87841
|
+
/***/ 25068:
|
|
87842
|
+
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
87843
|
+
|
|
87844
|
+
"use strict";
|
|
87845
|
+
|
|
87846
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
87847
|
+
if (k2 === undefined) k2 = k;
|
|
87848
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
87849
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
87850
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
87851
|
+
}
|
|
87852
|
+
Object.defineProperty(o, k2, desc);
|
|
87853
|
+
}) : (function(o, m, k, k2) {
|
|
87854
|
+
if (k2 === undefined) k2 = k;
|
|
87855
|
+
o[k2] = m[k];
|
|
87856
|
+
}));
|
|
87857
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
87858
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
87859
|
+
}) : function(o, v) {
|
|
87860
|
+
o["default"] = v;
|
|
87861
|
+
});
|
|
87862
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
87863
|
+
var ownKeys = function(o) {
|
|
87864
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
87865
|
+
var ar = [];
|
|
87866
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
87867
|
+
return ar;
|
|
87868
|
+
};
|
|
87869
|
+
return ownKeys(o);
|
|
87870
|
+
};
|
|
87871
|
+
return function (mod) {
|
|
87872
|
+
if (mod && mod.__esModule) return mod;
|
|
87873
|
+
var result = {};
|
|
87874
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
87875
|
+
__setModuleDefault(result, mod);
|
|
87876
|
+
return result;
|
|
87877
|
+
};
|
|
87878
|
+
})();
|
|
87879
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
87880
|
+
exports.AESCrypto = void 0;
|
|
87881
|
+
exports.aesEncryptWithIv = aesEncryptWithIv;
|
|
87882
|
+
/**
|
|
87883
|
+
* 集成中心 AES 加解密
|
|
87884
|
+
*
|
|
87885
|
+
* 与 dev-platform 控制台的 AESCrypto(crypto-js)完全对齐,保证密文可互相解出:
|
|
87886
|
+
* - 算法:AES-256-CBC
|
|
87887
|
+
* - key:32 字节(按 UTF-8 解析)
|
|
87888
|
+
* - IV:每次随机 16 字节
|
|
87889
|
+
* - padding:PKCS7
|
|
87890
|
+
* - 密文格式:base64(IV) + base64(ciphertext)
|
|
87891
|
+
* (IV 固定 16 字节 → base64 长度恒为 24,前 24 字符即 IV)
|
|
87892
|
+
*
|
|
87893
|
+
* 使用 Node 内置 crypto 复刻,无需引入额外依赖。
|
|
87894
|
+
*/
|
|
87895
|
+
const crypto = __importStar(__webpack_require__(55511));
|
|
87896
|
+
const ALGORITHM = 'aes-256-cbc';
|
|
87897
|
+
const KEY_BYTE_LENGTH = 32;
|
|
87898
|
+
const IV_BYTE_LENGTH = 16;
|
|
87899
|
+
/** base64(16 字节) 的固定长度 */
|
|
87900
|
+
const IV_BASE64_LENGTH = 24;
|
|
87901
|
+
function assertKey(key) {
|
|
87902
|
+
if (Buffer.byteLength(key, 'utf8') !== KEY_BYTE_LENGTH) {
|
|
87903
|
+
throw new Error('AES key 必须是 32 字节长度');
|
|
87904
|
+
}
|
|
87905
|
+
}
|
|
87906
|
+
/**
|
|
87907
|
+
* 使用指定 IV 加密(供测试与内部使用,保证可重现)
|
|
87908
|
+
*/
|
|
87909
|
+
function aesEncryptWithIv(plaintext, key, iv) {
|
|
87910
|
+
assertKey(key);
|
|
87911
|
+
if (iv.length !== IV_BYTE_LENGTH) {
|
|
87912
|
+
throw new Error('IV 必须是 16 字节长度');
|
|
87913
|
+
}
|
|
87914
|
+
const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(key, 'utf8'), iv);
|
|
87915
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
87916
|
+
return iv.toString('base64') + encrypted.toString('base64');
|
|
87917
|
+
}
|
|
87918
|
+
class AESCryptoImpl {
|
|
87919
|
+
/**
|
|
87920
|
+
* AES-256-CBC 加密,返回 base64(IV)+base64(密文)
|
|
87921
|
+
*/
|
|
87922
|
+
encrypt(plaintext, key) {
|
|
87923
|
+
assertKey(key);
|
|
87924
|
+
const iv = crypto.randomBytes(IV_BYTE_LENGTH);
|
|
87925
|
+
return aesEncryptWithIv(plaintext, key, iv);
|
|
87926
|
+
}
|
|
87927
|
+
/**
|
|
87928
|
+
* 解密 base64(IV)+base64(密文) 格式的密文
|
|
87929
|
+
*/
|
|
87930
|
+
decrypt(encryptedData, key) {
|
|
87931
|
+
assertKey(key);
|
|
87932
|
+
const ivBase64 = encryptedData.slice(0, IV_BASE64_LENGTH);
|
|
87933
|
+
const ciphertextBase64 = encryptedData.slice(IV_BASE64_LENGTH);
|
|
87934
|
+
const iv = Buffer.from(ivBase64, 'base64');
|
|
87935
|
+
if (iv.length !== IV_BYTE_LENGTH) {
|
|
87936
|
+
throw new Error('密文格式错误:IV 长度非法');
|
|
87937
|
+
}
|
|
87938
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, Buffer.from(key, 'utf8'), iv);
|
|
87939
|
+
const decrypted = Buffer.concat([
|
|
87940
|
+
decipher.update(Buffer.from(ciphertextBase64, 'base64')),
|
|
87941
|
+
decipher.final()
|
|
87942
|
+
]);
|
|
87943
|
+
return decrypted.toString('utf8');
|
|
87944
|
+
}
|
|
87945
|
+
}
|
|
87946
|
+
exports.AESCrypto = new AESCryptoImpl();
|
|
87947
|
+
|
|
87948
|
+
|
|
87658
87949
|
/***/ }),
|
|
87659
87950
|
|
|
87660
87951
|
/***/ 25177:
|
|
@@ -89863,8 +90154,12 @@ exports.MANAGE_FUNCTION_ACTIONS = [
|
|
|
89863
90154
|
"incrementalDeployFunction", // 增量部署,需通过 pluginOptions.functions 注入实现
|
|
89864
90155
|
];
|
|
89865
90156
|
const VPC_SCHEMA = zod_1.z.object({
|
|
89866
|
-
vpcId: zod_1.z
|
|
89867
|
-
|
|
90157
|
+
vpcId: zod_1.z
|
|
90158
|
+
.string()
|
|
90159
|
+
.describe("VPC ID from the real database/network console (e.g. vpc-xxxxxxxx). Required for non-native TCP DB access. Do NOT invent or use placeholders."),
|
|
90160
|
+
subnetId: zod_1.z
|
|
90161
|
+
.string()
|
|
90162
|
+
.describe("Subnet ID in the same VPC as the private DB endpoint (e.g. subnet-xxxxxxxx). Do NOT invent or use placeholders."),
|
|
89868
90163
|
});
|
|
89869
90164
|
// 镜像部署值,对应 SCF Runtime=CustomImage
|
|
89870
90165
|
exports.CUSTOM_IMAGE_RUNTIME = "CustomImage";
|
|
@@ -89948,8 +90243,11 @@ const CREATE_FUNCTION_SCHEMA = zod_1.z.object({
|
|
|
89948
90243
|
})
|
|
89949
90244
|
.optional(),
|
|
89950
90245
|
timeout: zod_1.z.number().optional().describe("函数超时时间"),
|
|
89951
|
-
envVariables: zod_1.z
|
|
89952
|
-
|
|
90246
|
+
envVariables: zod_1.z
|
|
90247
|
+
.record(zod_1.z.string())
|
|
90248
|
+
.optional()
|
|
90249
|
+
.describe("环境变量。若包含 DATABASE_URL / MYSQL_* / POSTGRES_* / REDIS_* 等传统 TCP 连库变量,必须同时配置 vpc(vpcId+subnetId),且 ID 必须来自真实库/网络信息,禁止猜测。原生 app.rdb()/app.database() 不需要 VPC。"),
|
|
90250
|
+
vpc: VPC_SCHEMA.optional().describe("私有网络配置(出网)。非原生 SDK、用 TCP 访问 VPC 内 MySQL/PostgreSQL/Redis 时必填。vpcId/subnetId 必须与数据库内网 VPC 一致;未知时先查控制台或询问用户,禁止填占位符。"),
|
|
89953
90251
|
runtime: zod_1.z
|
|
89954
90252
|
.string()
|
|
89955
90253
|
.optional()
|
|
@@ -90203,7 +90501,7 @@ function registerFunctionTools(server) {
|
|
|
90203
90501
|
},
|
|
90204
90502
|
{
|
|
90205
90503
|
tool: "queryGateway",
|
|
90206
|
-
action: "
|
|
90504
|
+
action: "getRoute",
|
|
90207
90505
|
reason: "查看该函数是否已暴露网关访问入口",
|
|
90208
90506
|
},
|
|
90209
90507
|
]);
|
|
@@ -90513,7 +90811,7 @@ function registerFunctionTools(server) {
|
|
|
90513
90811
|
raw: imageResult,
|
|
90514
90812
|
}, `已基于镜像 ${createImageConfig.imageUri} 创建 HTTP 函数 ${functionName}。` +
|
|
90515
90813
|
`请确认 TCR、SCF 与构建管道处于同一地域;如需通过 URL 访问,请显式调用 ` +
|
|
90516
|
-
`manageGateway(action="
|
|
90814
|
+
`manageGateway(action="createRoute", type="HTTP") 并按需调整函数安全规则。` +
|
|
90517
90815
|
`部署后可用 queryFunctions(action="getFunctionDetail") 确认函数已就绪。`, [
|
|
90518
90816
|
{
|
|
90519
90817
|
tool: "queryFunctions",
|
|
@@ -90527,8 +90825,8 @@ function registerFunctionTools(server) {
|
|
|
90527
90825
|
},
|
|
90528
90826
|
{
|
|
90529
90827
|
tool: "manageGateway",
|
|
90530
|
-
action: "
|
|
90531
|
-
reason: "如需通过 URL 访问镜像 HTTP
|
|
90828
|
+
action: "createRoute",
|
|
90829
|
+
reason: "如需通过 URL 访问镜像 HTTP 函数,显式创建 Domain/Route 访问入口并传 type=\"HTTP\"(映射 WEB_SCF)",
|
|
90532
90830
|
},
|
|
90533
90831
|
]);
|
|
90534
90832
|
}
|
|
@@ -90583,12 +90881,12 @@ function registerFunctionTools(server) {
|
|
|
90583
90881
|
if (func.type === "HTTP") {
|
|
90584
90882
|
nextActions.push({
|
|
90585
90883
|
tool: "manageGateway",
|
|
90586
|
-
action: "
|
|
90587
|
-
reason: "如果需要通过 URL 访问 HTTP 函数,请调用 manageGateway(action=\"
|
|
90884
|
+
action: "createRoute",
|
|
90885
|
+
reason: "如果需要通过 URL 访问 HTTP 函数,请调用 manageGateway(action=\"createRoute\") 并显式传 type=\"HTTP\"(映射 WEB_SCF),再按实际路径和鉴权需求创建访问入口,不要默认假设 /函数名 已存在",
|
|
90588
90886
|
});
|
|
90589
90887
|
nextActions.push({
|
|
90590
90888
|
tool: "queryGateway",
|
|
90591
|
-
action: "
|
|
90889
|
+
action: "getRoute",
|
|
90592
90890
|
reason: "交付前确认 HTTP 访问路径是否已存在并已生效",
|
|
90593
90891
|
});
|
|
90594
90892
|
nextActions.push({
|
|
@@ -90603,7 +90901,7 @@ function registerFunctionTools(server) {
|
|
|
90603
90901
|
});
|
|
90604
90902
|
}
|
|
90605
90903
|
const message = func.type === "HTTP"
|
|
90606
|
-
? `已创建 HTTP 函数 ${functionName}。如果后续需要通过 URL 访问,请显式调用 manageGateway(action="
|
|
90904
|
+
? `已创建 HTTP 函数 ${functionName}。如果后续需要通过 URL 访问,请显式调用 manageGateway(action="createRoute"),并把 type="HTTP" 一起传入(映射 UpstreamResourceType=WEB_SCF),再按实际路径和鉴权需求创建访问入口。评测或其他外部调用方可能会以匿名身份访问,而且失败后不一定会把 EXCEED_AUTHORITY 再反馈给 AI;交付前请主动确认访问路径和函数安全规则,若已出现 EXCEED_AUTHORITY,请先调用 queryPermissions(action="getResourcePermission", resourceType="function", resourceId="${functionName}") 查看当前规则,再按需要使用 managePermissions(action="updateResourcePermission") 调整权限。`
|
|
90607
90905
|
: `已创建函数 ${functionName}`;
|
|
90608
90906
|
return buildEnvelope({
|
|
90609
90907
|
action: input.action,
|
|
@@ -91091,8 +91389,11 @@ function registerFunctionTools(server) {
|
|
|
91091
91389
|
zipFile: zod_1.z.string().optional().describe("仅兼容特殊场景:预先准备好的代码包 base64 编码。普通 createFunction/updateFunctionCode 默认不要先压缩 zip,优先使用 functionRootPath。"),
|
|
91092
91390
|
handler: zod_1.z.string().optional().describe("函数入口"),
|
|
91093
91391
|
timeout: zod_1.z.number().optional().describe("配置更新时的超时时间"),
|
|
91094
|
-
envVariables: zod_1.z
|
|
91095
|
-
|
|
91392
|
+
envVariables: zod_1.z
|
|
91393
|
+
.record(zod_1.z.string())
|
|
91394
|
+
.optional()
|
|
91395
|
+
.describe("配置更新时要合并的环境变量。若含 DATABASE_URL / MYSQL_* / POSTGRES_* / REDIS_* 等 TCP 连库变量,必须同时提供真实 vpc(或函数已绑定完整 VPC)。禁止猜测 vpcId/subnetId。"),
|
|
91396
|
+
vpc: VPC_SCHEMA.optional().describe("配置更新时的 VPC 信息。非原生 TCP 连库场景必填真实 vpcId+subnetId;不要用占位符。"),
|
|
91096
91397
|
params: zod_1.z.record(zod_1.z.any()).optional().describe("invokeFunction 的调用参数"),
|
|
91097
91398
|
triggers: zod_1.z
|
|
91098
91399
|
.array(TRIGGER_SCHEMA)
|
|
@@ -93364,7 +93665,7 @@ class EnvService {
|
|
|
93364
93665
|
* @returns {Promise<IEnvInfoRes>}
|
|
93365
93666
|
*/
|
|
93366
93667
|
async getEnvInfo() {
|
|
93367
|
-
var _a;
|
|
93668
|
+
var _a, _b;
|
|
93368
93669
|
// 使用 DescribeEnvInfo 进行单环境查询
|
|
93369
93670
|
const params = {
|
|
93370
93671
|
EnvId: this.envId
|
|
@@ -93372,6 +93673,7 @@ class EnvService {
|
|
|
93372
93673
|
const response = await this.describeEnvInfo(params);
|
|
93373
93674
|
const envBaseInfo = (_a = response === null || response === void 0 ? void 0 : response.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo;
|
|
93374
93675
|
// 将 EnvBaseInfo 完整映射到 EnvInfo 类型,避免新增字段被丢弃
|
|
93676
|
+
// UserInfo 位于 response.EnvInfo 顶层(非 EnvBaseInfo),单独映射
|
|
93375
93677
|
const envInfo = {
|
|
93376
93678
|
EnvId: envBaseInfo.EnvId,
|
|
93377
93679
|
Alias: envBaseInfo.Alias,
|
|
@@ -93401,7 +93703,8 @@ class EnvService {
|
|
|
93401
93703
|
EnvPreferences: envBaseInfo.EnvPreferences,
|
|
93402
93704
|
Meta: envBaseInfo.Meta,
|
|
93403
93705
|
PostgreSQL: envBaseInfo.PostgreSQL,
|
|
93404
|
-
EnvStatus: envBaseInfo.EnvStatus
|
|
93706
|
+
EnvStatus: envBaseInfo.EnvStatus,
|
|
93707
|
+
UserInfo: (_b = response === null || response === void 0 ? void 0 : response.EnvInfo) === null || _b === void 0 ? void 0 : _b.UserInfo
|
|
93405
93708
|
};
|
|
93406
93709
|
return {
|
|
93407
93710
|
EnvInfo: envInfo,
|
|
@@ -113930,6 +114233,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
113930
114233
|
};
|
|
113931
114234
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
113932
114235
|
exports.CLOUDRUN_ACCESS_TYPES = exports.CLOUDRUN_SERVICE_TYPES = void 0;
|
|
114236
|
+
exports.detectCloudRunDbNetworkRisk = detectCloudRunDbNetworkRisk;
|
|
113933
114237
|
exports.registerCloudRunTools = registerCloudRunTools;
|
|
113934
114238
|
const child_process_1 = __webpack_require__(79646);
|
|
113935
114239
|
const fs_1 = __importDefault(__webpack_require__(29021));
|
|
@@ -113972,7 +114276,7 @@ const ManageCloudRunInputSchema = {
|
|
|
113972
114276
|
})).optional().describe('扩缩容配置数组,用于配置服务的自动扩缩容策略。可配置多个扩缩容策略'),
|
|
113973
114277
|
CustomLogs: zod_1.z.string().optional().describe('自定义日志配置,用于配置服务的日志收集和存储策略'),
|
|
113974
114278
|
Port: zod_1.z.number().min(1).max(65535).optional().describe('服务监听端口配置。函数型服务固定为3000,容器型服务可自定义。服务代码必须监听此端口才能正常接收请求'),
|
|
113975
|
-
EnvParams: zod_1.z.string().optional().describe('环境变量配置,JSON字符串格式。用于传递配置信息给服务代码,如\'{"DATABASE_URL":"
|
|
114279
|
+
EnvParams: zod_1.z.string().optional().describe('环境变量配置,JSON字符串格式。用于传递配置信息给服务代码,如\'{"DATABASE_URL":"postgres://user:pass@10.x.x.x:5432/db","NODE_ENV":"production"}\'。SDK v5.6.1+ 会自动对传入的环境变量进行 AES-256-CBC 加密传输。⚠️ 若 EnvParams 含 DATABASE_URL / MYSQL_* / POSTGRES_* / REDIS_* 等传统 TCP 连库变量,必须同时配置 VpcConf,否则实例通常无法访问 VPC 内数据库'),
|
|
113976
114280
|
Dockerfile: zod_1.z.string().optional().describe('Dockerfile文件名配置,仅容器型服务需要。指定用于构建容器镜像的Dockerfile文件路径,默认为项目根目录下的Dockerfile'),
|
|
113977
114281
|
BuildDir: zod_1.z.string().optional().describe('构建目录配置,指定代码构建的目录路径。当代码结构与标准不同时使用,默认为项目根目录'),
|
|
113978
114282
|
InternalAccess: zod_1.z.string().optional().describe('内网访问开关配置,控制是否启用内网访问。true=启用内网访问(可通过云开发SDK直接调用),false=关闭内网访问(仅公网访问)'),
|
|
@@ -113996,9 +114300,9 @@ const ManageCloudRunInputSchema = {
|
|
|
113996
114300
|
ReplicaNum: zod_1.z.number().min(0).describe('定时扩缩容的目标副本数,最小值0(缩容到0)')
|
|
113997
114301
|
})).optional().describe('定时扩缩容配置数组,用于配置服务的定时自动扩缩容策略。可配置多个时间段的扩缩容计划,支持每日/每周/每月循环'),
|
|
113998
114302
|
VpcConf: zod_1.z.object({
|
|
113999
|
-
VpcId: zod_1.z.string().describe('VPC网络ID
|
|
114000
|
-
SubnetId: zod_1.z.string().describe('子网ID
|
|
114001
|
-
}).optional().describe('VPC
|
|
114303
|
+
VpcId: zod_1.z.string().describe('VPC网络ID,格式如 vpc-xxxxxxxx。必须与目标数据库/Redis 处于同一地域,并优先选择同一 VPC。禁止猜测或使用占位符;须来自数据库控制台、已有资源详情、callCloudApi 或用户确认。建议首次创建即配置;已存在服务也可在 deploy 时传入,部署后必须用 queryCloudRun detail 复核是否生效'),
|
|
114304
|
+
SubnetId: zod_1.z.string().describe('子网ID,格式如 subnet-xxxxxxxx。云托管实例将占用该子网 IP,需确保有足够可用 IP'),
|
|
114305
|
+
}).optional().describe('VPC网络配置(实例出网/私有网络)。用于让云托管实例接入指定 VPC,从而内网访问 MySQL/PostgreSQL/Redis/CVM 等资源。与 OpenAccessTypes(外部如何访问本服务)是不同概念。TCP 连库场景必须配置。禁止猜测 VpcId/SubnetId,须来自数据库控制台、已有资源详情、callCloudApi 或用户确认。MCP 在创建时会将 VpcConf 映射为 SDK vpcInfo(CreateType=2);更新后必须用 queryCloudRun detail 复核 ServerConfig.VpcConf,未生效再走控制台网络设置或删建兜底'),
|
|
114002
114306
|
VolumesConf: zod_1.z.array(zod_1.z.object({
|
|
114003
114307
|
VolumeName: zod_1.z.string().describe('存储卷名称'),
|
|
114004
114308
|
VolumeType: zod_1.z.string().describe('存储卷类型,如CFS表示云文件存储'),
|
|
@@ -114102,6 +114406,74 @@ function buildManageCloudRunErrorMessage(action, serverName, error) {
|
|
|
114102
114406
|
}
|
|
114103
114407
|
return `[manageCloudRun/${action}] ${baseMessage}\n建议:${suggestions.join(" ")}`;
|
|
114104
114408
|
}
|
|
114409
|
+
const CLOUDRUN_DB_ENV_KEY_PATTERN = /^(DATABASE_URL|DB_HOST|DB_PORT|DB_USER|DB_PASSWORD|DB_NAME|MYSQL_|POSTGRES_|PGHOST|PGPORT|PGUSER|PGPASSWORD|PGDATABASE|PG_|REDIS_|MONGO_|MONGODB_|SQLALCHEMY_DATABASE_URI|SPRING_DATASOURCE_)/i;
|
|
114410
|
+
const CLOUDRUN_DB_URL_PATTERN = /(mysql|mariadb|postgres|postgresql|mongodb(\+srv)?|redis|rediss):\/\//i;
|
|
114411
|
+
/**
|
|
114412
|
+
* Detect likely TCP database/cache env usage without VpcConf.
|
|
114413
|
+
* Soft signal for AI agents — does not block deploy.
|
|
114414
|
+
*/
|
|
114415
|
+
function detectCloudRunDbNetworkRisk(options) {
|
|
114416
|
+
const vpcId = options.vpcConf?.VpcId?.trim();
|
|
114417
|
+
const subnetId = options.vpcConf?.SubnetId?.trim();
|
|
114418
|
+
if (vpcId && subnetId) {
|
|
114419
|
+
return null;
|
|
114420
|
+
}
|
|
114421
|
+
const rawEnv = options.envParams?.trim();
|
|
114422
|
+
if (!rawEnv) {
|
|
114423
|
+
return null;
|
|
114424
|
+
}
|
|
114425
|
+
let parsed;
|
|
114426
|
+
try {
|
|
114427
|
+
const value = JSON.parse(rawEnv);
|
|
114428
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
114429
|
+
return null;
|
|
114430
|
+
}
|
|
114431
|
+
parsed = value;
|
|
114432
|
+
}
|
|
114433
|
+
catch {
|
|
114434
|
+
// Non-JSON EnvParams still may embed connection URLs as plain text.
|
|
114435
|
+
if (!CLOUDRUN_DB_URL_PATTERN.test(rawEnv)) {
|
|
114436
|
+
return null;
|
|
114437
|
+
}
|
|
114438
|
+
return {
|
|
114439
|
+
code: "MISSING_VPC_FOR_DB_ENV",
|
|
114440
|
+
message: "EnvParams appears to include a database/cache connection URL, but serverConfig.VpcConf is missing. CloudRun instances usually cannot reach VPC-private MySQL/PostgreSQL/Redis without VpcConf.",
|
|
114441
|
+
matchedKeys: ["<non-json-envParams>"],
|
|
114442
|
+
remediation: [
|
|
114443
|
+
"Set serverConfig.VpcConf to the same region/VPC (and a subnet with free IPs) as the database.",
|
|
114444
|
+
"Do NOT invent VpcId/SubnetId. Resolve real IDs from the DB console, resource detail, callCloudApi, or the user.",
|
|
114445
|
+
"Use the database private/intranet hostname in EnvParams, not localhost or docker-compose service names.",
|
|
114446
|
+
"Ensure the DB security group allows the CloudRun subnet on the DB port.",
|
|
114447
|
+
"Re-deploy after VpcConf is set. OpenAccessTypes alone does not provide VPC egress to databases.",
|
|
114448
|
+
],
|
|
114449
|
+
};
|
|
114450
|
+
}
|
|
114451
|
+
const matchedKeys = [];
|
|
114452
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
114453
|
+
if (CLOUDRUN_DB_ENV_KEY_PATTERN.test(key)) {
|
|
114454
|
+
matchedKeys.push(key);
|
|
114455
|
+
continue;
|
|
114456
|
+
}
|
|
114457
|
+
if (typeof value === "string" && CLOUDRUN_DB_URL_PATTERN.test(value)) {
|
|
114458
|
+
matchedKeys.push(key);
|
|
114459
|
+
}
|
|
114460
|
+
}
|
|
114461
|
+
if (matchedKeys.length === 0) {
|
|
114462
|
+
return null;
|
|
114463
|
+
}
|
|
114464
|
+
return {
|
|
114465
|
+
code: "MISSING_VPC_FOR_DB_ENV",
|
|
114466
|
+
message: "EnvParams includes database/cache connection settings, but serverConfig.VpcConf is missing. Deploy may succeed while runtime DB connections fail.",
|
|
114467
|
+
matchedKeys,
|
|
114468
|
+
remediation: [
|
|
114469
|
+
"Set serverConfig.VpcConf.VpcId and SubnetId to the database VPC/subnet (same region).",
|
|
114470
|
+
"Do NOT invent VpcId/SubnetId. Resolve real IDs from the DB console, resource detail, callCloudApi, or the user.",
|
|
114471
|
+
"Use the private DB endpoint in EnvParams.",
|
|
114472
|
+
"Confirm security group / allowlist permits CloudRun subnet access to the DB port.",
|
|
114473
|
+
"Do not confuse OpenAccessTypes (ingress) with VpcConf (egress to VPC resources).",
|
|
114474
|
+
],
|
|
114475
|
+
};
|
|
114476
|
+
}
|
|
114105
114477
|
function getCloudRunQueryServerName(input) {
|
|
114106
114478
|
return input.detailServerName || input.serverName;
|
|
114107
114479
|
}
|
|
@@ -114599,6 +114971,17 @@ for await (let x of res.textStream) {
|
|
|
114599
114971
|
if (input.serverConfig) {
|
|
114600
114972
|
deployParams.serverConfig = input.serverConfig;
|
|
114601
114973
|
}
|
|
114974
|
+
// Manager SDK create path prefers top-level vpcInfo (CreateCloudRunServer.VpcInfo).
|
|
114975
|
+
// Map serverConfig.VpcConf → vpcInfo so first-time create actually binds VPC.
|
|
114976
|
+
// Update path still sends Items.VpcConf; platform may ignore VPC changes on existing services.
|
|
114977
|
+
const vpcConf = input.serverConfig?.VpcConf;
|
|
114978
|
+
if (vpcConf?.VpcId?.trim() && vpcConf?.SubnetId?.trim()) {
|
|
114979
|
+
deployParams.vpcInfo = {
|
|
114980
|
+
VpcId: vpcConf.VpcId.trim(),
|
|
114981
|
+
CreateType: 2,
|
|
114982
|
+
SubnetIds: [vpcConf.SubnetId.trim()],
|
|
114983
|
+
};
|
|
114984
|
+
}
|
|
114602
114985
|
let result;
|
|
114603
114986
|
try {
|
|
114604
114987
|
result = await cloudrunService.deploy(deployParams);
|
|
@@ -114672,6 +115055,14 @@ for await (let x of res.textStream) {
|
|
|
114672
115055
|
// Notification failure should not affect deployment flow
|
|
114673
115056
|
// Error is already logged in sendDeployNotification
|
|
114674
115057
|
}
|
|
115058
|
+
const dbNetworkRisk = detectCloudRunDbNetworkRisk({
|
|
115059
|
+
envParams: input.serverConfig?.EnvParams,
|
|
115060
|
+
vpcConf: input.serverConfig?.VpcConf,
|
|
115061
|
+
});
|
|
115062
|
+
const warnings = dbNetworkRisk ? [dbNetworkRisk] : [];
|
|
115063
|
+
const warningSuffix = dbNetworkRisk
|
|
115064
|
+
? ` Warning: ${dbNetworkRisk.message} Set serverConfig.VpcConf before relying on DB connectivity.`
|
|
115065
|
+
: "";
|
|
114675
115066
|
return {
|
|
114676
115067
|
content: [
|
|
114677
115068
|
{
|
|
@@ -114684,9 +115075,10 @@ for await (let x of res.textStream) {
|
|
|
114684
115075
|
deployPath: targetPath,
|
|
114685
115076
|
serverType: serverType,
|
|
114686
115077
|
cloudbasercGenerated: true,
|
|
114687
|
-
consoleUrl
|
|
115078
|
+
consoleUrl,
|
|
115079
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
114688
115080
|
},
|
|
114689
|
-
message: `Triggered deployment for ${serverType} service '${input.serverName}' from ${targetPath}. You can follow the progress in ${consoleUrl}`
|
|
115081
|
+
message: `Triggered deployment for ${serverType} service '${input.serverName}' from ${targetPath}. You can follow the progress in ${consoleUrl}.${warningSuffix}`
|
|
114690
115082
|
}, null, 2)
|
|
114691
115083
|
}
|
|
114692
115084
|
]
|
|
@@ -136828,6 +137220,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
136828
137220
|
};
|
|
136829
137221
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
136830
137222
|
exports.reportToolkitLifecycle = exports.reportToolCall = exports.telemetryReporter = void 0;
|
|
137223
|
+
exports.extractMcpClientInfo = extractMcpClientInfo;
|
|
137224
|
+
exports.readMcpClientInfoFromServer = readMcpClientInfoFromServer;
|
|
136831
137225
|
const crypto_1 = __importDefault(__webpack_require__(55511));
|
|
136832
137226
|
const http_1 = __importDefault(__webpack_require__(81630));
|
|
136833
137227
|
const https_1 = __importDefault(__webpack_require__(55591));
|
|
@@ -136879,7 +137273,7 @@ class TelemetryReporter {
|
|
|
136879
137273
|
const nodeVersion = process.version; // Node.js版本
|
|
136880
137274
|
const arch = os_1.default.arch(); // 系统架构
|
|
136881
137275
|
// 从构建时注入的版本号获取MCP版本信息
|
|
136882
|
-
const mcpVersion = process.env.npm_package_version || "2.
|
|
137276
|
+
const mcpVersion = process.env.npm_package_version || "2.25.0" || 0;
|
|
136883
137277
|
return {
|
|
136884
137278
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
136885
137279
|
deviceId: this.deviceId,
|
|
@@ -137061,6 +137455,49 @@ function resolveEnvId(cloudBaseOptions) {
|
|
|
137061
137455
|
}
|
|
137062
137456
|
return { envId, envIdSource };
|
|
137063
137457
|
}
|
|
137458
|
+
/**
|
|
137459
|
+
* Normalize SDK Implementation / clientInfo into telemetry-safe fields.
|
|
137460
|
+
*/
|
|
137461
|
+
function extractMcpClientInfo(clientVersion) {
|
|
137462
|
+
if (!clientVersion || typeof clientVersion !== "object") {
|
|
137463
|
+
return {};
|
|
137464
|
+
}
|
|
137465
|
+
const info = clientVersion;
|
|
137466
|
+
const name = typeof info.name === "string" ? info.name.trim() : "";
|
|
137467
|
+
const version = typeof info.version === "string" ? info.version.trim() : "";
|
|
137468
|
+
const title = typeof info.title === "string" ? info.title.trim() : "";
|
|
137469
|
+
return {
|
|
137470
|
+
...(name ? { name } : {}),
|
|
137471
|
+
...(version ? { version } : {}),
|
|
137472
|
+
...(title ? { title } : {}),
|
|
137473
|
+
};
|
|
137474
|
+
}
|
|
137475
|
+
/**
|
|
137476
|
+
* Read MCP clientInfo from an McpServer after initialize.
|
|
137477
|
+
* Returns empty object when handshake has not completed or the accessor is unavailable.
|
|
137478
|
+
*/
|
|
137479
|
+
function readMcpClientInfoFromServer(server) {
|
|
137480
|
+
try {
|
|
137481
|
+
return extractMcpClientInfo(server?.server?.getClientVersion?.());
|
|
137482
|
+
}
|
|
137483
|
+
catch {
|
|
137484
|
+
return {};
|
|
137485
|
+
}
|
|
137486
|
+
}
|
|
137487
|
+
function appendMcpClientInfoFields(eventData, clientInfo) {
|
|
137488
|
+
if (!clientInfo) {
|
|
137489
|
+
return;
|
|
137490
|
+
}
|
|
137491
|
+
if (clientInfo.name) {
|
|
137492
|
+
eventData.mcpClientName = clientInfo.name;
|
|
137493
|
+
}
|
|
137494
|
+
if (clientInfo.version) {
|
|
137495
|
+
eventData.mcpClientVersion = clientInfo.version;
|
|
137496
|
+
}
|
|
137497
|
+
if (clientInfo.title) {
|
|
137498
|
+
eventData.mcpClientTitle = clientInfo.title;
|
|
137499
|
+
}
|
|
137500
|
+
}
|
|
137064
137501
|
// 便捷方法
|
|
137065
137502
|
const reportToolCall = async (params) => {
|
|
137066
137503
|
const { nodeVersion, osType, osRelease, arch, mcpVersion } = exports.telemetryReporter.getUserAgent();
|
|
@@ -137105,6 +137542,7 @@ const reportToolCall = async (params) => {
|
|
|
137105
137542
|
if (params.ide) {
|
|
137106
137543
|
eventData.ide = params.ide;
|
|
137107
137544
|
}
|
|
137545
|
+
appendMcpClientInfoFields(eventData, params.mcpClientInfo);
|
|
137108
137546
|
// Debug: 打印最终上报参数
|
|
137109
137547
|
(0, logger_js_1.debug)('[telemetry] 工具调用上报参数', {
|
|
137110
137548
|
toolName: params.toolName,
|
|
@@ -137147,6 +137585,7 @@ const reportToolkitLifecycle = async (params) => {
|
|
|
137147
137585
|
if (params.ide) {
|
|
137148
137586
|
eventData.ide = params.ide;
|
|
137149
137587
|
}
|
|
137588
|
+
appendMcpClientInfoFields(eventData, params.mcpClientInfo);
|
|
137150
137589
|
// Debug: 打印最终上报参数
|
|
137151
137590
|
(0, logger_js_1.debug)('[telemetry] 生命周期事件上报参数', {
|
|
137152
137591
|
event: params.event,
|
|
@@ -172763,6 +173202,7 @@ const permission_1 = __webpack_require__(24786);
|
|
|
172763
173202
|
const sandbox_1 = __webpack_require__(48488);
|
|
172764
173203
|
const aiModel_1 = __webpack_require__(34800);
|
|
172765
173204
|
const monitor_1 = __webpack_require__(8901);
|
|
173205
|
+
const integration_1 = __webpack_require__(72785);
|
|
172766
173206
|
class Environment {
|
|
172767
173207
|
constructor(context, envId) {
|
|
172768
173208
|
this.inited = false;
|
|
@@ -172789,6 +173229,7 @@ class Environment {
|
|
|
172789
173229
|
this.sandboxService = new sandbox_1.SandboxService(this);
|
|
172790
173230
|
this.aiModelService = new aiModel_1.AiModelService(this);
|
|
172791
173231
|
this.monitorService = new monitor_1.MonitorService(this);
|
|
173232
|
+
this.integrationService = new integration_1.IntegrationService(this);
|
|
172792
173233
|
}
|
|
172793
173234
|
async lazyInit() {
|
|
172794
173235
|
if (!this.inited) {
|
|
@@ -172866,6 +173307,9 @@ class Environment {
|
|
|
172866
173307
|
getMonitorService() {
|
|
172867
173308
|
return this.monitorService;
|
|
172868
173309
|
}
|
|
173310
|
+
getIntegrationService() {
|
|
173311
|
+
return this.integrationService;
|
|
173312
|
+
}
|
|
172869
173313
|
getCommonService(serviceType = 'tcb', serviceVersion) {
|
|
172870
173314
|
return new common_1.CommonService(this, serviceType, serviceVersion);
|
|
172871
173315
|
}
|
|
@@ -194204,10 +194648,13 @@ function parseCommaParts(str) {
|
|
|
194204
194648
|
return parts;
|
|
194205
194649
|
}
|
|
194206
194650
|
|
|
194207
|
-
function expandTop(str) {
|
|
194651
|
+
function expandTop(str, options) {
|
|
194208
194652
|
if (!str)
|
|
194209
194653
|
return [];
|
|
194210
194654
|
|
|
194655
|
+
options = options || {};
|
|
194656
|
+
var max = options.max == null ? Infinity : options.max;
|
|
194657
|
+
|
|
194211
194658
|
// I don't know why Bash 4.3 does this, but it does.
|
|
194212
194659
|
// Anything starting with {} will have the first two bytes preserved
|
|
194213
194660
|
// but *only* at the top level, so {},a}b will not expand to anything,
|
|
@@ -194218,7 +194665,7 @@ function expandTop(str) {
|
|
|
194218
194665
|
str = '\\{\\}' + str.substr(2);
|
|
194219
194666
|
}
|
|
194220
194667
|
|
|
194221
|
-
return expand(escapeBraces(str), true).map(unescapeBraces);
|
|
194668
|
+
return expand(escapeBraces(str), max, true).map(unescapeBraces);
|
|
194222
194669
|
}
|
|
194223
194670
|
|
|
194224
194671
|
function identity(e) {
|
|
@@ -194239,110 +194686,115 @@ function gte(i, y) {
|
|
|
194239
194686
|
return i >= y;
|
|
194240
194687
|
}
|
|
194241
194688
|
|
|
194242
|
-
function expand(str, isTop) {
|
|
194689
|
+
function expand(str, max, isTop) {
|
|
194243
194690
|
var expansions = [];
|
|
194244
194691
|
|
|
194245
|
-
|
|
194246
|
-
|
|
194692
|
+
// The `{a},b}` rewrite below restarts expansion on a rewritten string with
|
|
194693
|
+
// the same `max` and `isTop = true`. Loop instead of recursing so a long run
|
|
194694
|
+
// of non-expanding `{}` groups can't exhaust the call stack.
|
|
194695
|
+
for (;;) {
|
|
194696
|
+
var m = balanced('{', '}', str);
|
|
194697
|
+
if (!m || /\$$/.test(m.pre)) return [str];
|
|
194247
194698
|
|
|
194248
|
-
|
|
194249
|
-
|
|
194250
|
-
|
|
194251
|
-
|
|
194252
|
-
|
|
194253
|
-
|
|
194254
|
-
|
|
194255
|
-
|
|
194256
|
-
|
|
194699
|
+
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
|
|
194700
|
+
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
|
|
194701
|
+
var isSequence = isNumericSequence || isAlphaSequence;
|
|
194702
|
+
var isOptions = m.body.indexOf(',') >= 0;
|
|
194703
|
+
if (!isSequence && !isOptions) {
|
|
194704
|
+
// {a},b}
|
|
194705
|
+
if (m.post.match(/,(?!,).*\}/)) {
|
|
194706
|
+
str = m.pre + '{' + m.body + escClose + m.post;
|
|
194707
|
+
isTop = true
|
|
194708
|
+
continue
|
|
194709
|
+
}
|
|
194710
|
+
return [str];
|
|
194257
194711
|
}
|
|
194258
|
-
return [str];
|
|
194259
|
-
}
|
|
194260
194712
|
|
|
194261
|
-
|
|
194262
|
-
|
|
194263
|
-
|
|
194264
|
-
|
|
194265
|
-
|
|
194266
|
-
if (n.length === 1) {
|
|
194267
|
-
// x{{a,b}}y ==> x{a}y x{b}y
|
|
194268
|
-
n = expand(n[0], false).map(embrace);
|
|
194713
|
+
var n;
|
|
194714
|
+
if (isSequence) {
|
|
194715
|
+
n = m.body.split(/\.\./);
|
|
194716
|
+
} else {
|
|
194717
|
+
n = parseCommaParts(m.body);
|
|
194269
194718
|
if (n.length === 1) {
|
|
194270
|
-
|
|
194271
|
-
|
|
194272
|
-
|
|
194273
|
-
|
|
194274
|
-
|
|
194275
|
-
|
|
194719
|
+
// x{{a,b}}y ==> x{a}y x{b}y
|
|
194720
|
+
n = expand(n[0], max, false).map(embrace);
|
|
194721
|
+
if (n.length === 1) {
|
|
194722
|
+
var post = m.post.length
|
|
194723
|
+
? expand(m.post, max, false)
|
|
194724
|
+
: [''];
|
|
194725
|
+
return post.map(function(p) {
|
|
194726
|
+
return m.pre + n[0] + p;
|
|
194727
|
+
});
|
|
194728
|
+
}
|
|
194276
194729
|
}
|
|
194277
194730
|
}
|
|
194278
|
-
}
|
|
194279
194731
|
|
|
194280
|
-
|
|
194281
|
-
|
|
194732
|
+
// at this point, n is the parts, and we know it's not a comma set
|
|
194733
|
+
// with a single entry.
|
|
194282
194734
|
|
|
194283
|
-
|
|
194284
|
-
|
|
194285
|
-
|
|
194286
|
-
|
|
194287
|
-
|
|
194288
|
-
|
|
194289
|
-
|
|
194290
|
-
|
|
194291
|
-
|
|
194292
|
-
|
|
194293
|
-
|
|
194294
|
-
|
|
194295
|
-
|
|
194296
|
-
|
|
194297
|
-
|
|
194298
|
-
|
|
194299
|
-
|
|
194300
|
-
|
|
194301
|
-
|
|
194302
|
-
|
|
194303
|
-
|
|
194304
|
-
|
|
194305
|
-
|
|
194306
|
-
|
|
194307
|
-
|
|
194308
|
-
|
|
194309
|
-
|
|
194310
|
-
|
|
194311
|
-
|
|
194312
|
-
|
|
194313
|
-
|
|
194314
|
-
|
|
194315
|
-
|
|
194316
|
-
|
|
194317
|
-
|
|
194318
|
-
|
|
194319
|
-
|
|
194320
|
-
|
|
194321
|
-
|
|
194322
|
-
|
|
194323
|
-
|
|
194735
|
+
// no need to expand pre, since it is guaranteed to be free of brace-sets
|
|
194736
|
+
var pre = m.pre;
|
|
194737
|
+
var post = m.post.length
|
|
194738
|
+
? expand(m.post, max, false)
|
|
194739
|
+
: [''];
|
|
194740
|
+
|
|
194741
|
+
var N;
|
|
194742
|
+
|
|
194743
|
+
if (isSequence) {
|
|
194744
|
+
var x = numeric(n[0]);
|
|
194745
|
+
var y = numeric(n[1]);
|
|
194746
|
+
var width = Math.max(n[0].length, n[1].length)
|
|
194747
|
+
var incr = n.length == 3
|
|
194748
|
+
? Math.max(Math.abs(numeric(n[2])), 1)
|
|
194749
|
+
: 1;
|
|
194750
|
+
var test = lte;
|
|
194751
|
+
var reverse = y < x;
|
|
194752
|
+
if (reverse) {
|
|
194753
|
+
incr *= -1;
|
|
194754
|
+
test = gte;
|
|
194755
|
+
}
|
|
194756
|
+
var pad = n.some(isPadded);
|
|
194757
|
+
|
|
194758
|
+
N = [];
|
|
194759
|
+
|
|
194760
|
+
for (var i = x; test(i, y) && N.length < max; i += incr) {
|
|
194761
|
+
var c;
|
|
194762
|
+
if (isAlphaSequence) {
|
|
194763
|
+
c = String.fromCharCode(i);
|
|
194764
|
+
if (c === '\\')
|
|
194765
|
+
c = '';
|
|
194766
|
+
} else {
|
|
194767
|
+
c = String(i);
|
|
194768
|
+
if (pad) {
|
|
194769
|
+
var need = width - c.length;
|
|
194770
|
+
if (need > 0) {
|
|
194771
|
+
var z = new Array(need + 1).join('0');
|
|
194772
|
+
if (i < 0)
|
|
194773
|
+
c = '-' + z + c.slice(1);
|
|
194774
|
+
else
|
|
194775
|
+
c = z + c;
|
|
194776
|
+
}
|
|
194324
194777
|
}
|
|
194325
194778
|
}
|
|
194779
|
+
N.push(c);
|
|
194326
194780
|
}
|
|
194327
|
-
|
|
194781
|
+
} else {
|
|
194782
|
+
N = concatMap(n, function(el) { return expand(el, max, false) });
|
|
194328
194783
|
}
|
|
194329
|
-
} else {
|
|
194330
|
-
N = concatMap(n, function(el) { return expand(el, false) });
|
|
194331
|
-
}
|
|
194332
194784
|
|
|
194333
|
-
|
|
194334
|
-
|
|
194335
|
-
|
|
194336
|
-
|
|
194337
|
-
|
|
194785
|
+
for (var j = 0; j < N.length; j++) {
|
|
194786
|
+
for (var k = 0; k < post.length && expansions.length < max; k++) {
|
|
194787
|
+
var expansion = pre + N[j] + post[k];
|
|
194788
|
+
if (!isTop || isSequence || expansion)
|
|
194789
|
+
expansions.push(expansion);
|
|
194790
|
+
}
|
|
194338
194791
|
}
|
|
194339
|
-
}
|
|
194340
194792
|
|
|
194341
|
-
|
|
194793
|
+
return expansions;
|
|
194794
|
+
}
|
|
194342
194795
|
}
|
|
194343
194796
|
|
|
194344
194797
|
|
|
194345
|
-
|
|
194346
194798
|
/***/ }),
|
|
194347
194799
|
|
|
194348
194800
|
/***/ 69027:
|
|
@@ -196917,7 +197369,7 @@ ${envIdSection}
|
|
|
196917
197369
|
## 环境信息
|
|
196918
197370
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
196919
197371
|
- Node.js版本: ${process.version}
|
|
196920
|
-
- MCP 版本:${process.env.npm_package_version || "2.
|
|
197372
|
+
- MCP 版本:${process.env.npm_package_version || "2.25.0" || 0}
|
|
196921
197373
|
- 系统架构: ${os_1.default.arch()}
|
|
196922
197374
|
- 时间: ${new Date().toISOString()}
|
|
196923
197375
|
- 请求ID: ${requestId}
|
|
@@ -196998,7 +197450,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
196998
197450
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
196999
197451
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
197000
197452
|
});
|
|
197001
|
-
const mcpVersion = true ? "2.
|
|
197453
|
+
const mcpVersion = true ? "2.25.0" : 0;
|
|
197002
197454
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
197003
197455
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
197004
197456
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -197035,7 +197487,8 @@ function createWrappedHandler(name, handler, server) {
|
|
|
197035
197487
|
error: errorMessage,
|
|
197036
197488
|
inputParams: sanitizeArgs(args), // 添加入参上报
|
|
197037
197489
|
cloudBaseOptions: cloudBaseOptions, // 传递 CloudBase 配置(可能已更新)
|
|
197038
|
-
ide: server.ide || process.env.INTEGRATION_IDE // 传递集成IDE信息
|
|
197490
|
+
ide: server.ide || process.env.INTEGRATION_IDE, // 传递集成IDE信息
|
|
197491
|
+
mcpClientInfo: (0, telemetry_js_1.readMcpClientInfoFromServer)(server),
|
|
197039
197492
|
}).catch(err => {
|
|
197040
197493
|
// 静默处理上报错误,不影响主要功能
|
|
197041
197494
|
(0, logger_js_1.debug)('遥测上报失败', { toolName: name, error: err instanceof Error ? err.message : String(err) });
|
|
@@ -199028,6 +199481,7 @@ module.exports = cmp
|
|
|
199028
199481
|
|
|
199029
199482
|
"use strict";
|
|
199030
199483
|
|
|
199484
|
+
/* eslint-disable @typescript-eslint/member-ordering */
|
|
199031
199485
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
199032
199486
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
199033
199487
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -199270,27 +199724,30 @@ class StorageService {
|
|
|
199270
199724
|
getMetadataController.loadTasks(tasks);
|
|
199271
199725
|
const files = await getMetadataController.run();
|
|
199272
199726
|
// 对文件上传进行处理
|
|
199273
|
-
|
|
199274
|
-
const
|
|
199275
|
-
|
|
199276
|
-
|
|
199277
|
-
|
|
199727
|
+
// 构造 files 数组(格式:{ localPath, cloudPath }),传给 uploadFilesCustom
|
|
199728
|
+
const filesForUpload = fileStatsList
|
|
199729
|
+
.filter(stats => !stats.isDir)
|
|
199730
|
+
.map(stats => ({
|
|
199731
|
+
localPath: stats.filePath,
|
|
199732
|
+
cloudPath: stats.cloudFileKey
|
|
199733
|
+
}));
|
|
199734
|
+
// 调用 uploadFilesCustom(智能混合模式:并行→串行)
|
|
199735
|
+
// 不再直接调用 uploadFilesWithRetry(纯并行重试,容易 socket hang up)
|
|
199736
|
+
return this.uploadFilesCustom({
|
|
199737
|
+
files: filesForUpload,
|
|
199738
|
+
bucket,
|
|
199739
|
+
region,
|
|
199740
|
+
ignore: [],
|
|
199278
199741
|
onProgress,
|
|
199279
|
-
onFileFinish
|
|
199280
|
-
|
|
199281
|
-
|
|
199282
|
-
|
|
199283
|
-
|
|
199284
|
-
times: retryCount,
|
|
199285
|
-
interval: retryInterval,
|
|
199286
|
-
failedFiles: []
|
|
199742
|
+
onFileFinish,
|
|
199743
|
+
fileId,
|
|
199744
|
+
parallel,
|
|
199745
|
+
retryCount,
|
|
199746
|
+
retryInterval
|
|
199287
199747
|
});
|
|
199288
199748
|
}
|
|
199289
|
-
/**
|
|
199290
|
-
* 批量上传文件
|
|
199291
|
-
* @param options
|
|
199292
|
-
*/
|
|
199293
199749
|
async uploadFilesCustom(options) {
|
|
199750
|
+
var _a;
|
|
199294
199751
|
const { files, bucket, region, ignore, onProgress, onFileFinish, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
|
|
199295
199752
|
if (!files || !files.length) {
|
|
199296
199753
|
return;
|
|
@@ -199304,6 +199761,146 @@ class StorageService {
|
|
|
199304
199761
|
};
|
|
199305
199762
|
})
|
|
199306
199763
|
.filter(item => ((ignore === null || ignore === void 0 ? void 0 : ignore.length) ? !micromatch_1.default.isMatch(item.filePath, ignore) : true));
|
|
199764
|
+
const totalFiles = fileList.length;
|
|
199765
|
+
console.log(`[uploadFiles] 准备上传 ${totalFiles} 个文件`);
|
|
199766
|
+
// 模式 1:完全串行模式(最稳定)
|
|
199767
|
+
if (process.env.COS_UPLOAD_SERIAL === 'true') {
|
|
199768
|
+
console.log(`[uploadFiles] 串行安全模式已启用`);
|
|
199769
|
+
const serialResult = await this.uploadFilesSerial({
|
|
199770
|
+
fileList,
|
|
199771
|
+
bucket,
|
|
199772
|
+
region,
|
|
199773
|
+
fileId,
|
|
199774
|
+
retryCount,
|
|
199775
|
+
retryInterval,
|
|
199776
|
+
onFileFinish
|
|
199777
|
+
});
|
|
199778
|
+
if (serialResult.failedFiles.length > 0) {
|
|
199779
|
+
throw new error_1.CloudBaseError(`[uploadFiles] 串行上传仍失败 (${serialResult.failedFiles.length} 个):\n` +
|
|
199780
|
+
serialResult.failedFiles.map(f => `- ${f.cloudFileKey}: ${f.error}`).join('\n'), { code: 'HOSTING_DEPLOY_FAILED' });
|
|
199781
|
+
}
|
|
199782
|
+
return {
|
|
199783
|
+
successCount: serialResult.successCount,
|
|
199784
|
+
totalFiles,
|
|
199785
|
+
failedFiles: []
|
|
199786
|
+
};
|
|
199787
|
+
}
|
|
199788
|
+
// 模式 2:智能混合模式(默认)
|
|
199789
|
+
// - 先并行快速上传
|
|
199790
|
+
// - 失败的文件自动走串行重试
|
|
199791
|
+
console.log(`[uploadFiles] 智能混合模式:先并行快速上传,失败文件自动串行重试`);
|
|
199792
|
+
// 第一步:并行上传(分批)
|
|
199793
|
+
const parallelFailedFiles = await this.uploadFilesParallel({
|
|
199794
|
+
fileList,
|
|
199795
|
+
bucket,
|
|
199796
|
+
region,
|
|
199797
|
+
fileId,
|
|
199798
|
+
parallel,
|
|
199799
|
+
retryCount: 0, // 并行阶段不重试,失败后交给串行阶段
|
|
199800
|
+
retryInterval,
|
|
199801
|
+
onFileFinish
|
|
199802
|
+
});
|
|
199803
|
+
// 第二步:串行重试失败的文件
|
|
199804
|
+
if (parallelFailedFiles && parallelFailedFiles.length > 0) {
|
|
199805
|
+
console.warn(`[uploadFiles] 并行阶段 ${parallelFailedFiles.length} 个文件失败,自动切换串行重试...`);
|
|
199806
|
+
const serialResult = await this.uploadFilesSerial({
|
|
199807
|
+
fileList: parallelFailedFiles,
|
|
199808
|
+
bucket,
|
|
199809
|
+
region,
|
|
199810
|
+
fileId: false, // 串行重试不需要再获取 fileId
|
|
199811
|
+
retryCount,
|
|
199812
|
+
retryInterval,
|
|
199813
|
+
onFileFinish
|
|
199814
|
+
});
|
|
199815
|
+
// 检查是否还有失败
|
|
199816
|
+
if (((_a = serialResult === null || serialResult === void 0 ? void 0 : serialResult.failedFiles) === null || _a === void 0 ? void 0 : _a.length) > 0) {
|
|
199817
|
+
throw new error_1.CloudBaseError(`[uploadFiles] 串行重试后仍失败 (${serialResult.failedFiles.length} 个):\n` +
|
|
199818
|
+
serialResult.failedFiles.map(f => `- ${f.cloudFileKey}: ${f.error}`).join('\n'), { code: 'HOSTING_DEPLOY_FAILED' });
|
|
199819
|
+
}
|
|
199820
|
+
}
|
|
199821
|
+
console.log(`[uploadFiles] 全部完成!共处理 ${totalFiles} 个文件`);
|
|
199822
|
+
return {
|
|
199823
|
+
successCount: totalFiles,
|
|
199824
|
+
totalFiles,
|
|
199825
|
+
failedFiles: []
|
|
199826
|
+
};
|
|
199827
|
+
}
|
|
199828
|
+
/**
|
|
199829
|
+
* 串行安全模式:逐文件上传(类似自定义脚本 deploy.js 的方式)
|
|
199830
|
+
* 特点:
|
|
199831
|
+
* - 每个文件独立 HTTP 连接,不会出现 socket hang up
|
|
199832
|
+
* - 单线程串行,稳定性最高
|
|
199833
|
+
* - 适合大规模目录(>1000 文件)或有网络问题的场景
|
|
199834
|
+
*/
|
|
199835
|
+
// eslint-disable-next-line @typescript-eslint/member-ordering
|
|
199836
|
+
async uploadFilesSerial(options) {
|
|
199837
|
+
const { fileList, bucket, region, fileId, retryCount, retryInterval, onFileFinish } = options;
|
|
199838
|
+
let uploadedCount = 0;
|
|
199839
|
+
let failedFiles = [];
|
|
199840
|
+
for (let i = 0; i < fileList.length; i++) {
|
|
199841
|
+
const file = fileList[i];
|
|
199842
|
+
let success = false;
|
|
199843
|
+
// 重试机制
|
|
199844
|
+
for (let attempt = 0; attempt <= retryCount; attempt++) {
|
|
199845
|
+
try {
|
|
199846
|
+
// 每个文件创建新的 COS 实例(确保连接隔离)
|
|
199847
|
+
const cos = this.getCos(1); // 严格单并发
|
|
199848
|
+
const putObject = util_1.default.promisify(cos.putObject).bind(cos);
|
|
199849
|
+
await putObject({
|
|
199850
|
+
Bucket: bucket,
|
|
199851
|
+
Region: region,
|
|
199852
|
+
Key: file.cloudFileKey,
|
|
199853
|
+
Body: fs_1.default.createReadStream(file.filePath),
|
|
199854
|
+
ContentLength: fs_1.default.statSync(file.filePath).size,
|
|
199855
|
+
});
|
|
199856
|
+
uploadedCount++;
|
|
199857
|
+
// 显示进度(每 100 个文件或最后 5 个)
|
|
199858
|
+
if (uploadedCount % 100 === 0 || fileList.length - i <= 5) {
|
|
199859
|
+
console.log(`[serial-upload] [${uploadedCount}/${fileList.length}] ${file.cloudFileKey}`);
|
|
199860
|
+
}
|
|
199861
|
+
success = true;
|
|
199862
|
+
// 回调成功
|
|
199863
|
+
if (onFileFinish) {
|
|
199864
|
+
onFileFinish(null, {}, file);
|
|
199865
|
+
}
|
|
199866
|
+
break;
|
|
199867
|
+
}
|
|
199868
|
+
catch (error) {
|
|
199869
|
+
const isLastAttempt = attempt === retryCount;
|
|
199870
|
+
if (isLastAttempt) {
|
|
199871
|
+
console.error(`[serial-upload] [${i + 1}/${fileList.length}] ${file.cloudFileKey}: ${error.message || error.code}`);
|
|
199872
|
+
// 记录失败的文件
|
|
199873
|
+
failedFiles.push(Object.assign(Object.assign({}, file), { error: error.message || String(error) }));
|
|
199874
|
+
// 回调失败
|
|
199875
|
+
if (onFileFinish) {
|
|
199876
|
+
onFileFinish(error, null, file);
|
|
199877
|
+
}
|
|
199878
|
+
}
|
|
199879
|
+
else {
|
|
199880
|
+
// 等待后重试
|
|
199881
|
+
await new Promise(resolve => { setTimeout(resolve, retryInterval); });
|
|
199882
|
+
}
|
|
199883
|
+
}
|
|
199884
|
+
}
|
|
199885
|
+
}
|
|
199886
|
+
console.log(`[serial-upload] 完成:成功 ${uploadedCount}/${fileList.length},失败 ${failedFiles.length}`);
|
|
199887
|
+
return {
|
|
199888
|
+
successCount: uploadedCount,
|
|
199889
|
+
failedFiles
|
|
199890
|
+
};
|
|
199891
|
+
}
|
|
199892
|
+
/**
|
|
199893
|
+
* 并发模式:使用 cos.uploadFiles() 批量上传
|
|
199894
|
+
* 特点:
|
|
199895
|
+
* - 速度快,利用 SDK 内部并行能力
|
|
199896
|
+
* - 分批上传 + 自动刷新凭证
|
|
199897
|
+
* - 适合小规模目录或网络良好的场景
|
|
199898
|
+
*
|
|
199899
|
+
* @returns 失败的文件列表(空的表示全部成功)
|
|
199900
|
+
*/
|
|
199901
|
+
// eslint-disable-next-line @typescript-eslint/member-ordering
|
|
199902
|
+
async uploadFilesParallel(options) {
|
|
199903
|
+
const { fileList, bucket, region, fileId, parallel, retryCount, retryInterval, onProgress, onFileFinish } = options;
|
|
199307
199904
|
// 生成上传文件属性
|
|
199308
199905
|
const tasks = fileList.map(stats => async () => {
|
|
199309
199906
|
let cosFileId;
|
|
@@ -199319,31 +199916,89 @@ class StorageService {
|
|
|
199319
199916
|
'x-cos-meta-fileid': cosFileId
|
|
199320
199917
|
};
|
|
199321
199918
|
});
|
|
199322
|
-
//
|
|
199919
|
+
// 控制请求并发(获取 metadata 阶段)
|
|
199323
199920
|
const asyncTaskController = new parallel_1.AsyncTaskParallelController(parallel, 50);
|
|
199324
199921
|
asyncTaskController.loadTasks(tasks);
|
|
199325
|
-
|
|
199326
|
-
|
|
199327
|
-
|
|
199328
|
-
const
|
|
199329
|
-
|
|
199330
|
-
|
|
199331
|
-
|
|
199332
|
-
|
|
199333
|
-
|
|
199334
|
-
|
|
199335
|
-
|
|
199336
|
-
|
|
199337
|
-
|
|
199338
|
-
|
|
199339
|
-
|
|
199340
|
-
|
|
199341
|
-
|
|
199342
|
-
|
|
199343
|
-
|
|
199344
|
-
|
|
199345
|
-
|
|
199346
|
-
|
|
199922
|
+
const processedFileList = await asyncTaskController.run();
|
|
199923
|
+
// 分批上传,避免单次上传时间过长导致 STS 临时凭证过期
|
|
199924
|
+
// 默认每批 500 个文件,可通过环境变量 COS_UPLOAD_BATCH_SIZE 自定义
|
|
199925
|
+
const BATCH_SIZE = process.env.COS_UPLOAD_BATCH_SIZE
|
|
199926
|
+
? parseInt(process.env.COS_UPLOAD_BATCH_SIZE, 10)
|
|
199927
|
+
: 500;
|
|
199928
|
+
// 收集所有批次失败的文件
|
|
199929
|
+
let allFailedFiles = [];
|
|
199930
|
+
for (let batchStart = 0; batchStart < processedFileList.length; batchStart += BATCH_SIZE) {
|
|
199931
|
+
const batchEnd = Math.min(batchStart + BATCH_SIZE, processedFileList.length);
|
|
199932
|
+
const batchFiles = processedFileList.slice(batchStart, batchEnd);
|
|
199933
|
+
console.log(`[parallel-upload] 正在上传第 ${Math.floor(batchStart / BATCH_SIZE) + 1} 批次 (文件 ${batchStart + 1}-${batchEnd}/${processedFileList.length})`);
|
|
199934
|
+
// 每批次重新获取 COS 实例(刷新 STS 临时凭证)
|
|
199935
|
+
const cos = this.getCos(parallel);
|
|
199936
|
+
const uploadFiles = util_1.default.promisify(cos.uploadFiles).bind(cos);
|
|
199937
|
+
// 跟踪本批次中真正失败的文件(通过 onFileFinish 回调)
|
|
199938
|
+
const batchFailedFiles = [];
|
|
199939
|
+
const batchSuccessFiles = new Set();
|
|
199940
|
+
const params = {
|
|
199941
|
+
files: batchFiles,
|
|
199942
|
+
SliceSize: BIG_FILE_SIZE,
|
|
199943
|
+
onProgress,
|
|
199944
|
+
onFileFinish: (error, result, file) => {
|
|
199945
|
+
// 空值检查:防止 file 为 null/undefined
|
|
199946
|
+
if (!file) {
|
|
199947
|
+
console.warn('[parallel-upload] onFileFinish received null/undefined file');
|
|
199948
|
+
return;
|
|
199949
|
+
}
|
|
199950
|
+
const fileKey = file.Key || file.cloudFileKey;
|
|
199951
|
+
if (error) {
|
|
199952
|
+
batchFailedFiles.push({
|
|
199953
|
+
filePath: file.FilePath || file.filePath,
|
|
199954
|
+
cloudFileKey: fileKey,
|
|
199955
|
+
error: error.message || String(error)
|
|
199956
|
+
});
|
|
199957
|
+
}
|
|
199958
|
+
else {
|
|
199959
|
+
batchSuccessFiles.add(fileKey);
|
|
199960
|
+
}
|
|
199961
|
+
// 继续调用用户传入的回调(转换参数格式)
|
|
199962
|
+
// 注意:cloudFileKey 和 filePath 是 CLI 期望的格式
|
|
199963
|
+
onFileFinish === null || onFileFinish === void 0 ? void 0 : onFileFinish(error, result, Object.assign(Object.assign({}, file), { cloudFileKey: fileKey, filePath: file.FilePath || file.filePath }));
|
|
199964
|
+
}
|
|
199965
|
+
};
|
|
199966
|
+
try {
|
|
199967
|
+
await this.uploadFilesWithRetry({
|
|
199968
|
+
uploadFiles,
|
|
199969
|
+
options: params,
|
|
199970
|
+
times: retryCount,
|
|
199971
|
+
interval: retryInterval,
|
|
199972
|
+
failedFiles: []
|
|
199973
|
+
});
|
|
199974
|
+
console.log(`[parallel-upload] 第 ${Math.floor(batchStart / BATCH_SIZE) + 1} 批次上传成功`);
|
|
199975
|
+
}
|
|
199976
|
+
catch (error) {
|
|
199977
|
+
console.error(`[parallel-upload] 第 ${Math.floor(batchStart / BATCH_SIZE) + 1} 批次上传失败:`, error.message || error.code || error);
|
|
199978
|
+
// 只记录真正失败的文件(通过 onFileFinish 回调跟踪)
|
|
199979
|
+
// 如果 onFileFinish 没有被调用(如 socket hang up),则标记整个批次为失败
|
|
199980
|
+
if (batchFailedFiles.length === 0 && batchSuccessFiles.size === 0) {
|
|
199981
|
+
// onFileFinish 未被调用,整个批次失败
|
|
199982
|
+
allFailedFiles.push(...batchFiles.map(f => ({
|
|
199983
|
+
filePath: f.FilePath || f.filePath,
|
|
199984
|
+
cloudFileKey: f.Key || f.cloudFileKey,
|
|
199985
|
+
error: error.message || String(error)
|
|
199986
|
+
})));
|
|
199987
|
+
}
|
|
199988
|
+
else {
|
|
199989
|
+
// onFileFinish 已被调用,只记录失败的文件
|
|
199990
|
+
allFailedFiles.push(...batchFailedFiles);
|
|
199991
|
+
}
|
|
199992
|
+
}
|
|
199993
|
+
}
|
|
199994
|
+
// 返回失败文件列表(不抛异常,让调用方决定是否走串行重试)
|
|
199995
|
+
if (allFailedFiles.length > 0) {
|
|
199996
|
+
console.warn(`[parallel-upload] 共 ${allFailedFiles.length}/${processedFileList.length} 个文件并行上传失败,等待串行重试...`);
|
|
199997
|
+
}
|
|
199998
|
+
else {
|
|
199999
|
+
console.log(`[parallel-upload] 全部 ${processedFileList.length} 个文件并行上传成功`);
|
|
200000
|
+
}
|
|
200001
|
+
return allFailedFiles;
|
|
199347
200002
|
}
|
|
199348
200003
|
/**
|
|
199349
200004
|
* 创建一个空的文件夹
|
|
@@ -201535,6 +202190,7 @@ class StorageService {
|
|
|
201535
202190
|
}
|
|
201536
202191
|
/**
|
|
201537
202192
|
* 获取 COS 配置
|
|
202193
|
+
* @param parallel 并发数量
|
|
201538
202194
|
*/
|
|
201539
202195
|
getCos(parallel = 20) {
|
|
201540
202196
|
const internalEndpoint = this.environment.cloudBaseContext.isInternalEndpoint();
|
|
@@ -201780,6 +202436,16 @@ __decorate([
|
|
|
201780
202436
|
], StorageService.prototype, "uploadDirectoryCustom", null);
|
|
201781
202437
|
__decorate([
|
|
201782
202438
|
(0, utils_1.preLazy)()
|
|
202439
|
+
/**
|
|
202440
|
+
* 上传文件(支持精准重试失败文件)
|
|
202441
|
+
*
|
|
202442
|
+
* 智能混合模式(默认):
|
|
202443
|
+
* 1. 先并行快速上传(速度快)
|
|
202444
|
+
* 2. 失败的文件自动走串行重试(稳定性高)
|
|
202445
|
+
*
|
|
202446
|
+
* 可选模式:
|
|
202447
|
+
* - COS_UPLOAD_SERIAL=true:完全串行模式(最稳定但慢)
|
|
202448
|
+
*/
|
|
201783
202449
|
], StorageService.prototype, "uploadFilesCustom", null);
|
|
201784
202450
|
__decorate([
|
|
201785
202451
|
(0, utils_1.preLazy)()
|
|
@@ -202590,6 +203256,367 @@ Promise.filter = function (promises, fn, options) {
|
|
|
202590
203256
|
};
|
|
202591
203257
|
|
|
202592
203258
|
|
|
203259
|
+
/***/ }),
|
|
203260
|
+
|
|
203261
|
+
/***/ 72785:
|
|
203262
|
+
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
203263
|
+
|
|
203264
|
+
"use strict";
|
|
203265
|
+
|
|
203266
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
203267
|
+
if (k2 === undefined) k2 = k;
|
|
203268
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
203269
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
203270
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
203271
|
+
}
|
|
203272
|
+
Object.defineProperty(o, k2, desc);
|
|
203273
|
+
}) : (function(o, m, k, k2) {
|
|
203274
|
+
if (k2 === undefined) k2 = k;
|
|
203275
|
+
o[k2] = m[k];
|
|
203276
|
+
}));
|
|
203277
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
203278
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
203279
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
203280
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
203281
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
203282
|
+
};
|
|
203283
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
203284
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
203285
|
+
};
|
|
203286
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
203287
|
+
exports.IntegrationService = void 0;
|
|
203288
|
+
const error_1 = __webpack_require__(40430);
|
|
203289
|
+
const utils_1 = __webpack_require__(62358);
|
|
203290
|
+
const constants_1 = __webpack_require__(79640);
|
|
203291
|
+
const crypto_1 = __webpack_require__(25068);
|
|
203292
|
+
const secretKey_1 = __webpack_require__(22573);
|
|
203293
|
+
__exportStar(__webpack_require__(18944), exports);
|
|
203294
|
+
/**
|
|
203295
|
+
* 集成中心(UserKey)服务
|
|
203296
|
+
*
|
|
203297
|
+
* 复用现有 tcb 云 API(version 2018-06-08),统一携带 Business='integration'。
|
|
203298
|
+
*
|
|
203299
|
+
* 提供两种使用模式:
|
|
203300
|
+
* 1. 透传模式:直接传 `ext`(JSON 字符串),SDK 原样传递给云 API(向后兼容)
|
|
203301
|
+
* 2. 自动加解密模式:传 `envVariables`(明文键值对),SDK 自动识别模板中的
|
|
203302
|
+
* password 字段、拉取七彩石密钥完成加密后拼装 ext
|
|
203303
|
+
*/
|
|
203304
|
+
class IntegrationService {
|
|
203305
|
+
constructor(environment) {
|
|
203306
|
+
this.environment = environment;
|
|
203307
|
+
this.tcbService = new utils_1.CloudService(environment.cloudBaseContext, 'tcb', '2018-06-08');
|
|
203308
|
+
}
|
|
203309
|
+
// =========== 只读 API ===========
|
|
203310
|
+
/** 列出集成模板(密钥对模版列表) */
|
|
203311
|
+
async listTemplates(params = {}) {
|
|
203312
|
+
var _a, _b, _c;
|
|
203313
|
+
const { envId } = this.getEnvInfo();
|
|
203314
|
+
return this.tcbService.request('GetUserKeyTemplateList', {
|
|
203315
|
+
EnvId: envId,
|
|
203316
|
+
Offset: (_a = params.offset) !== null && _a !== void 0 ? _a : 0,
|
|
203317
|
+
Limit: (_b = params.limit) !== null && _b !== void 0 ? _b : 100,
|
|
203318
|
+
AuthTypeCode: params.authTypeCode,
|
|
203319
|
+
AuthTypeName: params.authTypeName,
|
|
203320
|
+
Business: (_c = params.business) !== null && _c !== void 0 ? _c : constants_1.USER_KEY_BUSINESS // 默认 integration
|
|
203321
|
+
});
|
|
203322
|
+
}
|
|
203323
|
+
/** 列出集成实例(密钥对列表) */
|
|
203324
|
+
async listUserKeys(params = {}) {
|
|
203325
|
+
var _a, _b, _c;
|
|
203326
|
+
const { envId } = this.getEnvInfo();
|
|
203327
|
+
return this.tcbService.request('GetUserKeyList', {
|
|
203328
|
+
EnvId: envId,
|
|
203329
|
+
Offset: (_a = params.offset) !== null && _a !== void 0 ? _a : 0,
|
|
203330
|
+
Limit: (_b = params.limit) !== null && _b !== void 0 ? _b : 100,
|
|
203331
|
+
Name: params.name,
|
|
203332
|
+
AuthTypeCode: params.authTypeCode,
|
|
203333
|
+
Business: (_c = params.business) !== null && _c !== void 0 ? _c : constants_1.USER_KEY_BUSINESS // 默认 integration
|
|
203334
|
+
});
|
|
203335
|
+
}
|
|
203336
|
+
/**
|
|
203337
|
+
* 查询单个集成实例详情。
|
|
203338
|
+
*
|
|
203339
|
+
* 当 `decryptPwd=true` 时,SDK 自动拉取模板识别 password 字段并从七彩石
|
|
203340
|
+
* 获取 AES 密钥解密,Ext 中 password 字段返回明文。解密失败时保留密文原值,
|
|
203341
|
+
* 不中断流程。
|
|
203342
|
+
*/
|
|
203343
|
+
async getUserKey(params) {
|
|
203344
|
+
var _a, _b, _c;
|
|
203345
|
+
this.validateRequiredParams(params, ['keyId']);
|
|
203346
|
+
const { envId } = this.getEnvInfo();
|
|
203347
|
+
const result = await this.tcbService.request('GetOneUserKey', {
|
|
203348
|
+
EnvId: envId,
|
|
203349
|
+
KeyID: params.keyId,
|
|
203350
|
+
NeedPwd: (_a = params.needPwd) !== null && _a !== void 0 ? _a : false,
|
|
203351
|
+
Business: (_b = params.business) !== null && _b !== void 0 ? _b : constants_1.USER_KEY_BUSINESS // 默认 integration
|
|
203352
|
+
});
|
|
203353
|
+
// 自动解密 password 字段
|
|
203354
|
+
if (params.decryptPwd && ((_c = result.UserKeyInfo) === null || _c === void 0 ? void 0 : _c.Ext)) {
|
|
203355
|
+
result.UserKeyInfo = await this.decryptExtIfNeeded(result.UserKeyInfo, envId);
|
|
203356
|
+
}
|
|
203357
|
+
return result;
|
|
203358
|
+
}
|
|
203359
|
+
// =========== 写 API ===========
|
|
203360
|
+
/**
|
|
203361
|
+
* 创建集成实例。
|
|
203362
|
+
*
|
|
203363
|
+
* 支持两种传参方式:
|
|
203364
|
+
* - 传 `ext`:透传模式,原样传递给云 API(向后兼容,调用方自行加密)
|
|
203365
|
+
* - 传 `envVariables`:自动加密模式,SDK 识别模板 password 字段并加密后拼装 ext
|
|
203366
|
+
* - 两者都传时以 `ext` 优先
|
|
203367
|
+
*/
|
|
203368
|
+
async createUserKey(params) {
|
|
203369
|
+
var _a;
|
|
203370
|
+
this.validateRequiredParams(params, ['name', 'authTypeCode']);
|
|
203371
|
+
const { envId } = this.getEnvInfo();
|
|
203372
|
+
const ext = await this.buildExtFromParams(envId, params.authTypeCode, { ext: params.ext, envVariables: params.envVariables });
|
|
203373
|
+
return this.tcbService.request('CreateUserKey', {
|
|
203374
|
+
EnvId: envId,
|
|
203375
|
+
Name: params.name,
|
|
203376
|
+
KeyID: params.keyId,
|
|
203377
|
+
AuthTypeCode: params.authTypeCode,
|
|
203378
|
+
Description: params.description,
|
|
203379
|
+
Ext: ext,
|
|
203380
|
+
TicketID: params.ticketId,
|
|
203381
|
+
Business: (_a = params.business) !== null && _a !== void 0 ? _a : constants_1.USER_KEY_BUSINESS, // 默认 integration
|
|
203382
|
+
DemoCodeFunctionName: params.demoCodeFunctionName,
|
|
203383
|
+
RuntimeConfig: params.runtimeConfig
|
|
203384
|
+
});
|
|
203385
|
+
}
|
|
203386
|
+
/**
|
|
203387
|
+
* 更新集成实例。
|
|
203388
|
+
*
|
|
203389
|
+
* 支持两种传参方式:
|
|
203390
|
+
* - 传 `ext`:透传模式,原样传递给云 API(向后兼容)
|
|
203391
|
+
* - 传 `envVariables`:自动加密模式,与现有 Ext 合并后加密写入
|
|
203392
|
+
* - 两者都传时以 `ext` 优先
|
|
203393
|
+
* - 都未传时后端保留原有 Ext 不变
|
|
203394
|
+
*/
|
|
203395
|
+
async updateUserKey(params) {
|
|
203396
|
+
var _a;
|
|
203397
|
+
this.validateRequiredParams(params, ['keyId']);
|
|
203398
|
+
const { envId } = this.getEnvInfo();
|
|
203399
|
+
let ext = params.ext;
|
|
203400
|
+
// 传了 envVariables 且未传 ext:需要合并现有 Ext 后加密
|
|
203401
|
+
if (params.envVariables && !params.ext) {
|
|
203402
|
+
ext = await this.buildMergedExtForUpdate(envId, params.keyId, { authTypeCode: params.authTypeCode, envVariables: params.envVariables });
|
|
203403
|
+
}
|
|
203404
|
+
return this.tcbService.request('UpdateUserKey', {
|
|
203405
|
+
EnvId: envId,
|
|
203406
|
+
KeyID: params.keyId,
|
|
203407
|
+
Name: params.name,
|
|
203408
|
+
AuthTypeCode: params.authTypeCode,
|
|
203409
|
+
Description: params.description,
|
|
203410
|
+
Ext: ext,
|
|
203411
|
+
DemoCodeFunctionName: params.demoCodeFunctionName,
|
|
203412
|
+
Business: (_a = params.business) !== null && _a !== void 0 ? _a : constants_1.USER_KEY_BUSINESS, // 默认 integration
|
|
203413
|
+
RuntimeConfig: params.runtimeConfig
|
|
203414
|
+
});
|
|
203415
|
+
}
|
|
203416
|
+
/** 删除集成实例 */
|
|
203417
|
+
async deleteUserKey(params) {
|
|
203418
|
+
this.validateRequiredParams(params, ['keyId']);
|
|
203419
|
+
const { envId } = this.getEnvInfo();
|
|
203420
|
+
return this.tcbService.request('DeleteUserKey', {
|
|
203421
|
+
EnvId: envId,
|
|
203422
|
+
KeyID: params.keyId
|
|
203423
|
+
});
|
|
203424
|
+
}
|
|
203425
|
+
// =========== 辅助方法 ===========
|
|
203426
|
+
/** 获取环境信息 */
|
|
203427
|
+
getEnvInfo() {
|
|
203428
|
+
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
203429
|
+
return {
|
|
203430
|
+
envId: envConfig.EnvId
|
|
203431
|
+
};
|
|
203432
|
+
}
|
|
203433
|
+
/** 校验必填参数 */
|
|
203434
|
+
validateRequiredParams(params, fields) {
|
|
203435
|
+
for (const field of fields) {
|
|
203436
|
+
if (!params[field]) {
|
|
203437
|
+
throw new error_1.CloudBaseError(`${field} 是必填参数`);
|
|
203438
|
+
}
|
|
203439
|
+
}
|
|
203440
|
+
}
|
|
203441
|
+
/**
|
|
203442
|
+
* 根据 ext / envVariables 参数构造最终的 Ext JSON 字符串。
|
|
203443
|
+
* ext 优先;envVariables 由 SDK 自动加密 password 字段。
|
|
203444
|
+
*/
|
|
203445
|
+
async buildExtFromParams(envId, authTypeCode, opts) {
|
|
203446
|
+
// ext 优先(透传模式)
|
|
203447
|
+
if (opts.ext !== undefined) {
|
|
203448
|
+
return opts.ext;
|
|
203449
|
+
}
|
|
203450
|
+
// 自动加密模式
|
|
203451
|
+
if (opts.envVariables && Object.keys(opts.envVariables).length > 0) {
|
|
203452
|
+
return this.encryptAndStringify(envId, authTypeCode, opts.envVariables);
|
|
203453
|
+
}
|
|
203454
|
+
return undefined;
|
|
203455
|
+
}
|
|
203456
|
+
/**
|
|
203457
|
+
* 更新场景:将 envVariables 与现有实例的 Ext 合并后加密。
|
|
203458
|
+
* 需要先查询现有实例获取当前的 authTypeCode 和 Ext。
|
|
203459
|
+
*/
|
|
203460
|
+
async buildMergedExtForUpdate(envId, keyId, opts) {
|
|
203461
|
+
var _a, _b;
|
|
203462
|
+
const { envVariables, authTypeCode } = opts;
|
|
203463
|
+
if (!envVariables || Object.keys(envVariables).length === 0) {
|
|
203464
|
+
return undefined;
|
|
203465
|
+
}
|
|
203466
|
+
// 查询现有实例以合并 Ext
|
|
203467
|
+
let existingExtObj = {};
|
|
203468
|
+
let resolvedAuthTypeCode = authTypeCode;
|
|
203469
|
+
try {
|
|
203470
|
+
const existing = await this.getUserKey({
|
|
203471
|
+
keyId,
|
|
203472
|
+
needPwd: true,
|
|
203473
|
+
decryptPwd: true, // 解密后方便合并
|
|
203474
|
+
business: constants_1.USER_KEY_BUSINESS
|
|
203475
|
+
});
|
|
203476
|
+
const extObj = this.parseExt((_a = existing.UserKeyInfo) === null || _a === void 0 ? void 0 : _a.Ext);
|
|
203477
|
+
existingExtObj = Object.assign({}, extObj);
|
|
203478
|
+
resolvedAuthTypeCode = resolvedAuthTypeCode || ((_b = existing.UserKeyInfo) === null || _b === void 0 ? void 0 : _b.AuthTypeCode);
|
|
203479
|
+
}
|
|
203480
|
+
catch (_c) {
|
|
203481
|
+
// 查询失败时仅使用传入的 envVariables
|
|
203482
|
+
}
|
|
203483
|
+
if (!resolvedAuthTypeCode) {
|
|
203484
|
+
// 无法获取 authTypeCode,无法识别 password 字段,直接 JSON 序列化
|
|
203485
|
+
return JSON.stringify(Object.assign(Object.assign({}, existingExtObj), envVariables));
|
|
203486
|
+
}
|
|
203487
|
+
// 合并:现有 Ext 为基础,envVariables 覆盖
|
|
203488
|
+
const merged = Object.assign(Object.assign({}, existingExtObj), envVariables);
|
|
203489
|
+
return this.encryptAndStringify(envId, resolvedAuthTypeCode, merged);
|
|
203490
|
+
}
|
|
203491
|
+
/**
|
|
203492
|
+
* 加密 envVariables 中的 password 字段并返回 JSON 字符串。
|
|
203493
|
+
* 无 password 字段时直接 JSON.stringify,不请求七彩石。
|
|
203494
|
+
*/
|
|
203495
|
+
async encryptAndStringify(envId, authTypeCode, envVariables) {
|
|
203496
|
+
// 1. 拉取模板,识别 password 字段
|
|
203497
|
+
const passwordFieldNames = await this.getPasswordFieldNames(authTypeCode);
|
|
203498
|
+
// 2. 判断是否有 password 字段需要加密
|
|
203499
|
+
const hasPasswordValue = passwordFieldNames.some(name => envVariables[name]);
|
|
203500
|
+
if (!hasPasswordValue) {
|
|
203501
|
+
return JSON.stringify(envVariables);
|
|
203502
|
+
}
|
|
203503
|
+
// 3. 拉取 AES key 并加密
|
|
203504
|
+
try {
|
|
203505
|
+
const aesKey = await (0, secretKey_1.getAesKey)({ envId });
|
|
203506
|
+
const encrypted = this.encryptPasswordFields(envVariables, passwordFieldNames, aesKey);
|
|
203507
|
+
return JSON.stringify(encrypted);
|
|
203508
|
+
}
|
|
203509
|
+
catch (_a) {
|
|
203510
|
+
// 获取密钥失败时以明文写入(与 CLI 行为一致)
|
|
203511
|
+
return JSON.stringify(envVariables);
|
|
203512
|
+
}
|
|
203513
|
+
}
|
|
203514
|
+
/**
|
|
203515
|
+
* 解密 Ext 中的 password 字段并返回新的 IUserKeyInfo。
|
|
203516
|
+
* 解密失败时保留原值,不中断流程。
|
|
203517
|
+
*/
|
|
203518
|
+
async decryptExtIfNeeded(userKeyInfo, envId) {
|
|
203519
|
+
if (!userKeyInfo.Ext || !userKeyInfo.AuthTypeCode)
|
|
203520
|
+
return userKeyInfo;
|
|
203521
|
+
try {
|
|
203522
|
+
const passwordFieldNames = await this.getPasswordFieldNames(userKeyInfo.AuthTypeCode);
|
|
203523
|
+
if (passwordFieldNames.length === 0)
|
|
203524
|
+
return userKeyInfo;
|
|
203525
|
+
const extObj = this.parseExt(userKeyInfo.Ext);
|
|
203526
|
+
if (!Object.keys(extObj).length)
|
|
203527
|
+
return userKeyInfo;
|
|
203528
|
+
const hasPasswordValue = passwordFieldNames.some(name => extObj[name]);
|
|
203529
|
+
if (!hasPasswordValue)
|
|
203530
|
+
return userKeyInfo;
|
|
203531
|
+
const aesKey = await (0, secretKey_1.getAesKey)({ envId });
|
|
203532
|
+
const decrypted = this.decryptPasswordFields(extObj, passwordFieldNames, aesKey);
|
|
203533
|
+
return Object.assign(Object.assign({}, userKeyInfo), { Ext: JSON.stringify(decrypted) });
|
|
203534
|
+
}
|
|
203535
|
+
catch (_a) {
|
|
203536
|
+
return userKeyInfo;
|
|
203537
|
+
}
|
|
203538
|
+
}
|
|
203539
|
+
/** 从模板中获取 password 类型字段名列表 */
|
|
203540
|
+
async getPasswordFieldNames(authTypeCode) {
|
|
203541
|
+
var _a, _b;
|
|
203542
|
+
try {
|
|
203543
|
+
const res = await this.listTemplates({ authTypeCode, limit: 1 });
|
|
203544
|
+
const templateExt = (_b = (_a = res.KeyTemplateList) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.Ext;
|
|
203545
|
+
if (!templateExt)
|
|
203546
|
+
return [];
|
|
203547
|
+
const parsed = JSON.parse(templateExt);
|
|
203548
|
+
const fields = Array.isArray(parsed === null || parsed === void 0 ? void 0 : parsed.fields) ? parsed.fields : [];
|
|
203549
|
+
return fields
|
|
203550
|
+
.filter((f) => f && f.type === 'password' && typeof f.name === 'string')
|
|
203551
|
+
.map((f) => f.name);
|
|
203552
|
+
}
|
|
203553
|
+
catch (_c) {
|
|
203554
|
+
return [];
|
|
203555
|
+
}
|
|
203556
|
+
}
|
|
203557
|
+
/** 解析 Ext JSON 字符串为对象,失败返回空对象 */
|
|
203558
|
+
parseExt(ext) {
|
|
203559
|
+
if (!ext)
|
|
203560
|
+
return {};
|
|
203561
|
+
try {
|
|
203562
|
+
const parsed = JSON.parse(ext);
|
|
203563
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
203564
|
+
}
|
|
203565
|
+
catch (_a) {
|
|
203566
|
+
return {};
|
|
203567
|
+
}
|
|
203568
|
+
}
|
|
203569
|
+
/** 加密 ext 对象中的 password 字段(仅加密有值的字段),返回新对象 */
|
|
203570
|
+
encryptPasswordFields(extObj, passwordFieldNames, aesKey) {
|
|
203571
|
+
const result = Object.assign({}, extObj);
|
|
203572
|
+
passwordFieldNames.forEach(name => {
|
|
203573
|
+
const value = result[name];
|
|
203574
|
+
if (name && value) {
|
|
203575
|
+
result[name] = crypto_1.AESCrypto.encrypt(value, aesKey);
|
|
203576
|
+
}
|
|
203577
|
+
});
|
|
203578
|
+
return result;
|
|
203579
|
+
}
|
|
203580
|
+
/** 解密 ext 对象中的 password 字段,解密失败保留原值,返回新对象 */
|
|
203581
|
+
decryptPasswordFields(extObj, passwordFieldNames, aesKey) {
|
|
203582
|
+
const result = Object.assign({}, extObj);
|
|
203583
|
+
const MIN_CIPHERTEXT_LEN = 24; // base64(16 字节 IV) = 24 字符
|
|
203584
|
+
passwordFieldNames.forEach(name => {
|
|
203585
|
+
const encrypted = result[name];
|
|
203586
|
+
if (typeof encrypted !== 'string' || encrypted.length <= MIN_CIPHERTEXT_LEN) {
|
|
203587
|
+
return;
|
|
203588
|
+
}
|
|
203589
|
+
try {
|
|
203590
|
+
result[name] = crypto_1.AESCrypto.decrypt(encrypted, aesKey);
|
|
203591
|
+
}
|
|
203592
|
+
catch (_a) {
|
|
203593
|
+
// 解密失败保留原值,不中断流程
|
|
203594
|
+
}
|
|
203595
|
+
});
|
|
203596
|
+
return result;
|
|
203597
|
+
}
|
|
203598
|
+
}
|
|
203599
|
+
exports.IntegrationService = IntegrationService;
|
|
203600
|
+
__decorate([
|
|
203601
|
+
(0, utils_1.preLazy)()
|
|
203602
|
+
], IntegrationService.prototype, "listTemplates", null);
|
|
203603
|
+
__decorate([
|
|
203604
|
+
(0, utils_1.preLazy)()
|
|
203605
|
+
], IntegrationService.prototype, "listUserKeys", null);
|
|
203606
|
+
__decorate([
|
|
203607
|
+
(0, utils_1.preLazy)()
|
|
203608
|
+
], IntegrationService.prototype, "getUserKey", null);
|
|
203609
|
+
__decorate([
|
|
203610
|
+
(0, utils_1.preLazy)()
|
|
203611
|
+
], IntegrationService.prototype, "createUserKey", null);
|
|
203612
|
+
__decorate([
|
|
203613
|
+
(0, utils_1.preLazy)()
|
|
203614
|
+
], IntegrationService.prototype, "updateUserKey", null);
|
|
203615
|
+
__decorate([
|
|
203616
|
+
(0, utils_1.preLazy)()
|
|
203617
|
+
], IntegrationService.prototype, "deleteUserKey", null);
|
|
203618
|
+
|
|
203619
|
+
|
|
202593
203620
|
/***/ }),
|
|
202594
203621
|
|
|
202595
203622
|
/***/ 72923:
|
|
@@ -213011,7 +214038,7 @@ function registerSetupTools(server) {
|
|
|
213011
214038
|
server.registerTool("downloadTemplate", {
|
|
213012
214039
|
title: "下载项目模板",
|
|
213013
214040
|
description: `自动下载并部署CloudBase项目模板。
|
|
213014
|
-
**Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.
|
|
214041
|
+
**Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.25.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
213015
214042
|
inputSchema: {
|
|
213016
214043
|
template: zod_1.z
|
|
213017
214044
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -221392,6 +222419,22 @@ module.exports = typeof queueMicrotask === 'function'
|
|
|
221392
222419
|
.catch(err => setTimeout(() => { throw err }, 0))
|
|
221393
222420
|
|
|
221394
222421
|
|
|
222422
|
+
/***/ }),
|
|
222423
|
+
|
|
222424
|
+
/***/ 79640:
|
|
222425
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
222426
|
+
|
|
222427
|
+
"use strict";
|
|
222428
|
+
|
|
222429
|
+
/**
|
|
222430
|
+
* 集成中心常量定义(与 dev-platform 对齐)
|
|
222431
|
+
*/
|
|
222432
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
222433
|
+
exports.USER_KEY_BUSINESS = void 0;
|
|
222434
|
+
/** 业务类型标识:集成中心来源的密钥对 */
|
|
222435
|
+
exports.USER_KEY_BUSINESS = 'integration';
|
|
222436
|
+
|
|
222437
|
+
|
|
221395
222438
|
/***/ }),
|
|
221396
222439
|
|
|
221397
222440
|
/***/ 79646:
|
|
@@ -230095,21 +231138,23 @@ const zod_1 = __webpack_require__(21614);
|
|
|
230095
231138
|
const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
230096
231139
|
const json_content_js_1 = __webpack_require__(4365);
|
|
230097
231140
|
const QUERY_GATEWAY_ACTIONS = [
|
|
230098
|
-
"getAccess",
|
|
230099
|
-
"listDomains",
|
|
230100
231141
|
"listRoutes",
|
|
230101
231142
|
"getRoute",
|
|
230102
231143
|
"listCustomDomains",
|
|
230103
231144
|
];
|
|
230104
231145
|
const MANAGE_GATEWAY_ACTIONS = [
|
|
230105
|
-
"createAccess",
|
|
230106
231146
|
"createRoute",
|
|
230107
231147
|
"updateRoute",
|
|
230108
231148
|
"deleteRoute",
|
|
230109
231149
|
"bindCustomDomain",
|
|
230110
231150
|
"deleteCustomDomain",
|
|
230111
|
-
|
|
230112
|
-
|
|
231151
|
+
];
|
|
231152
|
+
const UPSTREAM_RESOURCE_TYPES = [
|
|
231153
|
+
"SCF",
|
|
231154
|
+
"WEB_SCF",
|
|
231155
|
+
"CBR",
|
|
231156
|
+
"STATIC_STORE",
|
|
231157
|
+
"LH",
|
|
230113
231158
|
];
|
|
230114
231159
|
function normalizeAccessPath(path) {
|
|
230115
231160
|
if (!path) {
|
|
@@ -230140,24 +231185,11 @@ function registerGatewayTools(server) {
|
|
|
230140
231185
|
return (0, json_content_js_1.jsonContent)(buildErrorEnvelope(error));
|
|
230141
231186
|
}
|
|
230142
231187
|
};
|
|
230143
|
-
const listGatewayDomains = async () => {
|
|
230144
|
-
const cloudbase = await getManager();
|
|
230145
|
-
const result = await cloudbase.access.getDomainList();
|
|
230146
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
230147
|
-
const domains = [
|
|
230148
|
-
result.DefaultDomain,
|
|
230149
|
-
...(result.ServiceSet || []).map((item) => item.Domain),
|
|
230150
|
-
].filter(Boolean);
|
|
230151
|
-
return {
|
|
230152
|
-
domains,
|
|
230153
|
-
enableService: result.EnableService,
|
|
230154
|
-
raw: result,
|
|
230155
|
-
};
|
|
230156
|
-
};
|
|
230157
231188
|
const listHttpServiceRoutes = async (domain) => {
|
|
230158
231189
|
const cloudbase = await getManager();
|
|
230159
231190
|
const result = await cloudbase.env.describeHttpServiceRoute({
|
|
230160
231191
|
EnvId: await resolveEnvId(),
|
|
231192
|
+
Limit: 1000,
|
|
230161
231193
|
...(domain
|
|
230162
231194
|
? {
|
|
230163
231195
|
Filters: [
|
|
@@ -230173,123 +231205,180 @@ function registerGatewayTools(server) {
|
|
|
230173
231205
|
return result;
|
|
230174
231206
|
};
|
|
230175
231207
|
const flattenRoutes = (result) => (result.Domains ?? []).flatMap((domainItem) => (domainItem.Routes ?? []).map((route) => ({
|
|
230176
|
-
Domain: domainItem.Domain,
|
|
231208
|
+
Domain: domainItem.Domain ?? "",
|
|
230177
231209
|
DomainType: domainItem.DomainType,
|
|
230178
231210
|
AccessType: domainItem.AccessType,
|
|
231211
|
+
IsDefault: domainItem.IsDefault,
|
|
230179
231212
|
...route,
|
|
230180
231213
|
})));
|
|
231214
|
+
const buildRouteUrls = (routes) => Array.from(new Set(routes
|
|
231215
|
+
.filter((route) => route.Domain && route.Path)
|
|
231216
|
+
.map((route) => `https://${route.Domain}${normalizeAccessPath(String(route.Path))}`)));
|
|
231217
|
+
const resolveDefaultHttpDomain = async () => {
|
|
231218
|
+
const routeInfo = await listHttpServiceRoutes();
|
|
231219
|
+
const domains = routeInfo.Domains ?? [];
|
|
231220
|
+
const defaultCandidates = domains.filter((item) => item.IsDefault === true && item.Domain);
|
|
231221
|
+
const preferred = defaultCandidates.find((item) => item.Enable !== false && item.Status === "SUCCESS") ??
|
|
231222
|
+
defaultCandidates.find((item) => item.Enable !== false) ??
|
|
231223
|
+
defaultCandidates[0];
|
|
231224
|
+
if (!preferred?.Domain) {
|
|
231225
|
+
throw new Error("环境默认 HTTP 访问域名未就绪或未开通。请先在控制台开通 HTTP 访问服务,或用 queryGateway(action=\"listRoutes\") 确认 Domains 中是否存在 IsDefault=true 的域名;也可以显式传入 domain 后重试 createRoute/updateRoute/deleteRoute。");
|
|
231226
|
+
}
|
|
231227
|
+
return preferred.Domain;
|
|
231228
|
+
};
|
|
230181
231229
|
const resolveRouteDomain = async (preferredDomain) => {
|
|
230182
231230
|
if (preferredDomain) {
|
|
230183
231231
|
return preferredDomain;
|
|
230184
231232
|
}
|
|
230185
|
-
|
|
230186
|
-
return routeInfo.OriginDomain;
|
|
231233
|
+
return resolveDefaultHttpDomain();
|
|
230187
231234
|
};
|
|
230188
|
-
const
|
|
230189
|
-
|
|
230190
|
-
|
|
230191
|
-
|
|
230192
|
-
if (
|
|
230193
|
-
|
|
231235
|
+
const mapUpstreamResourceType = (input) => {
|
|
231236
|
+
if (input.upstreamResourceType) {
|
|
231237
|
+
return input.upstreamResourceType;
|
|
231238
|
+
}
|
|
231239
|
+
if (input.type === "HTTP") {
|
|
231240
|
+
return "WEB_SCF";
|
|
230194
231241
|
}
|
|
231242
|
+
if (input.type === "Event") {
|
|
231243
|
+
return "SCF";
|
|
231244
|
+
}
|
|
231245
|
+
if (input.requiresFunctionType || input.targetType === "function") {
|
|
231246
|
+
throw new Error("为云函数创建/更新路由时必须显式提供 type(HTTP→WEB_SCF,Event→SCF)或 route.upstreamResourceType。省略会导致 HTTP 与 Event 函数互相误标,访问时可能返回 FUNCTION_PARAM_INVALID 或网关内部错误。");
|
|
231247
|
+
}
|
|
231248
|
+
throw new Error("必须提供 route.upstreamResourceType(SCF / WEB_SCF / CBR / STATIC_STORE / LH),或在函数场景提供 type=\"HTTP\"|\"Event\"。");
|
|
231249
|
+
};
|
|
231250
|
+
const normalizeRoutePayload = async (input) => {
|
|
231251
|
+
const upstreamResourceName = input.route?.serviceName ?? input.targetName;
|
|
231252
|
+
if (!upstreamResourceName) {
|
|
231253
|
+
throw new Error("必须提供 targetName 或 route.serviceName 作为上游资源名称(例如云函数名)");
|
|
231254
|
+
}
|
|
231255
|
+
const isFunctionRoute = input.targetType === "function" ||
|
|
231256
|
+
input.type !== undefined ||
|
|
231257
|
+
input.route?.upstreamResourceType === "SCF" ||
|
|
231258
|
+
input.route?.upstreamResourceType === "WEB_SCF" ||
|
|
231259
|
+
(!input.route?.upstreamResourceType && Boolean(input.targetName));
|
|
231260
|
+
const upstreamResourceType = mapUpstreamResourceType({
|
|
231261
|
+
type: input.type,
|
|
231262
|
+
targetType: input.targetType,
|
|
231263
|
+
upstreamResourceType: input.route?.upstreamResourceType,
|
|
231264
|
+
requiresFunctionType: isFunctionRoute,
|
|
231265
|
+
});
|
|
231266
|
+
const path = normalizeAccessPath(input.route?.path ?? input.path ?? `/${upstreamResourceName}`);
|
|
231267
|
+
const domain = await resolveRouteDomain(input.domain);
|
|
231268
|
+
const enableAuth = input.route?.auth !== undefined
|
|
231269
|
+
? input.route.auth
|
|
231270
|
+
: input.auth !== undefined
|
|
231271
|
+
? input.auth
|
|
231272
|
+
: undefined;
|
|
230195
231273
|
return {
|
|
230196
231274
|
EnvId: await resolveEnvId(),
|
|
230197
231275
|
Domain: {
|
|
230198
|
-
Domain:
|
|
231276
|
+
Domain: domain,
|
|
230199
231277
|
Routes: [
|
|
230200
231278
|
{
|
|
230201
231279
|
Path: path,
|
|
230202
|
-
UpstreamResourceType:
|
|
230203
|
-
UpstreamResourceName:
|
|
230204
|
-
EnableAuth:
|
|
230205
|
-
? route.auth
|
|
230206
|
-
: fallback.auth !== undefined
|
|
230207
|
-
? fallback.auth
|
|
230208
|
-
: undefined,
|
|
231280
|
+
UpstreamResourceType: upstreamResourceType,
|
|
231281
|
+
UpstreamResourceName: upstreamResourceName,
|
|
231282
|
+
EnableAuth: enableAuth,
|
|
230209
231283
|
},
|
|
230210
231284
|
],
|
|
230211
231285
|
},
|
|
231286
|
+
resolved: {
|
|
231287
|
+
domain,
|
|
231288
|
+
path,
|
|
231289
|
+
upstreamResourceType,
|
|
231290
|
+
upstreamResourceName,
|
|
231291
|
+
enableAuth,
|
|
231292
|
+
},
|
|
230212
231293
|
};
|
|
230213
231294
|
};
|
|
231295
|
+
const routeMutationNextActions = (targetName) => [
|
|
231296
|
+
{
|
|
231297
|
+
tool: "queryGateway",
|
|
231298
|
+
action: "getRoute",
|
|
231299
|
+
reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
|
|
231300
|
+
},
|
|
231301
|
+
{
|
|
231302
|
+
tool: "queryPermissions",
|
|
231303
|
+
action: "getResourcePermission",
|
|
231304
|
+
reason: "确认函数安全规则是否允许预期访问方;网关 EnableAuth/auth=false 不等于函数已允许匿名访问",
|
|
231305
|
+
},
|
|
231306
|
+
{
|
|
231307
|
+
tool: "managePermissions",
|
|
231308
|
+
action: "updateResourcePermission",
|
|
231309
|
+
reason: "只有在确认需要匿名或浏览器直连访问时,才按实际安全要求更新函数权限",
|
|
231310
|
+
},
|
|
231311
|
+
];
|
|
230214
231312
|
const handleQueryGateway = async (input) => {
|
|
230215
231313
|
switch (input.action) {
|
|
230216
|
-
case "listDomains": {
|
|
230217
|
-
const result = await listGatewayDomains();
|
|
230218
|
-
return buildEnvelope({
|
|
230219
|
-
action: input.action,
|
|
230220
|
-
domains: result.domains,
|
|
230221
|
-
enableService: result.enableService,
|
|
230222
|
-
raw: result.raw,
|
|
230223
|
-
}, `已获取 ${result.domains.length} 个网关域名`, [
|
|
230224
|
-
{
|
|
230225
|
-
tool: "queryGateway",
|
|
230226
|
-
action: "getAccess",
|
|
230227
|
-
reason: "查看某个目标当前的访问入口",
|
|
230228
|
-
},
|
|
230229
|
-
]);
|
|
230230
|
-
}
|
|
230231
231314
|
case "listCustomDomains": {
|
|
230232
|
-
const result = await
|
|
231315
|
+
const result = await listHttpServiceRoutes();
|
|
231316
|
+
const customDomains = (result.Domains ?? []).filter((item) => item.IsDefault !== true);
|
|
230233
231317
|
return buildEnvelope({
|
|
230234
231318
|
action: input.action,
|
|
230235
|
-
domains:
|
|
230236
|
-
total:
|
|
230237
|
-
raw: result
|
|
230238
|
-
}, `已获取 ${
|
|
231319
|
+
domains: customDomains,
|
|
231320
|
+
total: customDomains.length,
|
|
231321
|
+
raw: result,
|
|
231322
|
+
}, `已获取 ${customDomains.length} 个自定义域名`);
|
|
230239
231323
|
}
|
|
230240
231324
|
case "listRoutes": {
|
|
230241
|
-
const result = await listHttpServiceRoutes();
|
|
231325
|
+
const result = await listHttpServiceRoutes(input.domain);
|
|
230242
231326
|
const routes = flattenRoutes(result);
|
|
230243
231327
|
return buildEnvelope({
|
|
230244
231328
|
action: input.action,
|
|
230245
231329
|
routes,
|
|
231330
|
+
urls: buildRouteUrls(routes),
|
|
230246
231331
|
total: result.TotalCount ?? routes.length,
|
|
230247
231332
|
raw: result,
|
|
230248
231333
|
}, `已获取 ${result.TotalCount ?? routes.length} 条 HTTP 路由`);
|
|
230249
231334
|
}
|
|
230250
231335
|
case "getRoute": {
|
|
230251
|
-
|
|
230252
|
-
|
|
230253
|
-
|
|
231336
|
+
if (!input.routeId && !input.targetName && !input.path) {
|
|
231337
|
+
throw new Error("action=getRoute 时至少需要提供 routeId、targetName 或 path");
|
|
231338
|
+
}
|
|
231339
|
+
const result = await listHttpServiceRoutes(input.domain);
|
|
231340
|
+
const normalizedPath = input.path
|
|
231341
|
+
? normalizeAccessPath(input.path)
|
|
231342
|
+
: undefined;
|
|
231343
|
+
const matches = flattenRoutes(result).filter((item) => {
|
|
231344
|
+
if (input.routeId && item.RouteId !== input.routeId) {
|
|
231345
|
+
return false;
|
|
231346
|
+
}
|
|
231347
|
+
if (input.domain && item.Domain !== input.domain) {
|
|
231348
|
+
return false;
|
|
231349
|
+
}
|
|
231350
|
+
if (normalizedPath &&
|
|
231351
|
+
normalizeAccessPath(String(item.Path ?? "")) !== normalizedPath) {
|
|
231352
|
+
return false;
|
|
231353
|
+
}
|
|
231354
|
+
if (input.targetName &&
|
|
231355
|
+
item.UpstreamResourceName !== input.targetName) {
|
|
231356
|
+
return false;
|
|
231357
|
+
}
|
|
231358
|
+
return Boolean(input.routeId || input.targetName || normalizedPath);
|
|
231359
|
+
});
|
|
231360
|
+
const route = matches.length === 1 ? matches[0] : null;
|
|
231361
|
+
const urls = buildRouteUrls(matches);
|
|
230254
231362
|
return buildEnvelope({
|
|
230255
231363
|
action: input.action,
|
|
230256
231364
|
routeId: input.routeId ?? null,
|
|
231365
|
+
targetName: input.targetName ?? null,
|
|
231366
|
+
path: normalizedPath ?? null,
|
|
231367
|
+
domain: input.domain ?? null,
|
|
230257
231368
|
route,
|
|
230258
|
-
|
|
230259
|
-
|
|
230260
|
-
}
|
|
230261
|
-
case "getAccess": {
|
|
230262
|
-
if (!input.targetName) {
|
|
230263
|
-
throw new Error("getAccess 操作时,targetName 参数是必需的");
|
|
230264
|
-
}
|
|
230265
|
-
const cloudbase = await getManager();
|
|
230266
|
-
const [accessList, domainInfo] = await Promise.all([
|
|
230267
|
-
cloudbase.access.getAccessList({ name: input.targetName }),
|
|
230268
|
-
listGatewayDomains(),
|
|
230269
|
-
]);
|
|
230270
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, accessList);
|
|
230271
|
-
const urls = Array.from(new Set((accessList.APISet || []).flatMap((api) => domainInfo.domains.map((domain) => {
|
|
230272
|
-
const normalizedPath = normalizeAccessPath(api.Path);
|
|
230273
|
-
return `https://${domain}${normalizedPath}`;
|
|
230274
|
-
}))));
|
|
230275
|
-
return buildEnvelope({
|
|
230276
|
-
action: input.action,
|
|
230277
|
-
targetType: input.targetType ?? "function",
|
|
230278
|
-
targetName: input.targetName,
|
|
230279
|
-
apis: accessList.APISet || [],
|
|
230280
|
-
total: accessList.Total || 0,
|
|
230281
|
-
domains: domainInfo.domains,
|
|
231369
|
+
routes: matches,
|
|
231370
|
+
total: matches.length,
|
|
230282
231371
|
urls,
|
|
230283
|
-
|
|
230284
|
-
|
|
230285
|
-
|
|
230286
|
-
|
|
230287
|
-
|
|
230288
|
-
|
|
231372
|
+
raw: result,
|
|
231373
|
+
}, matches.length === 0
|
|
231374
|
+
? "未找到对应路由"
|
|
231375
|
+
: matches.length === 1
|
|
231376
|
+
? "已获取路由详情"
|
|
231377
|
+
: `匹配到 ${matches.length} 条路由,请补充 path 或 domain 精确定位`, [
|
|
230289
231378
|
{
|
|
230290
231379
|
tool: "manageGateway",
|
|
230291
|
-
action: "
|
|
230292
|
-
reason: "
|
|
231380
|
+
action: "createRoute",
|
|
231381
|
+
reason: "为该目标新增 Domain/Route 访问路径",
|
|
230293
231382
|
},
|
|
230294
231383
|
]);
|
|
230295
231384
|
}
|
|
@@ -230299,78 +231388,60 @@ function registerGatewayTools(server) {
|
|
|
230299
231388
|
};
|
|
230300
231389
|
const handleManageGateway = async (input) => {
|
|
230301
231390
|
switch (input.action) {
|
|
230302
|
-
case "
|
|
230303
|
-
if (!input.targetName || !input.targetType) {
|
|
230304
|
-
throw new Error("action=createAccess 时必须提供 targetType 和 targetName");
|
|
230305
|
-
}
|
|
230306
|
-
if (!input.type) {
|
|
230307
|
-
throw new Error("action=createAccess 且 targetType=function 时必须显式提供 type。HTTP 云函数传 HTTP;Event 函数传 Event。省略会默认按 Event 路由处理,可能让 HTTP 云函数访问后返回 FUNCTION_PARAM_INVALID。");
|
|
230308
|
-
}
|
|
231391
|
+
case "createRoute": {
|
|
230309
231392
|
const cloudbase = await getManager();
|
|
230310
|
-
const
|
|
231393
|
+
const payload = await normalizeRoutePayload(input);
|
|
230311
231394
|
let result;
|
|
230312
231395
|
try {
|
|
230313
|
-
result = await cloudbase.
|
|
230314
|
-
|
|
230315
|
-
|
|
230316
|
-
type: (input.type === "HTTP" ? 6 : 1),
|
|
230317
|
-
auth: input.auth,
|
|
231396
|
+
result = await cloudbase.env.createHttpServiceRoute({
|
|
231397
|
+
EnvId: payload.EnvId,
|
|
231398
|
+
Domain: payload.Domain,
|
|
230318
231399
|
});
|
|
230319
231400
|
}
|
|
230320
231401
|
catch (err) {
|
|
230321
|
-
|
|
230322
|
-
|
|
231402
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
231403
|
+
if (message.includes("An error has occurred")) {
|
|
231404
|
+
let hint = "为目标资源配置访问路由失败(后端内部错误)。请确保:1) 目标云函数已成功创建并处于 Active 状态;2) 环境默认 HTTP 域名已完成初始化(IsDefault);3) 该访问路径未被占用。";
|
|
230323
231405
|
if (input.type === "HTTP") {
|
|
230324
|
-
hint +=
|
|
231406
|
+
hint +=
|
|
231407
|
+
"此外注意:如果目标函数最初是作为 Event 函数创建的,这里 type 仍必须传 Event(UpstreamResourceType=SCF);误传 HTTP/WEB_SCF 会导致此错误。";
|
|
230325
231408
|
}
|
|
230326
|
-
throw new Error(`${hint} 原始错误:${
|
|
231409
|
+
throw new Error(`${hint} 原始错误:${message}`);
|
|
230327
231410
|
}
|
|
230328
231411
|
throw err;
|
|
230329
231412
|
}
|
|
230330
231413
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
230331
231414
|
return buildEnvelope({
|
|
230332
231415
|
action: input.action,
|
|
230333
|
-
|
|
230334
|
-
|
|
230335
|
-
|
|
230336
|
-
|
|
230337
|
-
|
|
230338
|
-
|
|
230339
|
-
|
|
230340
|
-
|
|
230341
|
-
reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
|
|
230342
|
-
},
|
|
230343
|
-
{
|
|
230344
|
-
tool: "queryPermissions",
|
|
230345
|
-
action: "getResourcePermission",
|
|
230346
|
-
reason: "确认函数安全规则是否允许预期访问方;网关 auth=false 不等于函数已允许匿名访问",
|
|
230347
|
-
},
|
|
230348
|
-
{
|
|
230349
|
-
tool: "managePermissions",
|
|
230350
|
-
action: "updateResourcePermission",
|
|
230351
|
-
reason: "只有在确认需要匿名或浏览器直连访问时,才按实际安全要求更新函数权限",
|
|
230352
|
-
},
|
|
230353
|
-
]);
|
|
230354
|
-
}
|
|
230355
|
-
case "createRoute": {
|
|
230356
|
-
const cloudbase = await getManager();
|
|
230357
|
-
const result = await cloudbase.env.createHttpServiceRoute((await normalizeRoutePayload(input.route, input, input.domain)));
|
|
230358
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
230359
|
-
return buildEnvelope({
|
|
230360
|
-
action: input.action,
|
|
230361
|
-
route: input.route ?? null,
|
|
231416
|
+
model: "httpServiceRoute",
|
|
231417
|
+
targetType: input.targetType ?? null,
|
|
231418
|
+
targetName: payload.resolved.upstreamResourceName,
|
|
231419
|
+
domain: payload.resolved.domain,
|
|
231420
|
+
path: payload.resolved.path,
|
|
231421
|
+
upstreamResourceType: payload.resolved.upstreamResourceType,
|
|
231422
|
+
upstreamResourceName: payload.resolved.upstreamResourceName,
|
|
231423
|
+
auth: payload.resolved.enableAuth ?? null,
|
|
230362
231424
|
raw: result,
|
|
230363
|
-
},
|
|
231425
|
+
}, `已为目标 ${payload.resolved.upstreamResourceName} 在域名 ${payload.resolved.domain} 创建路由 ${payload.resolved.path}(${payload.resolved.upstreamResourceType})。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。该操作只创建网关入口,不会自动放开函数安全规则;若需要匿名或浏览器直接访问,请继续检查函数资源权限。`, routeMutationNextActions(payload.resolved.upstreamResourceName));
|
|
230364
231426
|
}
|
|
230365
231427
|
case "updateRoute": {
|
|
230366
231428
|
const cloudbase = await getManager();
|
|
230367
|
-
const
|
|
231429
|
+
const payload = await normalizeRoutePayload(input);
|
|
231430
|
+
const result = await cloudbase.env.modifyHttpServiceRoute({
|
|
231431
|
+
EnvId: payload.EnvId,
|
|
231432
|
+
Domain: payload.Domain,
|
|
231433
|
+
});
|
|
230368
231434
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
230369
231435
|
return buildEnvelope({
|
|
230370
231436
|
action: input.action,
|
|
230371
|
-
|
|
231437
|
+
model: "httpServiceRoute",
|
|
231438
|
+
domain: payload.resolved.domain,
|
|
231439
|
+
path: payload.resolved.path,
|
|
231440
|
+
upstreamResourceType: payload.resolved.upstreamResourceType,
|
|
231441
|
+
upstreamResourceName: payload.resolved.upstreamResourceName,
|
|
231442
|
+
auth: payload.resolved.enableAuth ?? null,
|
|
230372
231443
|
raw: result,
|
|
230373
|
-
},
|
|
231444
|
+
}, `HTTP 路由更新成功(${payload.resolved.domain}${payload.resolved.path})`, routeMutationNextActions(payload.resolved.upstreamResourceName));
|
|
230374
231445
|
}
|
|
230375
231446
|
case "deleteRoute": {
|
|
230376
231447
|
const routePath = input.route?.path ?? input.path;
|
|
@@ -230379,16 +231450,18 @@ function registerGatewayTools(server) {
|
|
|
230379
231450
|
}
|
|
230380
231451
|
const cloudbase = await getManager();
|
|
230381
231452
|
const domain = await resolveRouteDomain(input.domain);
|
|
231453
|
+
const normalizedPath = normalizeAccessPath(routePath);
|
|
230382
231454
|
const result = await cloudbase.env.deleteHttpServiceRoute({
|
|
230383
231455
|
EnvId: await resolveEnvId(),
|
|
230384
231456
|
Domain: domain,
|
|
230385
|
-
Paths: [
|
|
231457
|
+
Paths: [normalizedPath],
|
|
230386
231458
|
});
|
|
230387
231459
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
230388
231460
|
return buildEnvelope({
|
|
230389
231461
|
action: input.action,
|
|
231462
|
+
model: "httpServiceRoute",
|
|
230390
231463
|
domain,
|
|
230391
|
-
path:
|
|
231464
|
+
path: normalizedPath,
|
|
230392
231465
|
raw: result,
|
|
230393
231466
|
}, "HTTP 路由删除成功");
|
|
230394
231467
|
}
|
|
@@ -230428,114 +231501,34 @@ function registerGatewayTools(server) {
|
|
|
230428
231501
|
raw: result,
|
|
230429
231502
|
}, "自定义域名删除成功");
|
|
230430
231503
|
}
|
|
230431
|
-
case "deleteAccess": {
|
|
230432
|
-
const cloudbase = await getManager();
|
|
230433
|
-
const accessPath = input.path ? normalizeAccessPath(input.path) : undefined;
|
|
230434
|
-
let resolvedAccessId = input.accessId;
|
|
230435
|
-
let resolvedName;
|
|
230436
|
-
if (!resolvedAccessId) {
|
|
230437
|
-
if (!input.targetName && !accessPath) {
|
|
230438
|
-
throw new Error("action=deleteAccess 时至少需要提供 accessId,或提供 targetName/path 用于定位入口");
|
|
230439
|
-
}
|
|
230440
|
-
const accessList = await cloudbase.access.getAccessList({
|
|
230441
|
-
...(input.targetName ? { name: input.targetName } : {}),
|
|
230442
|
-
...(accessPath ? { path: accessPath } : {}),
|
|
230443
|
-
});
|
|
230444
|
-
const candidates = (accessList.APISet ?? []).filter((item) => {
|
|
230445
|
-
if (!accessPath) {
|
|
230446
|
-
return true;
|
|
230447
|
-
}
|
|
230448
|
-
return normalizeAccessPath(item.Path) === accessPath;
|
|
230449
|
-
});
|
|
230450
|
-
if (candidates.length === 0) {
|
|
230451
|
-
throw new Error("未找到可删除的访问入口,请确认 targetName/path 或直接传 accessId");
|
|
230452
|
-
}
|
|
230453
|
-
if (candidates.length > 1 && !accessPath) {
|
|
230454
|
-
throw new Error("匹配到多个访问入口,请补充 path 或直接传 accessId");
|
|
230455
|
-
}
|
|
230456
|
-
resolvedAccessId = candidates[0]?.APIId;
|
|
230457
|
-
resolvedName = candidates[0]?.Name;
|
|
230458
|
-
}
|
|
230459
|
-
else {
|
|
230460
|
-
// 仅传入 accessId 时,先查询获取 name 等信息,确保删除参数完整
|
|
230461
|
-
try {
|
|
230462
|
-
const accessList = await cloudbase.access.getAccessList({});
|
|
230463
|
-
const match = (accessList.APISet ?? []).find((item) => item.APIId === resolvedAccessId);
|
|
230464
|
-
if (match) {
|
|
230465
|
-
resolvedName = match.Name;
|
|
230466
|
-
}
|
|
230467
|
-
}
|
|
230468
|
-
catch {
|
|
230469
|
-
// 查询失败不影响主流程,继续用 accessId 删除
|
|
230470
|
-
}
|
|
230471
|
-
}
|
|
230472
|
-
if (!resolvedAccessId) {
|
|
230473
|
-
throw new Error("无法解析访问入口 accessId,请改为显式传入 accessId");
|
|
230474
|
-
}
|
|
230475
|
-
const result = await cloudbase.access.deleteAccess({
|
|
230476
|
-
...(resolvedName ? { name: resolvedName } : {}),
|
|
230477
|
-
apiId: resolvedAccessId,
|
|
230478
|
-
});
|
|
230479
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
230480
|
-
return buildEnvelope({
|
|
230481
|
-
action: input.action,
|
|
230482
|
-
targetName: resolvedName ?? input.targetName ?? null,
|
|
230483
|
-
path: accessPath ?? null,
|
|
230484
|
-
accessId: resolvedAccessId,
|
|
230485
|
-
raw: result,
|
|
230486
|
-
}, "网关访问入口删除成功");
|
|
230487
|
-
}
|
|
230488
|
-
case "updatePathAuth": {
|
|
230489
|
-
if (!input.targetName && !input.path) {
|
|
230490
|
-
throw new Error("action=updatePathAuth 时至少需要提供 targetName 或 path");
|
|
230491
|
-
}
|
|
230492
|
-
if (input.auth === undefined) {
|
|
230493
|
-
throw new Error("action=updatePathAuth 时必须提供 auth");
|
|
230494
|
-
}
|
|
230495
|
-
const cloudbase = await getManager();
|
|
230496
|
-
const accessPath = input.path ? normalizeAccessPath(input.path) : undefined;
|
|
230497
|
-
const accessList = await cloudbase.access.getAccessList({
|
|
230498
|
-
...(input.targetName ? { name: input.targetName } : {}),
|
|
230499
|
-
...(accessPath ? { path: accessPath } : {}),
|
|
230500
|
-
});
|
|
230501
|
-
const apiIds = (accessList.APISet ?? []).map((item) => item.APIId).filter(Boolean);
|
|
230502
|
-
if (apiIds.length === 0) {
|
|
230503
|
-
throw new Error("未找到可更新鉴权状态的访问入口");
|
|
230504
|
-
}
|
|
230505
|
-
const result = await cloudbase.access.switchPathAuth({
|
|
230506
|
-
apiIds,
|
|
230507
|
-
auth: input.auth,
|
|
230508
|
-
});
|
|
230509
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
230510
|
-
return buildEnvelope({
|
|
230511
|
-
action: input.action,
|
|
230512
|
-
targetName: input.targetName ?? null,
|
|
230513
|
-
path: accessPath ?? null,
|
|
230514
|
-
auth: input.auth,
|
|
230515
|
-
apiIds,
|
|
230516
|
-
raw: result,
|
|
230517
|
-
}, "网关路径鉴权更新成功");
|
|
230518
|
-
}
|
|
230519
231504
|
default:
|
|
230520
231505
|
throw new Error(`不支持的操作类型: ${input.action}`);
|
|
230521
231506
|
}
|
|
230522
231507
|
};
|
|
230523
231508
|
server.registerTool?.("queryGateway", {
|
|
230524
231509
|
title: "查询 CloudBase 网关",
|
|
230525
|
-
description: "CloudBase
|
|
231510
|
+
description: "CloudBase 网关统一只读入口(Domain/Route 模型)。通过 listRoutes / getRoute / listCustomDomains 查询域名与路由;主键为 Domain + Path,上游类型为 SCF / WEB_SCF / CBR / STATIC_STORE / LH。",
|
|
230526
231511
|
inputSchema: {
|
|
230527
231512
|
action: zod_1.z
|
|
230528
231513
|
.enum(QUERY_GATEWAY_ACTIONS)
|
|
230529
|
-
.describe("
|
|
231514
|
+
.describe("只读操作类型:listRoutes、getRoute、listCustomDomains"),
|
|
230530
231515
|
targetType: zod_1.z
|
|
230531
231516
|
.enum(["function"])
|
|
230532
231517
|
.optional()
|
|
230533
|
-
.describe("目标资源类型。当前支持 function
|
|
231518
|
+
.describe("目标资源类型。当前支持 function"),
|
|
230534
231519
|
targetName: zod_1.z
|
|
230535
231520
|
.string()
|
|
230536
231521
|
.optional()
|
|
230537
|
-
.describe("
|
|
231522
|
+
.describe("上游资源名称。getRoute 时可按云函数名过滤"),
|
|
230538
231523
|
routeId: zod_1.z.string().optional().describe("路由 ID。getRoute 时可选"),
|
|
231524
|
+
path: zod_1.z
|
|
231525
|
+
.string()
|
|
231526
|
+
.optional()
|
|
231527
|
+
.describe("路由路径。getRoute / listRoutes 过滤时可选"),
|
|
231528
|
+
domain: zod_1.z
|
|
231529
|
+
.string()
|
|
231530
|
+
.optional()
|
|
231531
|
+
.describe("域名。getRoute / listRoutes 过滤时可选"),
|
|
230539
231532
|
},
|
|
230540
231533
|
annotations: {
|
|
230541
231534
|
readOnlyHint: true,
|
|
@@ -230545,19 +231538,19 @@ function registerGatewayTools(server) {
|
|
|
230545
231538
|
}, async (input) => withEnvelope(() => handleQueryGateway(input)));
|
|
230546
231539
|
server.registerTool?.("manageGateway", {
|
|
230547
231540
|
title: "管理 CloudBase 网关",
|
|
230548
|
-
description: "CloudBase
|
|
231541
|
+
description: "CloudBase 网关统一写入口(Domain/Route 模型)。为已存在的 HTTP 云函数补默认域名访问时,使用 createRoute,并提供 targetType=\"function\"、targetName、type=\"HTTP\"(映射 WEB_SCF)与期望 path;Event 函数传 type=\"Event\"(映射 SCF)。未传 domain 时自动解析 IsDefault 默认域名。注意 createRoute 只创建网关入口,不会自动修改函数资源权限。更新鉴权用 updateRoute;删除用 deleteRoute(Domain+Path)。⚠️ 绑定带 SSL 证书的自定义域名用 bindCustomDomain;CORS/安全域名请使用 envDomainManagement。",
|
|
230549
231542
|
inputSchema: {
|
|
230550
231543
|
action: zod_1.z
|
|
230551
231544
|
.enum(MANAGE_GATEWAY_ACTIONS)
|
|
230552
|
-
.describe(
|
|
231545
|
+
.describe("写操作类型。为已有函数补默认域名访问入口时使用 createRoute;函数场景必须显式提供 type(HTTP→WEB_SCF,Event→SCF)或 route.upstreamResourceType。"),
|
|
230553
231546
|
targetType: zod_1.z
|
|
230554
231547
|
.enum(["function"])
|
|
230555
231548
|
.optional()
|
|
230556
|
-
.describe("目标资源类型。当前支持 function
|
|
231549
|
+
.describe("目标资源类型。当前支持 function"),
|
|
230557
231550
|
targetName: zod_1.z
|
|
230558
231551
|
.string()
|
|
230559
231552
|
.optional()
|
|
230560
|
-
.describe("目标资源名称。
|
|
231553
|
+
.describe("目标资源名称。createRoute 到云函数时填写函数名"),
|
|
230561
231554
|
path: zod_1.z
|
|
230562
231555
|
.string()
|
|
230563
231556
|
.optional()
|
|
@@ -230565,25 +231558,28 @@ function registerGatewayTools(server) {
|
|
|
230565
231558
|
type: zod_1.z
|
|
230566
231559
|
.enum(["Event", "HTTP"])
|
|
230567
231560
|
.optional()
|
|
230568
|
-
.describe("
|
|
231561
|
+
.describe("目标函数运行时类型,不是接入协议。HTTP 云函数传 HTTP(UpstreamResourceType=WEB_SCF);Event 函数传 Event(SCF)。误标会导致 FUNCTION_PARAM_INVALID 或网关错误。函数路由场景必须显式提供 type 或 route.upstreamResourceType。"),
|
|
230569
231562
|
auth: zod_1.z
|
|
230570
231563
|
.boolean()
|
|
230571
231564
|
.optional()
|
|
230572
|
-
.describe("
|
|
231565
|
+
.describe("是否开启网关路径鉴权(EnableAuth)。若要走默认域名做匿名或浏览器访问,通常设为 false;该开关仅控制网关入口本身,不会修改函数资源权限。"),
|
|
230573
231566
|
route: zod_1.z
|
|
230574
231567
|
.object({
|
|
230575
|
-
routeId: zod_1.z.string().optional(),
|
|
230576
231568
|
path: zod_1.z.string().optional(),
|
|
230577
|
-
serviceType: zod_1.z.string().optional(),
|
|
230578
231569
|
serviceName: zod_1.z.string().optional(),
|
|
231570
|
+
upstreamResourceType: zod_1.z.enum(UPSTREAM_RESOURCE_TYPES).optional(),
|
|
230579
231571
|
auth: zod_1.z.boolean().optional(),
|
|
230580
231572
|
})
|
|
230581
231573
|
.optional()
|
|
230582
|
-
.describe("HTTP
|
|
230583
|
-
domain: zod_1.z
|
|
230584
|
-
|
|
230585
|
-
|
|
230586
|
-
|
|
231574
|
+
.describe("HTTP 路由配置。upstreamResourceType 可选 SCF / WEB_SCF / CBR / STATIC_STORE / LH"),
|
|
231575
|
+
domain: zod_1.z
|
|
231576
|
+
.string()
|
|
231577
|
+
.optional()
|
|
231578
|
+
.describe("域名。省略时自动使用环境 IsDefault 默认 HTTP 域名;自定义域名场景请显式传入"),
|
|
231579
|
+
certificateId: zod_1.z
|
|
231580
|
+
.string()
|
|
231581
|
+
.optional()
|
|
231582
|
+
.describe("证书 ID。bindCustomDomain 时必填"),
|
|
230587
231583
|
},
|
|
230588
231584
|
annotations: {
|
|
230589
231585
|
readOnlyHint: false,
|
|
@@ -231225,10 +232221,13 @@ function parseCommaParts(str) {
|
|
|
231225
232221
|
return parts;
|
|
231226
232222
|
}
|
|
231227
232223
|
|
|
231228
|
-
function expandTop(str) {
|
|
232224
|
+
function expandTop(str, options) {
|
|
231229
232225
|
if (!str)
|
|
231230
232226
|
return [];
|
|
231231
232227
|
|
|
232228
|
+
options = options || {};
|
|
232229
|
+
var max = options.max == null ? Infinity : options.max;
|
|
232230
|
+
|
|
231232
232231
|
// I don't know why Bash 4.3 does this, but it does.
|
|
231233
232232
|
// Anything starting with {} will have the first two bytes preserved
|
|
231234
232233
|
// but *only* at the top level, so {},a}b will not expand to anything,
|
|
@@ -231239,7 +232238,7 @@ function expandTop(str) {
|
|
|
231239
232238
|
str = '\\{\\}' + str.substr(2);
|
|
231240
232239
|
}
|
|
231241
232240
|
|
|
231242
|
-
return expand(escapeBraces(str), true).map(unescapeBraces);
|
|
232241
|
+
return expand(escapeBraces(str), max, true).map(unescapeBraces);
|
|
231243
232242
|
}
|
|
231244
232243
|
|
|
231245
232244
|
function embrace(str) {
|
|
@@ -231256,24 +232255,29 @@ function gte(i, y) {
|
|
|
231256
232255
|
return i >= y;
|
|
231257
232256
|
}
|
|
231258
232257
|
|
|
231259
|
-
function expand(str, isTop) {
|
|
232258
|
+
function expand(str, max, isTop) {
|
|
231260
232259
|
var expansions = [];
|
|
231261
232260
|
|
|
231262
|
-
|
|
231263
|
-
|
|
232261
|
+
// The `{a},b}` rewrite below restarts expansion on a rewritten string with
|
|
232262
|
+
// the same `max` and `isTop = true`. Loop instead of recursing so a long run
|
|
232263
|
+
// of non-expanding `{}` groups can't exhaust the call stack.
|
|
232264
|
+
for (;;) {
|
|
232265
|
+
const m = balanced('{', '}', str)
|
|
232266
|
+
if (!m) return [str]
|
|
231264
232267
|
|
|
231265
|
-
|
|
231266
|
-
|
|
231267
|
-
var post = m.post.length
|
|
231268
|
-
? expand(m.post, false)
|
|
231269
|
-
: [''];
|
|
232268
|
+
// no need to expand pre, since it is guaranteed to be free of brace-sets
|
|
232269
|
+
const pre = m.pre
|
|
231270
232270
|
|
|
231271
|
-
|
|
231272
|
-
|
|
231273
|
-
|
|
231274
|
-
|
|
232271
|
+
if (/\$$/.test(m.pre)) {
|
|
232272
|
+
const post =
|
|
232273
|
+
m.post.length ? expand(m.post, max, false) : ['']
|
|
232274
|
+
for (let k = 0; k < post.length && k < max; k++) {
|
|
232275
|
+
const expansion = pre + '{' + m.body + '}' + post[k]
|
|
232276
|
+
expansions.push(expansion)
|
|
232277
|
+
}
|
|
232278
|
+
return expansions
|
|
231275
232279
|
}
|
|
231276
|
-
|
|
232280
|
+
|
|
231277
232281
|
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
|
|
231278
232282
|
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
|
|
231279
232283
|
var isSequence = isNumericSequence || isAlphaSequence;
|
|
@@ -231282,11 +232286,18 @@ function expand(str, isTop) {
|
|
|
231282
232286
|
// {a},b}
|
|
231283
232287
|
if (m.post.match(/,(?!,).*\}/)) {
|
|
231284
232288
|
str = m.pre + '{' + m.body + escClose + m.post;
|
|
231285
|
-
|
|
232289
|
+
isTop = true;
|
|
232290
|
+
continue;
|
|
231286
232291
|
}
|
|
231287
232292
|
return [str];
|
|
231288
232293
|
}
|
|
231289
232294
|
|
|
232295
|
+
// Only expand post once we know this brace set actually expands. Computing
|
|
232296
|
+
// it before the early returns above expanded post a second time on every
|
|
232297
|
+
// non-expanding `{}`, which is what made inputs like `a{},{},{}...` blow up
|
|
232298
|
+
// exponentially.
|
|
232299
|
+
const post =
|
|
232300
|
+
m.post.length ? expand(m.post, max, false) : ['']
|
|
231290
232301
|
var n;
|
|
231291
232302
|
if (isSequence) {
|
|
231292
232303
|
n = m.body.split(/\.\./);
|
|
@@ -231294,7 +232305,7 @@ function expand(str, isTop) {
|
|
|
231294
232305
|
n = parseCommaParts(m.body);
|
|
231295
232306
|
if (n.length === 1) {
|
|
231296
232307
|
// x{{a,b}}y ==> x{a}y x{b}y
|
|
231297
|
-
n = expand(n[0], false).map(embrace);
|
|
232308
|
+
n = expand(n[0], max, false).map(embrace);
|
|
231298
232309
|
if (n.length === 1) {
|
|
231299
232310
|
return post.map(function(p) {
|
|
231300
232311
|
return m.pre + n[0] + p;
|
|
@@ -231312,7 +232323,7 @@ function expand(str, isTop) {
|
|
|
231312
232323
|
var y = numeric(n[1]);
|
|
231313
232324
|
var width = Math.max(n[0].length, n[1].length)
|
|
231314
232325
|
var incr = n.length == 3
|
|
231315
|
-
? Math.abs(numeric(n[2]))
|
|
232326
|
+
? Math.max(Math.abs(numeric(n[2])), 1)
|
|
231316
232327
|
: 1;
|
|
231317
232328
|
var test = lte;
|
|
231318
232329
|
var reverse = y < x;
|
|
@@ -231324,7 +232335,7 @@ function expand(str, isTop) {
|
|
|
231324
232335
|
|
|
231325
232336
|
N = [];
|
|
231326
232337
|
|
|
231327
|
-
for (var i = x; test(i, y); i += incr) {
|
|
232338
|
+
for (var i = x; test(i, y) && N.length < max; i += incr) {
|
|
231328
232339
|
var c;
|
|
231329
232340
|
if (isAlphaSequence) {
|
|
231330
232341
|
c = String.fromCharCode(i);
|
|
@@ -231349,24 +232360,23 @@ function expand(str, isTop) {
|
|
|
231349
232360
|
N = [];
|
|
231350
232361
|
|
|
231351
232362
|
for (var j = 0; j < n.length; j++) {
|
|
231352
|
-
N.push.apply(N, expand(n[j], false));
|
|
232363
|
+
N.push.apply(N, expand(n[j], max, false));
|
|
231353
232364
|
}
|
|
231354
232365
|
}
|
|
231355
232366
|
|
|
231356
232367
|
for (var j = 0; j < N.length; j++) {
|
|
231357
|
-
for (var k = 0; k < post.length; k++) {
|
|
232368
|
+
for (var k = 0; k < post.length && expansions.length < max; k++) {
|
|
231358
232369
|
var expansion = pre + N[j] + post[k];
|
|
231359
232370
|
if (!isTop || isSequence || expansion)
|
|
231360
232371
|
expansions.push(expansion);
|
|
231361
232372
|
}
|
|
231362
232373
|
}
|
|
231363
|
-
}
|
|
231364
232374
|
|
|
231365
|
-
|
|
232375
|
+
return expansions;
|
|
232376
|
+
}
|
|
231366
232377
|
}
|
|
231367
232378
|
|
|
231368
232379
|
|
|
231369
|
-
|
|
231370
232380
|
/***/ }),
|
|
231371
232381
|
|
|
231372
232382
|
/***/ 85014:
|
|
@@ -262894,6 +263904,17 @@ class CloudBase {
|
|
|
262894
263904
|
get aiModel() {
|
|
262895
263905
|
return this.currentEnvironment().getAiModelService();
|
|
262896
263906
|
}
|
|
263907
|
+
/**
|
|
263908
|
+
* 集成中心服务(UserKey)
|
|
263909
|
+
* 提供集成模板/实例的增删改查与绑定云函数能力
|
|
263910
|
+
*/
|
|
263911
|
+
get integration() {
|
|
263912
|
+
return this.currentEnvironment().getIntegrationService();
|
|
263913
|
+
}
|
|
263914
|
+
/**
|
|
263915
|
+
* 监控服务
|
|
263916
|
+
* 提供指标查询能力
|
|
263917
|
+
*/
|
|
262897
263918
|
get monitor() {
|
|
262898
263919
|
return this.currentEnvironment().getMonitorService();
|
|
262899
263920
|
}
|
|
@@ -264506,7 +265527,9 @@ function configToParams(options) {
|
|
|
264506
265527
|
if ((_c = func === null || func === void 0 ? void 0 : func.layers) === null || _c === void 0 ? void 0 : _c.length) {
|
|
264507
265528
|
params.Layers = func.layers.map(item => ({
|
|
264508
265529
|
LayerName: item.name,
|
|
264509
|
-
|
|
265530
|
+
// item.version 可能是字符串(如 JSON 配置文件中的 "1" 或模板变量替换结果),
|
|
265531
|
+
// SCF API 要求 LayerVersion 为 int64,需显式转为 number
|
|
265532
|
+
LayerVersion: Number(item.version)
|
|
264510
265533
|
}));
|
|
264511
265534
|
}
|
|
264512
265535
|
// 7. 代码保护
|
|
@@ -264545,8 +265568,8 @@ function configToParams(options) {
|
|
|
264545
265568
|
params.Code.ImageConfig = buildImageConfig(imageConfig);
|
|
264546
265569
|
}
|
|
264547
265570
|
// 第三阶段:统一应用默认值
|
|
264548
|
-
const runtime = params.Runtime || '
|
|
264549
|
-
return Object.assign({ Handler: func.handler || 'index.main',
|
|
265571
|
+
const runtime = params.Runtime || 'Nodejs20.19';
|
|
265572
|
+
return Object.assign({ Handler: func.handler || 'index.main', Runtime: 'Nodejs20.19', MemorySize: 256, InstallDependency: isNodeFunction(runtime) ? 'TRUE' : 'FALSE' }, params);
|
|
264550
265573
|
}
|
|
264551
265574
|
class FunctionService {
|
|
264552
265575
|
constructor(environment) {
|
|
@@ -264602,7 +265625,7 @@ class FunctionService {
|
|
|
264602
265625
|
async createFunction(funcParam) {
|
|
264603
265626
|
var _a, _b, _c;
|
|
264604
265627
|
const { namespace } = this.getFunctionConfig();
|
|
264605
|
-
const { func, functionRootPath, force = false, base64Code, codeSecret, functionPath, deployMode } = funcParam;
|
|
265628
|
+
const { func, functionRootPath, force = false, base64Code, codeSecret, functionPath, deployMode, code } = funcParam;
|
|
264606
265629
|
const funcName = func.name;
|
|
264607
265630
|
const { TopicId, LogsetId } = this.getClsServiceConfig();
|
|
264608
265631
|
const params = configToParams({
|
|
@@ -264616,20 +265639,30 @@ class FunctionService {
|
|
|
264616
265639
|
ClsLogsetId: LogsetId
|
|
264617
265640
|
}
|
|
264618
265641
|
});
|
|
265642
|
+
const customCode = code;
|
|
265643
|
+
const customImageCode = customCode === null || customCode === void 0 ? void 0 : customCode.ImageConfig;
|
|
265644
|
+
const funcImageCode = (_a = params.Code) === null || _a === void 0 ? void 0 : _a.ImageConfig;
|
|
264619
265645
|
// 根据部署方式处理 Code 参数
|
|
264620
|
-
//
|
|
264621
|
-
const isImageDeploy = deployMode === 'image' || (!deployMode && (
|
|
265646
|
+
// 优先级:deployMode=image > 显式传入 code > 原有自动打包逻辑(base64Code/functionPath/functionRootPath)
|
|
265647
|
+
const isImageDeploy = deployMode === 'image' || Boolean(customImageCode) || (!deployMode && Boolean(funcImageCode));
|
|
264622
265648
|
if (isImageDeploy) {
|
|
264623
|
-
//
|
|
265649
|
+
// 镜像部署优先使用显式 code.ImageConfig;未传时回退到 func.imageConfig(configToParams 生成)
|
|
265650
|
+
if (customImageCode) {
|
|
265651
|
+
params.Code = customCode;
|
|
265652
|
+
}
|
|
264624
265653
|
if (!((_c = (_b = params.Code) === null || _b === void 0 ? void 0 : _b.ImageConfig) === null || _c === void 0 ? void 0 : _c.ImageUri)) {
|
|
264625
265654
|
throw new error_1.CloudBaseError('镜像部署需要配置 imageConfig.imageUri');
|
|
264626
265655
|
}
|
|
264627
|
-
//
|
|
264628
|
-
// 镜像函数不需要 Handler
|
|
265656
|
+
// 镜像函数不需要 Handler / InstallDependency
|
|
264629
265657
|
delete params.Handler;
|
|
264630
|
-
// 镜像函数不需要安装依赖
|
|
264631
265658
|
delete params.InstallDependency;
|
|
264632
265659
|
}
|
|
265660
|
+
else if (customCode) {
|
|
265661
|
+
if (base64Code || functionPath || functionRootPath) {
|
|
265662
|
+
console.warn(`[${funcName}] 检测到同时传入 code 与本地打包参数,已优先使用 code 参数`);
|
|
265663
|
+
}
|
|
265664
|
+
params.Code = customCode;
|
|
265665
|
+
}
|
|
264633
265666
|
else {
|
|
264634
265667
|
// 代码部署:通过 getCodeParams 获取代码参数
|
|
264635
265668
|
params.Code = await this.getCodeParams({
|
|
@@ -264684,7 +265717,8 @@ class FunctionService {
|
|
|
264684
265717
|
functionPath,
|
|
264685
265718
|
functionRootPath,
|
|
264686
265719
|
codeSecret: codeSecret,
|
|
264687
|
-
deployMode: isImageDeploy ? 'image' :
|
|
265720
|
+
deployMode: isImageDeploy ? 'image' : deployMode,
|
|
265721
|
+
code
|
|
264688
265722
|
});
|
|
264689
265723
|
// 等待函数状态正常
|
|
264690
265724
|
await this.waitFunctionActive(funcName, codeSecret);
|
|
@@ -265132,7 +266166,9 @@ class FunctionService {
|
|
|
265132
266166
|
if ((_c = func === null || func === void 0 ? void 0 : func.layers) === null || _c === void 0 ? void 0 : _c.length) {
|
|
265133
266167
|
params.Layers = func.layers.map(item => ({
|
|
265134
266168
|
LayerName: item.name,
|
|
265135
|
-
|
|
266169
|
+
// item.version 可能是字符串(如 JSON 配置文件中的 "1" 或模板变量替换结果),
|
|
266170
|
+
// SCF API 要求 LayerVersion 为 int64,需显式转为 number
|
|
266171
|
+
LayerVersion: Number(item.version)
|
|
265136
266172
|
}));
|
|
265137
266173
|
}
|
|
265138
266174
|
// 特殊处理:HTTP 函数多并发配置(大小写不敏感)
|
|
@@ -265187,7 +266223,7 @@ class FunctionService {
|
|
|
265187
266223
|
* @memberof FunctionService
|
|
265188
266224
|
*/
|
|
265189
266225
|
async updateFunctionCode(funcParam) {
|
|
265190
|
-
const { func, functionRootPath, base64Code, codeSecret, functionPath, deployMode } = funcParam;
|
|
266226
|
+
const { func, functionRootPath, base64Code, codeSecret, functionPath, deployMode, code } = funcParam;
|
|
265191
266227
|
const funcName = func.name;
|
|
265192
266228
|
const { namespace } = this.getFunctionConfig();
|
|
265193
266229
|
// 镜像部署:使用镜像配置更新函数代码
|
|
@@ -265214,6 +266250,10 @@ class FunctionService {
|
|
|
265214
266250
|
});
|
|
265215
266251
|
}
|
|
265216
266252
|
}
|
|
266253
|
+
const customCode = code;
|
|
266254
|
+
if (customCode && (base64Code || functionPath || functionRootPath)) {
|
|
266255
|
+
console.warn(`[${funcName}] 检测到同时传入 code 与本地打包参数,已优先使用 code 参数`);
|
|
266256
|
+
}
|
|
265217
266257
|
// 代码部署:原有逻辑
|
|
265218
266258
|
let installDependency;
|
|
265219
266259
|
// Node 函数默认安装依赖
|
|
@@ -265222,7 +266262,7 @@ class FunctionService {
|
|
|
265222
266262
|
if (func.installDependency !== undefined) {
|
|
265223
266263
|
installDependency = toBooleanString(func.installDependency);
|
|
265224
266264
|
}
|
|
265225
|
-
const codeParams = await this.getCodeParams({
|
|
266265
|
+
const codeParams = customCode || await this.getCodeParams({
|
|
265226
266266
|
func,
|
|
265227
266267
|
functionPath,
|
|
265228
266268
|
functionRootPath,
|
|
@@ -265816,7 +266856,7 @@ class FunctionService {
|
|
|
265816
266856
|
const { func, functionPath, functionRootPath } = options;
|
|
265817
266857
|
const { env, appId } = this.getFunctionConfig();
|
|
265818
266858
|
if (!appId) {
|
|
265819
|
-
throw new error_1.CloudBaseError('无法获取 AppId
|
|
266859
|
+
throw new error_1.CloudBaseError('无法获取 AppId,请确认环境初始化完成或已开通存储服务');
|
|
265820
266860
|
}
|
|
265821
266861
|
const objectPath = `${appId}/${env}/${func.name}.zip`;
|
|
265822
266862
|
// 1. 生成存放函数包的临时 Cos 目录
|
|
@@ -266052,14 +267092,18 @@ class FunctionService {
|
|
|
266052
267092
|
* @memberof FunctionService
|
|
266053
267093
|
*/
|
|
266054
267094
|
getFunctionConfig() {
|
|
266055
|
-
var _a, _b, _c, _d, _e;
|
|
267095
|
+
var _a, _b, _c, _d, _e, _f;
|
|
266056
267096
|
const envConfig = this.environment.lazyEnvironmentConfig;
|
|
266057
267097
|
// Functions 可能为空
|
|
266058
267098
|
const namespace = ((_b = (_a = envConfig.Functions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.Namespace) || '';
|
|
266059
|
-
// AppId
|
|
267099
|
+
// AppId 优先从 DescribeEnvInfo 的 UserInfo 获取(账号级标识,不依赖存储服务)
|
|
267100
|
+
// 兜底从 Storages[0] 的 AppId 或 Bucket 名提取
|
|
266060
267101
|
// COS Bucket 格式: {BucketName}-{APPID},APPID 为纯数字
|
|
266061
267102
|
const storage = (_c = envConfig.Storages) === null || _c === void 0 ? void 0 : _c[0];
|
|
266062
|
-
const appId = (
|
|
267103
|
+
const appId = ((_d = envConfig.UserInfo) === null || _d === void 0 ? void 0 : _d.AppId)
|
|
267104
|
+
|| (storage === null || storage === void 0 ? void 0 : storage.AppId)
|
|
267105
|
+
|| ((_f = (_e = storage === null || storage === void 0 ? void 0 : storage.Bucket) === null || _e === void 0 ? void 0 : _e.match(/-(\d+)$/)) === null || _f === void 0 ? void 0 : _f[1])
|
|
267106
|
+
|| '';
|
|
266063
267107
|
const { proxy } = this.environment.cloudBaseContext;
|
|
266064
267108
|
return {
|
|
266065
267109
|
proxy,
|