@cloudbase/cloudbase-mcp 2.24.1 → 2.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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
- const { UploadUrl: uploadUrl, UploadHeaders: uploadHeaders, PackageName: packageName, PackageVersion: packageVersion } = await this.tcbService.request('DescribeCloudBaseBuildService', {
356
- EnvId: envConfig.EnvId,
357
- ServiceName: serverName
358
- });
359
- const deployInfo = {
360
- DeployType: 'package',
361
- PackageName: packageName,
362
- PackageVersion: packageVersion
363
- };
364
- /**
365
- * 上传部署包
366
- */
367
- const zipFile = await codeToZip(targetPath, { installDependency: (serverConfig === null || serverConfig === void 0 ? void 0 : serverConfig.InstallDependency) !== undefined ? serverConfig.InstallDependency : true });
368
- await (0, utils_1.upload)({
369
- url: uploadUrl,
370
- file: zipFile,
371
- headers: (uploadHeaders || []).reduce((map, item) => {
372
- map[item.Key] = item.Value;
373
- return map;
374
- }, {}) || {},
375
- method: 'PUT'
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
- return this.tcbrService.request(isNew ? 'CreateCloudRunServer' : 'UpdateCloudRunServer', {
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
  }
@@ -2415,8 +2441,8 @@ async function enrichEnvInfoWithRuntimeMode(result, manager) {
2415
2441
  : "No legacy NoSQL Database/Storage observed in this env.",
2416
2442
  MysqlNotAvailable: hasMysql
2417
2443
  ? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
2418
- : "No MySQL instance is provisioned for this env. Do NOT use `manageMysqlDatabase` / `queryMysqlDatabase` (those are MySQL-specific) and do NOT read the `relational-database-tool` skill — that family targets MySQL, not CloudBase PG.",
2419
- RecommendedSkills: "Read `postgresql-development` first for new PG code. `no-sql-web-sdk` is still applicable for existing NoSQL collections in this env. Skip `relational-database-tool` (MySQL-only) entirely.",
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.",
2420
2446
  }
2421
2447
  : {
2422
2448
  PrimaryBackend: "PostgreSQL is NOT provisioned in this env — this is a legacy NoSQL CloudBase backend.",
@@ -2425,8 +2451,8 @@ async function enrichEnvInfoWithRuntimeMode(result, manager) {
2425
2451
  Storage: "Browser uploads use `app.uploadFile()` against the bucket exposed in `EnvInfo.Storages[].Bucket`.",
2426
2452
  MysqlNotAvailable: hasMysql
2427
2453
  ? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
2428
- : "No MySQL instance in this env. `manageMysqlDatabase` / `queryMysqlDatabase` and the `relational-database-tool` skill are not applicable.",
2429
- RecommendedSkills: "Read `no-sql-web-sdk` (and `cloud-storage-web` for uploads). `postgresql-development` and `relational-database-tool` are not applicable to this env.",
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.",
2430
2456
  };
2431
2457
  return {
2432
2458
  ...result,
@@ -3347,7 +3373,7 @@ function registerEnvTools(server) {
3347
3373
  // Register primary tool name (queryEnv)
3348
3374
  const queryEnvToolSchema = {
3349
3375
  title: "CloudBase 环境查询",
3350
- 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-tool` skill 都不该使用。",
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 都不该使用。",
3351
3377
  inputSchema: {
3352
3378
  action: zod_1.z
3353
3379
  .enum(["list", "info", "domains"])
@@ -80012,6 +80038,32 @@ function parallel(list, iterator, callback)
80012
80038
  }
80013
80039
 
80014
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
+
80015
80067
  /***/ }),
80016
80068
 
80017
80069
  /***/ 18959:
@@ -84596,6 +84648,102 @@ exports.colors = {
84596
84648
  })));
84597
84649
 
84598
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
+
84599
84747
  /***/ }),
84600
84748
 
84601
84749
  /***/ 22738:
@@ -87688,6 +87836,116 @@ __decorate([
87688
87836
  ], PermissionService.prototype, "modifyEnvAuthzConfig", null);
87689
87837
 
87690
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
+
87691
87949
  /***/ }),
87692
87950
 
87693
87951
  /***/ 25177:
@@ -89896,8 +90154,12 @@ exports.MANAGE_FUNCTION_ACTIONS = [
89896
90154
  "incrementalDeployFunction", // 增量部署,需通过 pluginOptions.functions 注入实现
89897
90155
  ];
89898
90156
  const VPC_SCHEMA = zod_1.z.object({
89899
- vpcId: zod_1.z.string(),
89900
- subnetId: zod_1.z.string(),
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."),
89901
90163
  });
89902
90164
  // 镜像部署值,对应 SCF Runtime=CustomImage
89903
90165
  exports.CUSTOM_IMAGE_RUNTIME = "CustomImage";
@@ -89981,8 +90243,11 @@ const CREATE_FUNCTION_SCHEMA = zod_1.z.object({
89981
90243
  })
89982
90244
  .optional(),
89983
90245
  timeout: zod_1.z.number().optional().describe("函数超时时间"),
89984
- envVariables: zod_1.z.record(zod_1.z.string()).optional().describe("环境变量"),
89985
- vpc: VPC_SCHEMA.optional().describe("私有网络配置"),
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 一致;未知时先查控制台或询问用户,禁止填占位符。"),
89986
90251
  runtime: zod_1.z
89987
90252
  .string()
89988
90253
  .optional()
@@ -90236,7 +90501,7 @@ function registerFunctionTools(server) {
90236
90501
  },
90237
90502
  {
90238
90503
  tool: "queryGateway",
90239
- action: "getAccess",
90504
+ action: "getRoute",
90240
90505
  reason: "查看该函数是否已暴露网关访问入口",
90241
90506
  },
90242
90507
  ]);
@@ -90546,7 +90811,7 @@ function registerFunctionTools(server) {
90546
90811
  raw: imageResult,
90547
90812
  }, `已基于镜像 ${createImageConfig.imageUri} 创建 HTTP 函数 ${functionName}。` +
90548
90813
  `请确认 TCR、SCF 与构建管道处于同一地域;如需通过 URL 访问,请显式调用 ` +
90549
- `manageGateway(action="createAccess", type="HTTP") 并按需调整函数安全规则。` +
90814
+ `manageGateway(action="createRoute", upstreamResourceType="WEB_SCF") 并按需调整函数安全规则。` +
90550
90815
  `部署后可用 queryFunctions(action="getFunctionDetail") 确认函数已就绪。`, [
90551
90816
  {
90552
90817
  tool: "queryFunctions",
@@ -90560,8 +90825,8 @@ function registerFunctionTools(server) {
90560
90825
  },
90561
90826
  {
90562
90827
  tool: "manageGateway",
90563
- action: "createAccess",
90564
- reason: "如需通过 URL 访问镜像 HTTP 函数,显式创建网关访问入口并传 type=\"HTTP\"",
90828
+ action: "createRoute",
90829
+ reason: "如需通过 URL 访问镜像 HTTP 函数,显式创建 Domain/Route 访问入口并传 type=\"HTTP\"(映射 WEB_SCF)",
90565
90830
  },
90566
90831
  ]);
90567
90832
  }
@@ -90616,12 +90881,12 @@ function registerFunctionTools(server) {
90616
90881
  if (func.type === "HTTP") {
90617
90882
  nextActions.push({
90618
90883
  tool: "manageGateway",
90619
- action: "createAccess",
90620
- reason: "如果需要通过 URL 访问 HTTP 函数,请调用 manageGateway(action=\"createAccess\") 并显式传 type=\"HTTP\",再按实际路径和鉴权需求创建访问入口,不要默认假设 /函数名 已存在",
90884
+ action: "createRoute",
90885
+ reason: "如果需要通过 URL 访问 HTTP 函数,请调用 manageGateway(action=\"createRoute\") 并显式传 upstreamResourceType=\"WEB_SCF\",再按实际路径和鉴权需求创建访问入口,不要默认假设 /函数名 已存在",
90621
90886
  });
90622
90887
  nextActions.push({
90623
90888
  tool: "queryGateway",
90624
- action: "getAccess",
90889
+ action: "getRoute",
90625
90890
  reason: "交付前确认 HTTP 访问路径是否已存在并已生效",
90626
90891
  });
90627
90892
  nextActions.push({
@@ -90636,7 +90901,7 @@ function registerFunctionTools(server) {
90636
90901
  });
90637
90902
  }
90638
90903
  const message = func.type === "HTTP"
90639
- ? `已创建 HTTP 函数 ${functionName}。如果后续需要通过 URL 访问,请显式调用 manageGateway(action="createAccess"),并把 type="HTTP" 一起传入,再按实际路径和鉴权需求创建访问入口。评测或其他外部调用方可能会以匿名身份访问,而且失败后不一定会把 EXCEED_AUTHORITY 再反馈给 AI;交付前请主动确认访问路径和函数安全规则,若已出现 EXCEED_AUTHORITY,请先调用 queryPermissions(action="getResourcePermission", resourceType="function", resourceId="${functionName}") 查看当前规则,再按需要使用 managePermissions(action="updateResourcePermission") 调整权限。`
90904
+ ? `已创建 HTTP 函数 ${functionName}。如果后续需要通过 URL 访问,请显式调用 manageGateway(action="createRoute"),并把 upstreamResourceType="WEB_SCF" 一起传入,再按实际路径和鉴权需求创建访问入口。评测或其他外部调用方可能会以匿名身份访问,而且失败后不一定会把 EXCEED_AUTHORITY 再反馈给 AI;交付前请主动确认访问路径和函数安全规则,若已出现 EXCEED_AUTHORITY,请先调用 queryPermissions(action="getResourcePermission", resourceType="function", resourceId="${functionName}") 查看当前规则,再按需要使用 managePermissions(action="updateResourcePermission") 调整权限。`
90640
90905
  : `已创建函数 ${functionName}`;
90641
90906
  return buildEnvelope({
90642
90907
  action: input.action,
@@ -91124,8 +91389,11 @@ function registerFunctionTools(server) {
91124
91389
  zipFile: zod_1.z.string().optional().describe("仅兼容特殊场景:预先准备好的代码包 base64 编码。普通 createFunction/updateFunctionCode 默认不要先压缩 zip,优先使用 functionRootPath。"),
91125
91390
  handler: zod_1.z.string().optional().describe("函数入口"),
91126
91391
  timeout: zod_1.z.number().optional().describe("配置更新时的超时时间"),
91127
- envVariables: zod_1.z.record(zod_1.z.string()).optional().describe("配置更新时要合并的环境变量"),
91128
- vpc: VPC_SCHEMA.optional().describe("配置更新时的 VPC 信息"),
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;不要用占位符。"),
91129
91397
  params: zod_1.z.record(zod_1.z.any()).optional().describe("invokeFunction 的调用参数"),
91130
91398
  triggers: zod_1.z
91131
91399
  .array(TRIGGER_SCHEMA)
@@ -93397,7 +93665,7 @@ class EnvService {
93397
93665
  * @returns {Promise<IEnvInfoRes>}
93398
93666
  */
93399
93667
  async getEnvInfo() {
93400
- var _a;
93668
+ var _a, _b;
93401
93669
  // 使用 DescribeEnvInfo 进行单环境查询
93402
93670
  const params = {
93403
93671
  EnvId: this.envId
@@ -93405,6 +93673,7 @@ class EnvService {
93405
93673
  const response = await this.describeEnvInfo(params);
93406
93674
  const envBaseInfo = (_a = response === null || response === void 0 ? void 0 : response.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo;
93407
93675
  // 将 EnvBaseInfo 完整映射到 EnvInfo 类型,避免新增字段被丢弃
93676
+ // UserInfo 位于 response.EnvInfo 顶层(非 EnvBaseInfo),单独映射
93408
93677
  const envInfo = {
93409
93678
  EnvId: envBaseInfo.EnvId,
93410
93679
  Alias: envBaseInfo.Alias,
@@ -93434,7 +93703,8 @@ class EnvService {
93434
93703
  EnvPreferences: envBaseInfo.EnvPreferences,
93435
93704
  Meta: envBaseInfo.Meta,
93436
93705
  PostgreSQL: envBaseInfo.PostgreSQL,
93437
- 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
93438
93708
  };
93439
93709
  return {
93440
93710
  EnvInfo: envInfo,
@@ -103410,7 +103680,6 @@ const capi_js_1 = __webpack_require__(40738);
103410
103680
  const cloudrun_js_1 = __webpack_require__(35023);
103411
103681
  const dataModel_js_1 = __webpack_require__(34052);
103412
103682
  const gateway_js_1 = __webpack_require__(84319);
103413
- const invite_code_js_1 = __webpack_require__(44760);
103414
103683
  const agents_js_1 = __webpack_require__(9527);
103415
103684
  const app_auth_js_1 = __webpack_require__(5313);
103416
103685
  const apps_js_1 = __webpack_require__(36817);
@@ -103441,7 +103710,6 @@ const DEFAULT_PLUGINS = [
103441
103710
  "logs",
103442
103711
  "agents",
103443
103712
  "download",
103444
- "invite-code",
103445
103713
  "capi",
103446
103714
  ];
103447
103715
  function registerDatabase(server) {
@@ -103483,7 +103751,6 @@ const AVAILABLE_PLUGINS = {
103483
103751
  logs: { name: "logs", register: logs_js_1.registerLogTools },
103484
103752
  agents: { name: "agents", register: agents_js_1.registerAgentTools },
103485
103753
  apps: { name: "apps", register: apps_js_1.registerAppTools },
103486
- "invite-code": { name: "invite-code", register: invite_code_js_1.registerInviteCodeTools },
103487
103754
  cloudrun: { name: "cloudrun", register: cloudrun_js_1.registerCloudRunTools },
103488
103755
  capi: { name: "capi", register: capi_js_1.registerCapiTools },
103489
103756
  };
@@ -113963,6 +114230,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
113963
114230
  };
113964
114231
  Object.defineProperty(exports, "__esModule", ({ value: true }));
113965
114232
  exports.CLOUDRUN_ACCESS_TYPES = exports.CLOUDRUN_SERVICE_TYPES = void 0;
114233
+ exports.detectCloudRunDbNetworkRisk = detectCloudRunDbNetworkRisk;
113966
114234
  exports.registerCloudRunTools = registerCloudRunTools;
113967
114235
  const child_process_1 = __webpack_require__(79646);
113968
114236
  const fs_1 = __importDefault(__webpack_require__(29021));
@@ -114005,7 +114273,7 @@ const ManageCloudRunInputSchema = {
114005
114273
  })).optional().describe('扩缩容配置数组,用于配置服务的自动扩缩容策略。可配置多个扩缩容策略'),
114006
114274
  CustomLogs: zod_1.z.string().optional().describe('自定义日志配置,用于配置服务的日志收集和存储策略'),
114007
114275
  Port: zod_1.z.number().min(1).max(65535).optional().describe('服务监听端口配置。函数型服务固定为3000,容器型服务可自定义。服务代码必须监听此端口才能正常接收请求'),
114008
- EnvParams: zod_1.z.string().optional().describe('环境变量配置,JSON字符串格式。用于传递配置信息给服务代码,如\'{"DATABASE_URL":"mysql://...","NODE_ENV":"production"}\'。SDK v5.6.1+ 会自动对传入的环境变量进行 AES-256-CBC 加密传输'),
114276
+ 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 内数据库'),
114009
114277
  Dockerfile: zod_1.z.string().optional().describe('Dockerfile文件名配置,仅容器型服务需要。指定用于构建容器镜像的Dockerfile文件路径,默认为项目根目录下的Dockerfile'),
114010
114278
  BuildDir: zod_1.z.string().optional().describe('构建目录配置,指定代码构建的目录路径。当代码结构与标准不同时使用,默认为项目根目录'),
114011
114279
  InternalAccess: zod_1.z.string().optional().describe('内网访问开关配置,控制是否启用内网访问。true=启用内网访问(可通过云开发SDK直接调用),false=关闭内网访问(仅公网访问)'),
@@ -114029,9 +114297,9 @@ const ManageCloudRunInputSchema = {
114029
114297
  ReplicaNum: zod_1.z.number().min(0).describe('定时扩缩容的目标副本数,最小值0(缩容到0)')
114030
114298
  })).optional().describe('定时扩缩容配置数组,用于配置服务的定时自动扩缩容策略。可配置多个时间段的扩缩容计划,支持每日/每周/每月循环'),
114031
114299
  VpcConf: zod_1.z.object({
114032
- VpcId: zod_1.z.string().describe('VPC网络ID,指定服务所在的私有网络'),
114033
- SubnetId: zod_1.z.string().describe('子网ID,指定服务所在的子网'),
114034
- }).optional().describe('VPC网络配置,用于将云托管服务部署到指定的私有网络中。需要提前创建好VPC和子网'),
114300
+ VpcId: zod_1.z.string().describe('VPC网络ID,格式如 vpc-xxxxxxxx。必须与目标数据库/Redis 处于同一地域,并优先选择同一 VPC。禁止猜测或使用占位符;须来自数据库控制台、已有资源详情、callCloudApi 或用户确认。建议首次创建即配置;已存在服务也可在 deploy 时传入,部署后必须用 queryCloudRun detail 复核是否生效'),
114301
+ SubnetId: zod_1.z.string().describe('子网ID,格式如 subnet-xxxxxxxx。云托管实例将占用该子网 IP,需确保有足够可用 IP'),
114302
+ }).optional().describe('VPC网络配置(实例出网/私有网络)。用于让云托管实例接入指定 VPC,从而内网访问 MySQL/PostgreSQL/Redis/CVM 等资源。与 OpenAccessTypes(外部如何访问本服务)是不同概念。TCP 连库场景必须配置。禁止猜测 VpcId/SubnetId,须来自数据库控制台、已有资源详情、callCloudApi 或用户确认。MCP 在创建时会将 VpcConf 映射为 SDK vpcInfo(CreateType=2);更新后必须用 queryCloudRun detail 复核 ServerConfig.VpcConf,未生效再走控制台网络设置或删建兜底'),
114035
114303
  VolumesConf: zod_1.z.array(zod_1.z.object({
114036
114304
  VolumeName: zod_1.z.string().describe('存储卷名称'),
114037
114305
  VolumeType: zod_1.z.string().describe('存储卷类型,如CFS表示云文件存储'),
@@ -114135,6 +114403,74 @@ function buildManageCloudRunErrorMessage(action, serverName, error) {
114135
114403
  }
114136
114404
  return `[manageCloudRun/${action}] ${baseMessage}\n建议:${suggestions.join(" ")}`;
114137
114405
  }
114406
+ 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;
114407
+ const CLOUDRUN_DB_URL_PATTERN = /(mysql|mariadb|postgres|postgresql|mongodb(\+srv)?|redis|rediss):\/\//i;
114408
+ /**
114409
+ * Detect likely TCP database/cache env usage without VpcConf.
114410
+ * Soft signal for AI agents — does not block deploy.
114411
+ */
114412
+ function detectCloudRunDbNetworkRisk(options) {
114413
+ const vpcId = options.vpcConf?.VpcId?.trim();
114414
+ const subnetId = options.vpcConf?.SubnetId?.trim();
114415
+ if (vpcId && subnetId) {
114416
+ return null;
114417
+ }
114418
+ const rawEnv = options.envParams?.trim();
114419
+ if (!rawEnv) {
114420
+ return null;
114421
+ }
114422
+ let parsed;
114423
+ try {
114424
+ const value = JSON.parse(rawEnv);
114425
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
114426
+ return null;
114427
+ }
114428
+ parsed = value;
114429
+ }
114430
+ catch {
114431
+ // Non-JSON EnvParams still may embed connection URLs as plain text.
114432
+ if (!CLOUDRUN_DB_URL_PATTERN.test(rawEnv)) {
114433
+ return null;
114434
+ }
114435
+ return {
114436
+ code: "MISSING_VPC_FOR_DB_ENV",
114437
+ 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.",
114438
+ matchedKeys: ["<non-json-envParams>"],
114439
+ remediation: [
114440
+ "Set serverConfig.VpcConf to the same region/VPC (and a subnet with free IPs) as the database.",
114441
+ "Do NOT invent VpcId/SubnetId. Resolve real IDs from the DB console, resource detail, callCloudApi, or the user.",
114442
+ "Use the database private/intranet hostname in EnvParams, not localhost or docker-compose service names.",
114443
+ "Ensure the DB security group allows the CloudRun subnet on the DB port.",
114444
+ "Re-deploy after VpcConf is set. OpenAccessTypes alone does not provide VPC egress to databases.",
114445
+ ],
114446
+ };
114447
+ }
114448
+ const matchedKeys = [];
114449
+ for (const [key, value] of Object.entries(parsed)) {
114450
+ if (CLOUDRUN_DB_ENV_KEY_PATTERN.test(key)) {
114451
+ matchedKeys.push(key);
114452
+ continue;
114453
+ }
114454
+ if (typeof value === "string" && CLOUDRUN_DB_URL_PATTERN.test(value)) {
114455
+ matchedKeys.push(key);
114456
+ }
114457
+ }
114458
+ if (matchedKeys.length === 0) {
114459
+ return null;
114460
+ }
114461
+ return {
114462
+ code: "MISSING_VPC_FOR_DB_ENV",
114463
+ message: "EnvParams includes database/cache connection settings, but serverConfig.VpcConf is missing. Deploy may succeed while runtime DB connections fail.",
114464
+ matchedKeys,
114465
+ remediation: [
114466
+ "Set serverConfig.VpcConf.VpcId and SubnetId to the database VPC/subnet (same region).",
114467
+ "Do NOT invent VpcId/SubnetId. Resolve real IDs from the DB console, resource detail, callCloudApi, or the user.",
114468
+ "Use the private DB endpoint in EnvParams.",
114469
+ "Confirm security group / allowlist permits CloudRun subnet access to the DB port.",
114470
+ "Do not confuse OpenAccessTypes (ingress) with VpcConf (egress to VPC resources).",
114471
+ ],
114472
+ };
114473
+ }
114138
114474
  function getCloudRunQueryServerName(input) {
114139
114475
  return input.detailServerName || input.serverName;
114140
114476
  }
@@ -114632,6 +114968,17 @@ for await (let x of res.textStream) {
114632
114968
  if (input.serverConfig) {
114633
114969
  deployParams.serverConfig = input.serverConfig;
114634
114970
  }
114971
+ // Manager SDK create path prefers top-level vpcInfo (CreateCloudRunServer.VpcInfo).
114972
+ // Map serverConfig.VpcConf → vpcInfo so first-time create actually binds VPC.
114973
+ // Update path still sends Items.VpcConf; platform may ignore VPC changes on existing services.
114974
+ const vpcConf = input.serverConfig?.VpcConf;
114975
+ if (vpcConf?.VpcId?.trim() && vpcConf?.SubnetId?.trim()) {
114976
+ deployParams.vpcInfo = {
114977
+ VpcId: vpcConf.VpcId.trim(),
114978
+ CreateType: 2,
114979
+ SubnetIds: [vpcConf.SubnetId.trim()],
114980
+ };
114981
+ }
114635
114982
  let result;
114636
114983
  try {
114637
114984
  result = await cloudrunService.deploy(deployParams);
@@ -114705,6 +115052,14 @@ for await (let x of res.textStream) {
114705
115052
  // Notification failure should not affect deployment flow
114706
115053
  // Error is already logged in sendDeployNotification
114707
115054
  }
115055
+ const dbNetworkRisk = detectCloudRunDbNetworkRisk({
115056
+ envParams: input.serverConfig?.EnvParams,
115057
+ vpcConf: input.serverConfig?.VpcConf,
115058
+ });
115059
+ const warnings = dbNetworkRisk ? [dbNetworkRisk] : [];
115060
+ const warningSuffix = dbNetworkRisk
115061
+ ? ` Warning: ${dbNetworkRisk.message} Set serverConfig.VpcConf before relying on DB connectivity.`
115062
+ : "";
114708
115063
  return {
114709
115064
  content: [
114710
115065
  {
@@ -114717,9 +115072,10 @@ for await (let x of res.textStream) {
114717
115072
  deployPath: targetPath,
114718
115073
  serverType: serverType,
114719
115074
  cloudbasercGenerated: true,
114720
- consoleUrl
115075
+ consoleUrl,
115076
+ ...(warnings.length > 0 ? { warnings } : {}),
114721
115077
  },
114722
- message: `Triggered deployment for ${serverType} service '${input.serverName}' from ${targetPath}. You can follow the progress in ${consoleUrl}`
115078
+ message: `Triggered deployment for ${serverType} service '${input.serverName}' from ${targetPath}. You can follow the progress in ${consoleUrl}.${warningSuffix}`
114723
115079
  }, null, 2)
114724
115080
  }
114725
115081
  ]
@@ -132938,71 +133294,6 @@ util.inherits(ArchiverError, Error);
132938
133294
 
132939
133295
  exports = module.exports = ArchiverError;
132940
133296
 
132941
- /***/ }),
132942
-
132943
- /***/ 44760:
132944
- /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
132945
-
132946
- "use strict";
132947
-
132948
- Object.defineProperty(exports, "__esModule", ({ value: true }));
132949
- exports.registerInviteCodeTools = registerInviteCodeTools;
132950
- const zod_1 = __webpack_require__(21614);
132951
- const cloudbase_manager_js_1 = __webpack_require__(3431);
132952
- function registerInviteCodeTools(server) {
132953
- const cloudBaseOptions = server.cloudBaseOptions;
132954
- const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
132955
- server.registerTool?.("activateInviteCode", {
132956
- title: "激活邀请码",
132957
- description: "云开发 AI编程激励计划,通过邀请码激活用户激励。",
132958
- inputSchema: {
132959
- InviteCode: zod_1.z.string().describe("待激活的邀请码")
132960
- },
132961
- annotations: {
132962
- readOnlyHint: false,
132963
- destructiveHint: false,
132964
- idempotentHint: false,
132965
- openWorldHint: true,
132966
- category: "invite-code"
132967
- }
132968
- }, async ({ InviteCode }) => {
132969
- if (!InviteCode) {
132970
- return {
132971
- content: [
132972
- {
132973
- type: "text",
132974
- text: JSON.stringify({
132975
- ErrorCode: 'MissingParameter',
132976
- ErrorMsg: '邀请码为必填项',
132977
- RequestId: ''
132978
- }, null, 2)
132979
- }
132980
- ]
132981
- };
132982
- }
132983
- const manager = await getManager();
132984
- const EnvId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
132985
- const result = await manager.commonService().call({
132986
- Action: 'ActivateInviteCode',
132987
- Param: { InviteCode, EnvId }
132988
- });
132989
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
132990
- return {
132991
- content: [
132992
- {
132993
- type: "text",
132994
- text: JSON.stringify({
132995
- ErrorCode: result?.ErrorCode || '',
132996
- ErrorMsg: result?.ErrorMsg || '',
132997
- RequestId: result?.RequestId || ''
132998
- }, null, 2)
132999
- }
133000
- ]
133001
- };
133002
- });
133003
- }
133004
-
133005
-
133006
133297
  /***/ }),
133007
133298
 
133008
133299
  /***/ 44763:
@@ -136914,7 +137205,7 @@ class TelemetryReporter {
136914
137205
  const nodeVersion = process.version; // Node.js版本
136915
137206
  const arch = os_1.default.arch(); // 系统架构
136916
137207
  // 从构建时注入的版本号获取MCP版本信息
136917
- const mcpVersion = process.env.npm_package_version || "2.24.1" || 0;
137208
+ const mcpVersion = process.env.npm_package_version || "2.25.1" || 0;
136918
137209
  return {
136919
137210
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
136920
137211
  deviceId: this.deviceId,
@@ -172843,6 +173134,7 @@ const permission_1 = __webpack_require__(24786);
172843
173134
  const sandbox_1 = __webpack_require__(48488);
172844
173135
  const aiModel_1 = __webpack_require__(34800);
172845
173136
  const monitor_1 = __webpack_require__(8901);
173137
+ const integration_1 = __webpack_require__(72785);
172846
173138
  class Environment {
172847
173139
  constructor(context, envId) {
172848
173140
  this.inited = false;
@@ -172869,6 +173161,7 @@ class Environment {
172869
173161
  this.sandboxService = new sandbox_1.SandboxService(this);
172870
173162
  this.aiModelService = new aiModel_1.AiModelService(this);
172871
173163
  this.monitorService = new monitor_1.MonitorService(this);
173164
+ this.integrationService = new integration_1.IntegrationService(this);
172872
173165
  }
172873
173166
  async lazyInit() {
172874
173167
  if (!this.inited) {
@@ -172946,6 +173239,9 @@ class Environment {
172946
173239
  getMonitorService() {
172947
173240
  return this.monitorService;
172948
173241
  }
173242
+ getIntegrationService() {
173243
+ return this.integrationService;
173244
+ }
172949
173245
  getCommonService(serviceType = 'tcb', serviceVersion) {
172950
173246
  return new common_1.CommonService(this, serviceType, serviceVersion);
172951
173247
  }
@@ -197005,7 +197301,7 @@ ${envIdSection}
197005
197301
  ## 环境信息
197006
197302
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
197007
197303
  - Node.js版本: ${process.version}
197008
- - MCP 版本:${process.env.npm_package_version || "2.24.1" || 0}
197304
+ - MCP 版本:${process.env.npm_package_version || "2.25.1" || 0}
197009
197305
  - 系统架构: ${os_1.default.arch()}
197010
197306
  - 时间: ${new Date().toISOString()}
197011
197307
  - 请求ID: ${requestId}
@@ -197086,7 +197382,7 @@ function createWrappedHandler(name, handler, server) {
197086
197382
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
197087
197383
  ide: server.ide || process.env.INTEGRATION_IDE || ''
197088
197384
  });
197089
- const mcpVersion = true ? "2.24.1" : 0;
197385
+ const mcpVersion = true ? "2.25.1" : 0;
197090
197386
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
197091
197387
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
197092
197388
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -199117,6 +199413,7 @@ module.exports = cmp
199117
199413
 
199118
199414
  "use strict";
199119
199415
 
199416
+ /* eslint-disable @typescript-eslint/member-ordering */
199120
199417
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
199121
199418
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
199122
199419
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -199359,27 +199656,30 @@ class StorageService {
199359
199656
  getMetadataController.loadTasks(tasks);
199360
199657
  const files = await getMetadataController.run();
199361
199658
  // 对文件上传进行处理
199362
- const cos = this.getCos(parallel);
199363
- const uploadFiles = util_1.default.promisify(cos.uploadFiles).bind(cos);
199364
- const params = {
199365
- files,
199366
- SliceSize: BIG_FILE_SIZE,
199659
+ // 构造 files 数组(格式:{ localPath, cloudPath }),传给 uploadFilesCustom
199660
+ const filesForUpload = fileStatsList
199661
+ .filter(stats => !stats.isDir)
199662
+ .map(stats => ({
199663
+ localPath: stats.filePath,
199664
+ cloudPath: stats.cloudFileKey
199665
+ }));
199666
+ // 调用 uploadFilesCustom(智能混合模式:并行→串行)
199667
+ // 不再直接调用 uploadFilesWithRetry(纯并行重试,容易 socket hang up)
199668
+ return this.uploadFilesCustom({
199669
+ files: filesForUpload,
199670
+ bucket,
199671
+ region,
199672
+ ignore: [],
199367
199673
  onProgress,
199368
- onFileFinish
199369
- };
199370
- return this.uploadFilesWithRetry({
199371
- uploadFiles,
199372
- options: params,
199373
- times: retryCount,
199374
- interval: retryInterval,
199375
- failedFiles: []
199674
+ onFileFinish,
199675
+ fileId,
199676
+ parallel,
199677
+ retryCount,
199678
+ retryInterval
199376
199679
  });
199377
199680
  }
199378
- /**
199379
- * 批量上传文件
199380
- * @param options
199381
- */
199382
199681
  async uploadFilesCustom(options) {
199682
+ var _a;
199383
199683
  const { files, bucket, region, ignore, onProgress, onFileFinish, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
199384
199684
  if (!files || !files.length) {
199385
199685
  return;
@@ -199393,6 +199693,146 @@ class StorageService {
199393
199693
  };
199394
199694
  })
199395
199695
  .filter(item => ((ignore === null || ignore === void 0 ? void 0 : ignore.length) ? !micromatch_1.default.isMatch(item.filePath, ignore) : true));
199696
+ const totalFiles = fileList.length;
199697
+ console.log(`[uploadFiles] 准备上传 ${totalFiles} 个文件`);
199698
+ // 模式 1:完全串行模式(最稳定)
199699
+ if (process.env.COS_UPLOAD_SERIAL === 'true') {
199700
+ console.log(`[uploadFiles] 串行安全模式已启用`);
199701
+ const serialResult = await this.uploadFilesSerial({
199702
+ fileList,
199703
+ bucket,
199704
+ region,
199705
+ fileId,
199706
+ retryCount,
199707
+ retryInterval,
199708
+ onFileFinish
199709
+ });
199710
+ if (serialResult.failedFiles.length > 0) {
199711
+ throw new error_1.CloudBaseError(`[uploadFiles] 串行上传仍失败 (${serialResult.failedFiles.length} 个):\n` +
199712
+ serialResult.failedFiles.map(f => `- ${f.cloudFileKey}: ${f.error}`).join('\n'), { code: 'HOSTING_DEPLOY_FAILED' });
199713
+ }
199714
+ return {
199715
+ successCount: serialResult.successCount,
199716
+ totalFiles,
199717
+ failedFiles: []
199718
+ };
199719
+ }
199720
+ // 模式 2:智能混合模式(默认)
199721
+ // - 先并行快速上传
199722
+ // - 失败的文件自动走串行重试
199723
+ console.log(`[uploadFiles] 智能混合模式:先并行快速上传,失败文件自动串行重试`);
199724
+ // 第一步:并行上传(分批)
199725
+ const parallelFailedFiles = await this.uploadFilesParallel({
199726
+ fileList,
199727
+ bucket,
199728
+ region,
199729
+ fileId,
199730
+ parallel,
199731
+ retryCount: 0, // 并行阶段不重试,失败后交给串行阶段
199732
+ retryInterval,
199733
+ onFileFinish
199734
+ });
199735
+ // 第二步:串行重试失败的文件
199736
+ if (parallelFailedFiles && parallelFailedFiles.length > 0) {
199737
+ console.warn(`[uploadFiles] 并行阶段 ${parallelFailedFiles.length} 个文件失败,自动切换串行重试...`);
199738
+ const serialResult = await this.uploadFilesSerial({
199739
+ fileList: parallelFailedFiles,
199740
+ bucket,
199741
+ region,
199742
+ fileId: false, // 串行重试不需要再获取 fileId
199743
+ retryCount,
199744
+ retryInterval,
199745
+ onFileFinish
199746
+ });
199747
+ // 检查是否还有失败
199748
+ if (((_a = serialResult === null || serialResult === void 0 ? void 0 : serialResult.failedFiles) === null || _a === void 0 ? void 0 : _a.length) > 0) {
199749
+ throw new error_1.CloudBaseError(`[uploadFiles] 串行重试后仍失败 (${serialResult.failedFiles.length} 个):\n` +
199750
+ serialResult.failedFiles.map(f => `- ${f.cloudFileKey}: ${f.error}`).join('\n'), { code: 'HOSTING_DEPLOY_FAILED' });
199751
+ }
199752
+ }
199753
+ console.log(`[uploadFiles] 全部完成!共处理 ${totalFiles} 个文件`);
199754
+ return {
199755
+ successCount: totalFiles,
199756
+ totalFiles,
199757
+ failedFiles: []
199758
+ };
199759
+ }
199760
+ /**
199761
+ * 串行安全模式:逐文件上传(类似自定义脚本 deploy.js 的方式)
199762
+ * 特点:
199763
+ * - 每个文件独立 HTTP 连接,不会出现 socket hang up
199764
+ * - 单线程串行,稳定性最高
199765
+ * - 适合大规模目录(>1000 文件)或有网络问题的场景
199766
+ */
199767
+ // eslint-disable-next-line @typescript-eslint/member-ordering
199768
+ async uploadFilesSerial(options) {
199769
+ const { fileList, bucket, region, fileId, retryCount, retryInterval, onFileFinish } = options;
199770
+ let uploadedCount = 0;
199771
+ let failedFiles = [];
199772
+ for (let i = 0; i < fileList.length; i++) {
199773
+ const file = fileList[i];
199774
+ let success = false;
199775
+ // 重试机制
199776
+ for (let attempt = 0; attempt <= retryCount; attempt++) {
199777
+ try {
199778
+ // 每个文件创建新的 COS 实例(确保连接隔离)
199779
+ const cos = this.getCos(1); // 严格单并发
199780
+ const putObject = util_1.default.promisify(cos.putObject).bind(cos);
199781
+ await putObject({
199782
+ Bucket: bucket,
199783
+ Region: region,
199784
+ Key: file.cloudFileKey,
199785
+ Body: fs_1.default.createReadStream(file.filePath),
199786
+ ContentLength: fs_1.default.statSync(file.filePath).size,
199787
+ });
199788
+ uploadedCount++;
199789
+ // 显示进度(每 100 个文件或最后 5 个)
199790
+ if (uploadedCount % 100 === 0 || fileList.length - i <= 5) {
199791
+ console.log(`[serial-upload] [${uploadedCount}/${fileList.length}] ${file.cloudFileKey}`);
199792
+ }
199793
+ success = true;
199794
+ // 回调成功
199795
+ if (onFileFinish) {
199796
+ onFileFinish(null, {}, file);
199797
+ }
199798
+ break;
199799
+ }
199800
+ catch (error) {
199801
+ const isLastAttempt = attempt === retryCount;
199802
+ if (isLastAttempt) {
199803
+ console.error(`[serial-upload] [${i + 1}/${fileList.length}] ${file.cloudFileKey}: ${error.message || error.code}`);
199804
+ // 记录失败的文件
199805
+ failedFiles.push(Object.assign(Object.assign({}, file), { error: error.message || String(error) }));
199806
+ // 回调失败
199807
+ if (onFileFinish) {
199808
+ onFileFinish(error, null, file);
199809
+ }
199810
+ }
199811
+ else {
199812
+ // 等待后重试
199813
+ await new Promise(resolve => { setTimeout(resolve, retryInterval); });
199814
+ }
199815
+ }
199816
+ }
199817
+ }
199818
+ console.log(`[serial-upload] 完成:成功 ${uploadedCount}/${fileList.length},失败 ${failedFiles.length}`);
199819
+ return {
199820
+ successCount: uploadedCount,
199821
+ failedFiles
199822
+ };
199823
+ }
199824
+ /**
199825
+ * 并发模式:使用 cos.uploadFiles() 批量上传
199826
+ * 特点:
199827
+ * - 速度快,利用 SDK 内部并行能力
199828
+ * - 分批上传 + 自动刷新凭证
199829
+ * - 适合小规模目录或网络良好的场景
199830
+ *
199831
+ * @returns 失败的文件列表(空的表示全部成功)
199832
+ */
199833
+ // eslint-disable-next-line @typescript-eslint/member-ordering
199834
+ async uploadFilesParallel(options) {
199835
+ const { fileList, bucket, region, fileId, parallel, retryCount, retryInterval, onProgress, onFileFinish } = options;
199396
199836
  // 生成上传文件属性
199397
199837
  const tasks = fileList.map(stats => async () => {
199398
199838
  let cosFileId;
@@ -199408,31 +199848,89 @@ class StorageService {
199408
199848
  'x-cos-meta-fileid': cosFileId
199409
199849
  };
199410
199850
  });
199411
- // 控制请求并发
199851
+ // 控制请求并发(获取 metadata 阶段)
199412
199852
  const asyncTaskController = new parallel_1.AsyncTaskParallelController(parallel, 50);
199413
199853
  asyncTaskController.loadTasks(tasks);
199414
- fileList = await asyncTaskController.run();
199415
- const cos = this.getCos(parallel);
199416
- const uploadFiles = util_1.default.promisify(cos.uploadFiles).bind(cos);
199417
- const params = {
199418
- files: fileList,
199419
- SliceSize: BIG_FILE_SIZE,
199420
- onProgress,
199421
- onFileFinish
199422
- };
199423
- // return uploadFiles({
199424
- // onProgress,
199425
- // onFileFinish,
199426
- // files: fileList,
199427
- // SliceSize: BIG_FILE_SIZE
199428
- // })
199429
- return this.uploadFilesWithRetry({
199430
- uploadFiles,
199431
- options: params,
199432
- times: retryCount,
199433
- interval: retryInterval,
199434
- failedFiles: []
199435
- });
199854
+ const processedFileList = await asyncTaskController.run();
199855
+ // 分批上传,避免单次上传时间过长导致 STS 临时凭证过期
199856
+ // 默认每批 500 个文件,可通过环境变量 COS_UPLOAD_BATCH_SIZE 自定义
199857
+ const BATCH_SIZE = process.env.COS_UPLOAD_BATCH_SIZE
199858
+ ? parseInt(process.env.COS_UPLOAD_BATCH_SIZE, 10)
199859
+ : 500;
199860
+ // 收集所有批次失败的文件
199861
+ let allFailedFiles = [];
199862
+ for (let batchStart = 0; batchStart < processedFileList.length; batchStart += BATCH_SIZE) {
199863
+ const batchEnd = Math.min(batchStart + BATCH_SIZE, processedFileList.length);
199864
+ const batchFiles = processedFileList.slice(batchStart, batchEnd);
199865
+ console.log(`[parallel-upload] 正在上传第 ${Math.floor(batchStart / BATCH_SIZE) + 1} 批次 (文件 ${batchStart + 1}-${batchEnd}/${processedFileList.length})`);
199866
+ // 每批次重新获取 COS 实例(刷新 STS 临时凭证)
199867
+ const cos = this.getCos(parallel);
199868
+ const uploadFiles = util_1.default.promisify(cos.uploadFiles).bind(cos);
199869
+ // 跟踪本批次中真正失败的文件(通过 onFileFinish 回调)
199870
+ const batchFailedFiles = [];
199871
+ const batchSuccessFiles = new Set();
199872
+ const params = {
199873
+ files: batchFiles,
199874
+ SliceSize: BIG_FILE_SIZE,
199875
+ onProgress,
199876
+ onFileFinish: (error, result, file) => {
199877
+ // 空值检查:防止 file 为 null/undefined
199878
+ if (!file) {
199879
+ console.warn('[parallel-upload] onFileFinish received null/undefined file');
199880
+ return;
199881
+ }
199882
+ const fileKey = file.Key || file.cloudFileKey;
199883
+ if (error) {
199884
+ batchFailedFiles.push({
199885
+ filePath: file.FilePath || file.filePath,
199886
+ cloudFileKey: fileKey,
199887
+ error: error.message || String(error)
199888
+ });
199889
+ }
199890
+ else {
199891
+ batchSuccessFiles.add(fileKey);
199892
+ }
199893
+ // 继续调用用户传入的回调(转换参数格式)
199894
+ // 注意:cloudFileKey 和 filePath 是 CLI 期望的格式
199895
+ onFileFinish === null || onFileFinish === void 0 ? void 0 : onFileFinish(error, result, Object.assign(Object.assign({}, file), { cloudFileKey: fileKey, filePath: file.FilePath || file.filePath }));
199896
+ }
199897
+ };
199898
+ try {
199899
+ await this.uploadFilesWithRetry({
199900
+ uploadFiles,
199901
+ options: params,
199902
+ times: retryCount,
199903
+ interval: retryInterval,
199904
+ failedFiles: []
199905
+ });
199906
+ console.log(`[parallel-upload] 第 ${Math.floor(batchStart / BATCH_SIZE) + 1} 批次上传成功`);
199907
+ }
199908
+ catch (error) {
199909
+ console.error(`[parallel-upload] 第 ${Math.floor(batchStart / BATCH_SIZE) + 1} 批次上传失败:`, error.message || error.code || error);
199910
+ // 只记录真正失败的文件(通过 onFileFinish 回调跟踪)
199911
+ // 如果 onFileFinish 没有被调用(如 socket hang up),则标记整个批次为失败
199912
+ if (batchFailedFiles.length === 0 && batchSuccessFiles.size === 0) {
199913
+ // onFileFinish 未被调用,整个批次失败
199914
+ allFailedFiles.push(...batchFiles.map(f => ({
199915
+ filePath: f.FilePath || f.filePath,
199916
+ cloudFileKey: f.Key || f.cloudFileKey,
199917
+ error: error.message || String(error)
199918
+ })));
199919
+ }
199920
+ else {
199921
+ // onFileFinish 已被调用,只记录失败的文件
199922
+ allFailedFiles.push(...batchFailedFiles);
199923
+ }
199924
+ }
199925
+ }
199926
+ // 返回失败文件列表(不抛异常,让调用方决定是否走串行重试)
199927
+ if (allFailedFiles.length > 0) {
199928
+ console.warn(`[parallel-upload] 共 ${allFailedFiles.length}/${processedFileList.length} 个文件并行上传失败,等待串行重试...`);
199929
+ }
199930
+ else {
199931
+ console.log(`[parallel-upload] 全部 ${processedFileList.length} 个文件并行上传成功`);
199932
+ }
199933
+ return allFailedFiles;
199436
199934
  }
199437
199935
  /**
199438
199936
  * 创建一个空的文件夹
@@ -201624,6 +202122,7 @@ class StorageService {
201624
202122
  }
201625
202123
  /**
201626
202124
  * 获取 COS 配置
202125
+ * @param parallel 并发数量
201627
202126
  */
201628
202127
  getCos(parallel = 20) {
201629
202128
  const internalEndpoint = this.environment.cloudBaseContext.isInternalEndpoint();
@@ -201869,6 +202368,16 @@ __decorate([
201869
202368
  ], StorageService.prototype, "uploadDirectoryCustom", null);
201870
202369
  __decorate([
201871
202370
  (0, utils_1.preLazy)()
202371
+ /**
202372
+ * 上传文件(支持精准重试失败文件)
202373
+ *
202374
+ * 智能混合模式(默认):
202375
+ * 1. 先并行快速上传(速度快)
202376
+ * 2. 失败的文件自动走串行重试(稳定性高)
202377
+ *
202378
+ * 可选模式:
202379
+ * - COS_UPLOAD_SERIAL=true:完全串行模式(最稳定但慢)
202380
+ */
201872
202381
  ], StorageService.prototype, "uploadFilesCustom", null);
201873
202382
  __decorate([
201874
202383
  (0, utils_1.preLazy)()
@@ -202679,6 +203188,367 @@ Promise.filter = function (promises, fn, options) {
202679
203188
  };
202680
203189
 
202681
203190
 
203191
+ /***/ }),
203192
+
203193
+ /***/ 72785:
203194
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
203195
+
203196
+ "use strict";
203197
+
203198
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
203199
+ if (k2 === undefined) k2 = k;
203200
+ var desc = Object.getOwnPropertyDescriptor(m, k);
203201
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
203202
+ desc = { enumerable: true, get: function() { return m[k]; } };
203203
+ }
203204
+ Object.defineProperty(o, k2, desc);
203205
+ }) : (function(o, m, k, k2) {
203206
+ if (k2 === undefined) k2 = k;
203207
+ o[k2] = m[k];
203208
+ }));
203209
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
203210
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
203211
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
203212
+ 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;
203213
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
203214
+ };
203215
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
203216
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
203217
+ };
203218
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
203219
+ exports.IntegrationService = void 0;
203220
+ const error_1 = __webpack_require__(40430);
203221
+ const utils_1 = __webpack_require__(62358);
203222
+ const constants_1 = __webpack_require__(79640);
203223
+ const crypto_1 = __webpack_require__(25068);
203224
+ const secretKey_1 = __webpack_require__(22573);
203225
+ __exportStar(__webpack_require__(18944), exports);
203226
+ /**
203227
+ * 集成中心(UserKey)服务
203228
+ *
203229
+ * 复用现有 tcb 云 API(version 2018-06-08),统一携带 Business='integration'。
203230
+ *
203231
+ * 提供两种使用模式:
203232
+ * 1. 透传模式:直接传 `ext`(JSON 字符串),SDK 原样传递给云 API(向后兼容)
203233
+ * 2. 自动加解密模式:传 `envVariables`(明文键值对),SDK 自动识别模板中的
203234
+ * password 字段、拉取七彩石密钥完成加密后拼装 ext
203235
+ */
203236
+ class IntegrationService {
203237
+ constructor(environment) {
203238
+ this.environment = environment;
203239
+ this.tcbService = new utils_1.CloudService(environment.cloudBaseContext, 'tcb', '2018-06-08');
203240
+ }
203241
+ // =========== 只读 API ===========
203242
+ /** 列出集成模板(密钥对模版列表) */
203243
+ async listTemplates(params = {}) {
203244
+ var _a, _b, _c;
203245
+ const { envId } = this.getEnvInfo();
203246
+ return this.tcbService.request('GetUserKeyTemplateList', {
203247
+ EnvId: envId,
203248
+ Offset: (_a = params.offset) !== null && _a !== void 0 ? _a : 0,
203249
+ Limit: (_b = params.limit) !== null && _b !== void 0 ? _b : 100,
203250
+ AuthTypeCode: params.authTypeCode,
203251
+ AuthTypeName: params.authTypeName,
203252
+ Business: (_c = params.business) !== null && _c !== void 0 ? _c : constants_1.USER_KEY_BUSINESS // 默认 integration
203253
+ });
203254
+ }
203255
+ /** 列出集成实例(密钥对列表) */
203256
+ async listUserKeys(params = {}) {
203257
+ var _a, _b, _c;
203258
+ const { envId } = this.getEnvInfo();
203259
+ return this.tcbService.request('GetUserKeyList', {
203260
+ EnvId: envId,
203261
+ Offset: (_a = params.offset) !== null && _a !== void 0 ? _a : 0,
203262
+ Limit: (_b = params.limit) !== null && _b !== void 0 ? _b : 100,
203263
+ Name: params.name,
203264
+ AuthTypeCode: params.authTypeCode,
203265
+ Business: (_c = params.business) !== null && _c !== void 0 ? _c : constants_1.USER_KEY_BUSINESS // 默认 integration
203266
+ });
203267
+ }
203268
+ /**
203269
+ * 查询单个集成实例详情。
203270
+ *
203271
+ * 当 `decryptPwd=true` 时,SDK 自动拉取模板识别 password 字段并从七彩石
203272
+ * 获取 AES 密钥解密,Ext 中 password 字段返回明文。解密失败时保留密文原值,
203273
+ * 不中断流程。
203274
+ */
203275
+ async getUserKey(params) {
203276
+ var _a, _b, _c;
203277
+ this.validateRequiredParams(params, ['keyId']);
203278
+ const { envId } = this.getEnvInfo();
203279
+ const result = await this.tcbService.request('GetOneUserKey', {
203280
+ EnvId: envId,
203281
+ KeyID: params.keyId,
203282
+ NeedPwd: (_a = params.needPwd) !== null && _a !== void 0 ? _a : false,
203283
+ Business: (_b = params.business) !== null && _b !== void 0 ? _b : constants_1.USER_KEY_BUSINESS // 默认 integration
203284
+ });
203285
+ // 自动解密 password 字段
203286
+ if (params.decryptPwd && ((_c = result.UserKeyInfo) === null || _c === void 0 ? void 0 : _c.Ext)) {
203287
+ result.UserKeyInfo = await this.decryptExtIfNeeded(result.UserKeyInfo, envId);
203288
+ }
203289
+ return result;
203290
+ }
203291
+ // =========== 写 API ===========
203292
+ /**
203293
+ * 创建集成实例。
203294
+ *
203295
+ * 支持两种传参方式:
203296
+ * - 传 `ext`:透传模式,原样传递给云 API(向后兼容,调用方自行加密)
203297
+ * - 传 `envVariables`:自动加密模式,SDK 识别模板 password 字段并加密后拼装 ext
203298
+ * - 两者都传时以 `ext` 优先
203299
+ */
203300
+ async createUserKey(params) {
203301
+ var _a;
203302
+ this.validateRequiredParams(params, ['name', 'authTypeCode']);
203303
+ const { envId } = this.getEnvInfo();
203304
+ const ext = await this.buildExtFromParams(envId, params.authTypeCode, { ext: params.ext, envVariables: params.envVariables });
203305
+ return this.tcbService.request('CreateUserKey', {
203306
+ EnvId: envId,
203307
+ Name: params.name,
203308
+ KeyID: params.keyId,
203309
+ AuthTypeCode: params.authTypeCode,
203310
+ Description: params.description,
203311
+ Ext: ext,
203312
+ TicketID: params.ticketId,
203313
+ Business: (_a = params.business) !== null && _a !== void 0 ? _a : constants_1.USER_KEY_BUSINESS, // 默认 integration
203314
+ DemoCodeFunctionName: params.demoCodeFunctionName,
203315
+ RuntimeConfig: params.runtimeConfig
203316
+ });
203317
+ }
203318
+ /**
203319
+ * 更新集成实例。
203320
+ *
203321
+ * 支持两种传参方式:
203322
+ * - 传 `ext`:透传模式,原样传递给云 API(向后兼容)
203323
+ * - 传 `envVariables`:自动加密模式,与现有 Ext 合并后加密写入
203324
+ * - 两者都传时以 `ext` 优先
203325
+ * - 都未传时后端保留原有 Ext 不变
203326
+ */
203327
+ async updateUserKey(params) {
203328
+ var _a;
203329
+ this.validateRequiredParams(params, ['keyId']);
203330
+ const { envId } = this.getEnvInfo();
203331
+ let ext = params.ext;
203332
+ // 传了 envVariables 且未传 ext:需要合并现有 Ext 后加密
203333
+ if (params.envVariables && !params.ext) {
203334
+ ext = await this.buildMergedExtForUpdate(envId, params.keyId, { authTypeCode: params.authTypeCode, envVariables: params.envVariables });
203335
+ }
203336
+ return this.tcbService.request('UpdateUserKey', {
203337
+ EnvId: envId,
203338
+ KeyID: params.keyId,
203339
+ Name: params.name,
203340
+ AuthTypeCode: params.authTypeCode,
203341
+ Description: params.description,
203342
+ Ext: ext,
203343
+ DemoCodeFunctionName: params.demoCodeFunctionName,
203344
+ Business: (_a = params.business) !== null && _a !== void 0 ? _a : constants_1.USER_KEY_BUSINESS, // 默认 integration
203345
+ RuntimeConfig: params.runtimeConfig
203346
+ });
203347
+ }
203348
+ /** 删除集成实例 */
203349
+ async deleteUserKey(params) {
203350
+ this.validateRequiredParams(params, ['keyId']);
203351
+ const { envId } = this.getEnvInfo();
203352
+ return this.tcbService.request('DeleteUserKey', {
203353
+ EnvId: envId,
203354
+ KeyID: params.keyId
203355
+ });
203356
+ }
203357
+ // =========== 辅助方法 ===========
203358
+ /** 获取环境信息 */
203359
+ getEnvInfo() {
203360
+ const envConfig = this.environment.lazyEnvironmentConfig;
203361
+ return {
203362
+ envId: envConfig.EnvId
203363
+ };
203364
+ }
203365
+ /** 校验必填参数 */
203366
+ validateRequiredParams(params, fields) {
203367
+ for (const field of fields) {
203368
+ if (!params[field]) {
203369
+ throw new error_1.CloudBaseError(`${field} 是必填参数`);
203370
+ }
203371
+ }
203372
+ }
203373
+ /**
203374
+ * 根据 ext / envVariables 参数构造最终的 Ext JSON 字符串。
203375
+ * ext 优先;envVariables 由 SDK 自动加密 password 字段。
203376
+ */
203377
+ async buildExtFromParams(envId, authTypeCode, opts) {
203378
+ // ext 优先(透传模式)
203379
+ if (opts.ext !== undefined) {
203380
+ return opts.ext;
203381
+ }
203382
+ // 自动加密模式
203383
+ if (opts.envVariables && Object.keys(opts.envVariables).length > 0) {
203384
+ return this.encryptAndStringify(envId, authTypeCode, opts.envVariables);
203385
+ }
203386
+ return undefined;
203387
+ }
203388
+ /**
203389
+ * 更新场景:将 envVariables 与现有实例的 Ext 合并后加密。
203390
+ * 需要先查询现有实例获取当前的 authTypeCode 和 Ext。
203391
+ */
203392
+ async buildMergedExtForUpdate(envId, keyId, opts) {
203393
+ var _a, _b;
203394
+ const { envVariables, authTypeCode } = opts;
203395
+ if (!envVariables || Object.keys(envVariables).length === 0) {
203396
+ return undefined;
203397
+ }
203398
+ // 查询现有实例以合并 Ext
203399
+ let existingExtObj = {};
203400
+ let resolvedAuthTypeCode = authTypeCode;
203401
+ try {
203402
+ const existing = await this.getUserKey({
203403
+ keyId,
203404
+ needPwd: true,
203405
+ decryptPwd: true, // 解密后方便合并
203406
+ business: constants_1.USER_KEY_BUSINESS
203407
+ });
203408
+ const extObj = this.parseExt((_a = existing.UserKeyInfo) === null || _a === void 0 ? void 0 : _a.Ext);
203409
+ existingExtObj = Object.assign({}, extObj);
203410
+ resolvedAuthTypeCode = resolvedAuthTypeCode || ((_b = existing.UserKeyInfo) === null || _b === void 0 ? void 0 : _b.AuthTypeCode);
203411
+ }
203412
+ catch (_c) {
203413
+ // 查询失败时仅使用传入的 envVariables
203414
+ }
203415
+ if (!resolvedAuthTypeCode) {
203416
+ // 无法获取 authTypeCode,无法识别 password 字段,直接 JSON 序列化
203417
+ return JSON.stringify(Object.assign(Object.assign({}, existingExtObj), envVariables));
203418
+ }
203419
+ // 合并:现有 Ext 为基础,envVariables 覆盖
203420
+ const merged = Object.assign(Object.assign({}, existingExtObj), envVariables);
203421
+ return this.encryptAndStringify(envId, resolvedAuthTypeCode, merged);
203422
+ }
203423
+ /**
203424
+ * 加密 envVariables 中的 password 字段并返回 JSON 字符串。
203425
+ * 无 password 字段时直接 JSON.stringify,不请求七彩石。
203426
+ */
203427
+ async encryptAndStringify(envId, authTypeCode, envVariables) {
203428
+ // 1. 拉取模板,识别 password 字段
203429
+ const passwordFieldNames = await this.getPasswordFieldNames(authTypeCode);
203430
+ // 2. 判断是否有 password 字段需要加密
203431
+ const hasPasswordValue = passwordFieldNames.some(name => envVariables[name]);
203432
+ if (!hasPasswordValue) {
203433
+ return JSON.stringify(envVariables);
203434
+ }
203435
+ // 3. 拉取 AES key 并加密
203436
+ try {
203437
+ const aesKey = await (0, secretKey_1.getAesKey)({ envId });
203438
+ const encrypted = this.encryptPasswordFields(envVariables, passwordFieldNames, aesKey);
203439
+ return JSON.stringify(encrypted);
203440
+ }
203441
+ catch (_a) {
203442
+ // 获取密钥失败时以明文写入(与 CLI 行为一致)
203443
+ return JSON.stringify(envVariables);
203444
+ }
203445
+ }
203446
+ /**
203447
+ * 解密 Ext 中的 password 字段并返回新的 IUserKeyInfo。
203448
+ * 解密失败时保留原值,不中断流程。
203449
+ */
203450
+ async decryptExtIfNeeded(userKeyInfo, envId) {
203451
+ if (!userKeyInfo.Ext || !userKeyInfo.AuthTypeCode)
203452
+ return userKeyInfo;
203453
+ try {
203454
+ const passwordFieldNames = await this.getPasswordFieldNames(userKeyInfo.AuthTypeCode);
203455
+ if (passwordFieldNames.length === 0)
203456
+ return userKeyInfo;
203457
+ const extObj = this.parseExt(userKeyInfo.Ext);
203458
+ if (!Object.keys(extObj).length)
203459
+ return userKeyInfo;
203460
+ const hasPasswordValue = passwordFieldNames.some(name => extObj[name]);
203461
+ if (!hasPasswordValue)
203462
+ return userKeyInfo;
203463
+ const aesKey = await (0, secretKey_1.getAesKey)({ envId });
203464
+ const decrypted = this.decryptPasswordFields(extObj, passwordFieldNames, aesKey);
203465
+ return Object.assign(Object.assign({}, userKeyInfo), { Ext: JSON.stringify(decrypted) });
203466
+ }
203467
+ catch (_a) {
203468
+ return userKeyInfo;
203469
+ }
203470
+ }
203471
+ /** 从模板中获取 password 类型字段名列表 */
203472
+ async getPasswordFieldNames(authTypeCode) {
203473
+ var _a, _b;
203474
+ try {
203475
+ const res = await this.listTemplates({ authTypeCode, limit: 1 });
203476
+ const templateExt = (_b = (_a = res.KeyTemplateList) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.Ext;
203477
+ if (!templateExt)
203478
+ return [];
203479
+ const parsed = JSON.parse(templateExt);
203480
+ const fields = Array.isArray(parsed === null || parsed === void 0 ? void 0 : parsed.fields) ? parsed.fields : [];
203481
+ return fields
203482
+ .filter((f) => f && f.type === 'password' && typeof f.name === 'string')
203483
+ .map((f) => f.name);
203484
+ }
203485
+ catch (_c) {
203486
+ return [];
203487
+ }
203488
+ }
203489
+ /** 解析 Ext JSON 字符串为对象,失败返回空对象 */
203490
+ parseExt(ext) {
203491
+ if (!ext)
203492
+ return {};
203493
+ try {
203494
+ const parsed = JSON.parse(ext);
203495
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
203496
+ }
203497
+ catch (_a) {
203498
+ return {};
203499
+ }
203500
+ }
203501
+ /** 加密 ext 对象中的 password 字段(仅加密有值的字段),返回新对象 */
203502
+ encryptPasswordFields(extObj, passwordFieldNames, aesKey) {
203503
+ const result = Object.assign({}, extObj);
203504
+ passwordFieldNames.forEach(name => {
203505
+ const value = result[name];
203506
+ if (name && value) {
203507
+ result[name] = crypto_1.AESCrypto.encrypt(value, aesKey);
203508
+ }
203509
+ });
203510
+ return result;
203511
+ }
203512
+ /** 解密 ext 对象中的 password 字段,解密失败保留原值,返回新对象 */
203513
+ decryptPasswordFields(extObj, passwordFieldNames, aesKey) {
203514
+ const result = Object.assign({}, extObj);
203515
+ const MIN_CIPHERTEXT_LEN = 24; // base64(16 字节 IV) = 24 字符
203516
+ passwordFieldNames.forEach(name => {
203517
+ const encrypted = result[name];
203518
+ if (typeof encrypted !== 'string' || encrypted.length <= MIN_CIPHERTEXT_LEN) {
203519
+ return;
203520
+ }
203521
+ try {
203522
+ result[name] = crypto_1.AESCrypto.decrypt(encrypted, aesKey);
203523
+ }
203524
+ catch (_a) {
203525
+ // 解密失败保留原值,不中断流程
203526
+ }
203527
+ });
203528
+ return result;
203529
+ }
203530
+ }
203531
+ exports.IntegrationService = IntegrationService;
203532
+ __decorate([
203533
+ (0, utils_1.preLazy)()
203534
+ ], IntegrationService.prototype, "listTemplates", null);
203535
+ __decorate([
203536
+ (0, utils_1.preLazy)()
203537
+ ], IntegrationService.prototype, "listUserKeys", null);
203538
+ __decorate([
203539
+ (0, utils_1.preLazy)()
203540
+ ], IntegrationService.prototype, "getUserKey", null);
203541
+ __decorate([
203542
+ (0, utils_1.preLazy)()
203543
+ ], IntegrationService.prototype, "createUserKey", null);
203544
+ __decorate([
203545
+ (0, utils_1.preLazy)()
203546
+ ], IntegrationService.prototype, "updateUserKey", null);
203547
+ __decorate([
203548
+ (0, utils_1.preLazy)()
203549
+ ], IntegrationService.prototype, "deleteUserKey", null);
203550
+
203551
+
202682
203552
  /***/ }),
202683
203553
 
202684
203554
  /***/ 72923:
@@ -213100,7 +213970,7 @@ function registerSetupTools(server) {
213100
213970
  server.registerTool("downloadTemplate", {
213101
213971
  title: "下载项目模板",
213102
213972
  description: `自动下载并部署CloudBase项目模板。
213103
- **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.24.1" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
213973
+ **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.1" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
213104
213974
  inputSchema: {
213105
213975
  template: zod_1.z
213106
213976
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -221481,6 +222351,22 @@ module.exports = typeof queueMicrotask === 'function'
221481
222351
  .catch(err => setTimeout(() => { throw err }, 0))
221482
222352
 
221483
222353
 
222354
+ /***/ }),
222355
+
222356
+ /***/ 79640:
222357
+ /***/ ((__unused_webpack_module, exports) => {
222358
+
222359
+ "use strict";
222360
+
222361
+ /**
222362
+ * 集成中心常量定义(与 dev-platform 对齐)
222363
+ */
222364
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
222365
+ exports.USER_KEY_BUSINESS = void 0;
222366
+ /** 业务类型标识:集成中心来源的密钥对 */
222367
+ exports.USER_KEY_BUSINESS = 'integration';
222368
+
222369
+
221484
222370
  /***/ }),
221485
222371
 
221486
222372
  /***/ 79646:
@@ -230184,21 +231070,23 @@ const zod_1 = __webpack_require__(21614);
230184
231070
  const cloudbase_manager_js_1 = __webpack_require__(3431);
230185
231071
  const json_content_js_1 = __webpack_require__(4365);
230186
231072
  const QUERY_GATEWAY_ACTIONS = [
230187
- "getAccess",
230188
- "listDomains",
230189
231073
  "listRoutes",
230190
231074
  "getRoute",
230191
231075
  "listCustomDomains",
230192
231076
  ];
230193
231077
  const MANAGE_GATEWAY_ACTIONS = [
230194
- "createAccess",
230195
231078
  "createRoute",
230196
231079
  "updateRoute",
230197
231080
  "deleteRoute",
230198
231081
  "bindCustomDomain",
230199
231082
  "deleteCustomDomain",
230200
- "deleteAccess",
230201
- "updatePathAuth",
231083
+ ];
231084
+ const UPSTREAM_RESOURCE_TYPES = [
231085
+ "SCF",
231086
+ "WEB_SCF",
231087
+ "CBR",
231088
+ "STATIC_STORE",
231089
+ "LH",
230202
231090
  ];
230203
231091
  function normalizeAccessPath(path) {
230204
231092
  if (!path) {
@@ -230229,24 +231117,11 @@ function registerGatewayTools(server) {
230229
231117
  return (0, json_content_js_1.jsonContent)(buildErrorEnvelope(error));
230230
231118
  }
230231
231119
  };
230232
- const listGatewayDomains = async () => {
230233
- const cloudbase = await getManager();
230234
- const result = await cloudbase.access.getDomainList();
230235
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230236
- const domains = [
230237
- result.DefaultDomain,
230238
- ...(result.ServiceSet || []).map((item) => item.Domain),
230239
- ].filter(Boolean);
230240
- return {
230241
- domains,
230242
- enableService: result.EnableService,
230243
- raw: result,
230244
- };
230245
- };
230246
231120
  const listHttpServiceRoutes = async (domain) => {
230247
231121
  const cloudbase = await getManager();
230248
231122
  const result = await cloudbase.env.describeHttpServiceRoute({
230249
231123
  EnvId: await resolveEnvId(),
231124
+ Limit: 1000,
230250
231125
  ...(domain
230251
231126
  ? {
230252
231127
  Filters: [
@@ -230262,123 +231137,176 @@ function registerGatewayTools(server) {
230262
231137
  return result;
230263
231138
  };
230264
231139
  const flattenRoutes = (result) => (result.Domains ?? []).flatMap((domainItem) => (domainItem.Routes ?? []).map((route) => ({
230265
- Domain: domainItem.Domain,
231140
+ Domain: domainItem.Domain ?? "",
230266
231141
  DomainType: domainItem.DomainType,
230267
231142
  AccessType: domainItem.AccessType,
231143
+ IsDefault: domainItem.IsDefault,
230268
231144
  ...route,
230269
231145
  })));
231146
+ const buildRouteUrls = (routes) => Array.from(new Set(routes
231147
+ .filter((route) => route.Domain && route.Path)
231148
+ .map((route) => `https://${route.Domain}${normalizeAccessPath(String(route.Path))}`)));
231149
+ const resolveDefaultHttpDomain = async () => {
231150
+ const routeInfo = await listHttpServiceRoutes();
231151
+ const domains = routeInfo.Domains ?? [];
231152
+ const defaultCandidates = domains.filter((item) => item.IsDefault === true && item.Domain);
231153
+ const preferred = defaultCandidates.find((item) => item.Enable !== false && item.Status === "SUCCESS") ??
231154
+ defaultCandidates.find((item) => item.Enable !== false) ??
231155
+ defaultCandidates[0];
231156
+ if (!preferred?.Domain) {
231157
+ throw new Error("环境默认 HTTP 访问域名未就绪或未开通。请先在控制台开通 HTTP 访问服务,或用 queryGateway(action=\"listRoutes\") 确认 Domains 中是否存在 IsDefault=true 的域名;也可以显式传入 domain 后重试 createRoute/updateRoute/deleteRoute。");
231158
+ }
231159
+ return preferred.Domain;
231160
+ };
230270
231161
  const resolveRouteDomain = async (preferredDomain) => {
230271
231162
  if (preferredDomain) {
230272
231163
  return preferredDomain;
230273
231164
  }
230274
- const routeInfo = await listHttpServiceRoutes();
230275
- return routeInfo.OriginDomain;
231165
+ return resolveDefaultHttpDomain();
231166
+ };
231167
+ const resolveUpstreamResourceType = (input) => {
231168
+ const resolved = input.routeUpstreamResourceType ?? input.upstreamResourceType;
231169
+ if (resolved) {
231170
+ return resolved;
231171
+ }
231172
+ throw new Error("必须提供 upstreamResourceType(或 route.upstreamResourceType):" +
231173
+ "WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
231174
+ "禁止仅凭 targetName/serviceName 推断上游类型。");
230276
231175
  };
230277
- const normalizeRoutePayload = async (route, fallback, domain) => {
230278
- const path = normalizeAccessPath(route?.path ?? fallback.path);
230279
- const serviceType = route?.serviceType ?? fallback.targetType;
230280
- const serviceName = route?.serviceName ?? fallback.targetName;
230281
- if (!serviceType || !serviceName) {
230282
- throw new Error("route.serviceType 和 route.serviceName 为必填项");
231176
+ const normalizeRoutePayload = async (input) => {
231177
+ const upstreamResourceName = input.route?.serviceName ?? input.targetName;
231178
+ if (!upstreamResourceName) {
231179
+ throw new Error("必须提供 targetName 或 route.serviceName 作为上游资源名称:" +
231180
+ "云函数名、云托管服务名,或静态托管实例名(常见为 staticstore)。");
231181
+ }
231182
+ const upstreamResourceType = resolveUpstreamResourceType({
231183
+ upstreamResourceType: input.upstreamResourceType,
231184
+ routeUpstreamResourceType: input.route?.upstreamResourceType,
231185
+ });
231186
+ const path = normalizeAccessPath(input.route?.path ?? input.path ?? `/${upstreamResourceName}`);
231187
+ const domain = await resolveRouteDomain(input.domain);
231188
+ const enableAuth = input.route?.auth !== undefined
231189
+ ? input.route.auth
231190
+ : input.auth !== undefined
231191
+ ? input.auth
231192
+ : undefined;
231193
+ const enablePathTransmission = input.route?.enablePathTransmission !== undefined
231194
+ ? input.route.enablePathTransmission
231195
+ : input.enablePathTransmission !== undefined
231196
+ ? input.enablePathTransmission
231197
+ : undefined;
231198
+ const route = {
231199
+ Path: path,
231200
+ UpstreamResourceType: upstreamResourceType,
231201
+ UpstreamResourceName: upstreamResourceName,
231202
+ EnableAuth: enableAuth,
231203
+ };
231204
+ if (enablePathTransmission !== undefined) {
231205
+ route.EnablePathTransmission = enablePathTransmission;
230283
231206
  }
230284
231207
  return {
230285
231208
  EnvId: await resolveEnvId(),
230286
231209
  Domain: {
230287
- Domain: await resolveRouteDomain(domain),
230288
- Routes: [
230289
- {
230290
- Path: path,
230291
- UpstreamResourceType: serviceType === "function" ? "SCF" : "CBR",
230292
- UpstreamResourceName: serviceName,
230293
- EnableAuth: route?.auth !== undefined
230294
- ? route.auth
230295
- : fallback.auth !== undefined
230296
- ? fallback.auth
230297
- : undefined,
230298
- },
230299
- ],
231210
+ Domain: domain,
231211
+ Routes: [route],
231212
+ },
231213
+ resolved: {
231214
+ domain,
231215
+ path,
231216
+ upstreamResourceType,
231217
+ upstreamResourceName,
231218
+ enableAuth,
231219
+ enablePathTransmission,
230300
231220
  },
230301
231221
  };
230302
231222
  };
231223
+ const routeMutationNextActions = (targetName) => [
231224
+ {
231225
+ tool: "queryGateway",
231226
+ action: "getRoute",
231227
+ reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
231228
+ },
231229
+ {
231230
+ tool: "queryPermissions",
231231
+ action: "getResourcePermission",
231232
+ reason: "确认函数安全规则是否允许预期访问方;网关 EnableAuth/auth=false 不等于函数已允许匿名访问",
231233
+ },
231234
+ {
231235
+ tool: "managePermissions",
231236
+ action: "updateResourcePermission",
231237
+ reason: "只有在确认需要匿名或浏览器直连访问时,才按实际安全要求更新函数权限",
231238
+ },
231239
+ ];
230303
231240
  const handleQueryGateway = async (input) => {
230304
231241
  switch (input.action) {
230305
- case "listDomains": {
230306
- const result = await listGatewayDomains();
230307
- return buildEnvelope({
230308
- action: input.action,
230309
- domains: result.domains,
230310
- enableService: result.enableService,
230311
- raw: result.raw,
230312
- }, `已获取 ${result.domains.length} 个网关域名`, [
230313
- {
230314
- tool: "queryGateway",
230315
- action: "getAccess",
230316
- reason: "查看某个目标当前的访问入口",
230317
- },
230318
- ]);
230319
- }
230320
231242
  case "listCustomDomains": {
230321
- const result = await listGatewayDomains();
231243
+ const result = await listHttpServiceRoutes();
231244
+ const customDomains = (result.Domains ?? []).filter((item) => item.IsDefault !== true);
230322
231245
  return buildEnvelope({
230323
231246
  action: input.action,
230324
- domains: result.raw.ServiceSet ?? [],
230325
- total: (result.raw.ServiceSet ?? []).length,
230326
- raw: result.raw,
230327
- }, `已获取 ${(result.raw.ServiceSet ?? []).length} 个自定义域名`);
231247
+ domains: customDomains,
231248
+ total: customDomains.length,
231249
+ raw: result,
231250
+ }, `已获取 ${customDomains.length} 个自定义域名`);
230328
231251
  }
230329
231252
  case "listRoutes": {
230330
- const result = await listHttpServiceRoutes();
231253
+ const result = await listHttpServiceRoutes(input.domain);
230331
231254
  const routes = flattenRoutes(result);
230332
231255
  return buildEnvelope({
230333
231256
  action: input.action,
230334
231257
  routes,
231258
+ urls: buildRouteUrls(routes),
230335
231259
  total: result.TotalCount ?? routes.length,
230336
231260
  raw: result,
230337
231261
  }, `已获取 ${result.TotalCount ?? routes.length} 条 HTTP 路由`);
230338
231262
  }
230339
231263
  case "getRoute": {
230340
- const result = await listHttpServiceRoutes();
230341
- const route = flattenRoutes(result).find((item) => (input.routeId && item.RouteId === input.routeId) ||
230342
- (input.targetName && item.UpstreamResourceName === input.targetName)) ?? null;
231264
+ if (!input.routeId && !input.targetName && !input.path) {
231265
+ throw new Error("action=getRoute 时至少需要提供 routeId、targetName 或 path");
231266
+ }
231267
+ const result = await listHttpServiceRoutes(input.domain);
231268
+ const normalizedPath = input.path
231269
+ ? normalizeAccessPath(input.path)
231270
+ : undefined;
231271
+ const matches = flattenRoutes(result).filter((item) => {
231272
+ if (input.routeId && item.RouteId !== input.routeId) {
231273
+ return false;
231274
+ }
231275
+ if (input.domain && item.Domain !== input.domain) {
231276
+ return false;
231277
+ }
231278
+ if (normalizedPath &&
231279
+ normalizeAccessPath(String(item.Path ?? "")) !== normalizedPath) {
231280
+ return false;
231281
+ }
231282
+ if (input.targetName &&
231283
+ item.UpstreamResourceName !== input.targetName) {
231284
+ return false;
231285
+ }
231286
+ return Boolean(input.routeId || input.targetName || normalizedPath);
231287
+ });
231288
+ const route = matches.length === 1 ? matches[0] : null;
231289
+ const urls = buildRouteUrls(matches);
230343
231290
  return buildEnvelope({
230344
231291
  action: input.action,
230345
231292
  routeId: input.routeId ?? null,
231293
+ targetName: input.targetName ?? null,
231294
+ path: normalizedPath ?? null,
231295
+ domain: input.domain ?? null,
230346
231296
  route,
230347
- raw: result,
230348
- }, route ? "已获取路由详情" : "未找到对应路由");
230349
- }
230350
- case "getAccess": {
230351
- if (!input.targetName) {
230352
- throw new Error("getAccess 操作时,targetName 参数是必需的");
230353
- }
230354
- const cloudbase = await getManager();
230355
- const [accessList, domainInfo] = await Promise.all([
230356
- cloudbase.access.getAccessList({ name: input.targetName }),
230357
- listGatewayDomains(),
230358
- ]);
230359
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, accessList);
230360
- const urls = Array.from(new Set((accessList.APISet || []).flatMap((api) => domainInfo.domains.map((domain) => {
230361
- const normalizedPath = normalizeAccessPath(api.Path);
230362
- return `https://${domain}${normalizedPath}`;
230363
- }))));
230364
- return buildEnvelope({
230365
- action: input.action,
230366
- targetType: input.targetType ?? "function",
230367
- targetName: input.targetName,
230368
- apis: accessList.APISet || [],
230369
- total: accessList.Total || 0,
230370
- domains: domainInfo.domains,
231297
+ routes: matches,
231298
+ total: matches.length,
230371
231299
  urls,
230372
- enableService: accessList.EnableService ?? domainInfo.enableService ?? false,
230373
- raw: {
230374
- accessList,
230375
- domainList: domainInfo.raw,
230376
- },
230377
- }, `已获取目标 ${input.targetName} 的网关访问配置`, [
231300
+ raw: result,
231301
+ }, matches.length === 0
231302
+ ? "未找到对应路由"
231303
+ : matches.length === 1
231304
+ ? "已获取路由详情"
231305
+ : `匹配到 ${matches.length} 条路由,请补充 path 或 domain 精确定位`, [
230378
231306
  {
230379
231307
  tool: "manageGateway",
230380
- action: "createAccess",
230381
- reason: "为该目标新增访问路径",
231308
+ action: "createRoute",
231309
+ reason: "为该目标新增 Domain/Route 访问路径",
230382
231310
  },
230383
231311
  ]);
230384
231312
  }
@@ -230388,78 +231316,74 @@ function registerGatewayTools(server) {
230388
231316
  };
230389
231317
  const handleManageGateway = async (input) => {
230390
231318
  switch (input.action) {
230391
- case "createAccess": {
230392
- if (!input.targetName || !input.targetType) {
230393
- throw new Error("action=createAccess 时必须提供 targetType 和 targetName");
230394
- }
230395
- if (!input.type) {
230396
- throw new Error("action=createAccess 且 targetType=function 时必须显式提供 type。HTTP 云函数传 HTTP;Event 函数传 Event。省略会默认按 Event 路由处理,可能让 HTTP 云函数访问后返回 FUNCTION_PARAM_INVALID。");
230397
- }
231319
+ case "createRoute": {
230398
231320
  const cloudbase = await getManager();
230399
- const accessPath = normalizeAccessPath(input.path || `/${input.targetName}`);
231321
+ const payload = await normalizeRoutePayload(input);
230400
231322
  let result;
230401
231323
  try {
230402
- result = await cloudbase.access.createAccess({
230403
- name: input.targetName,
230404
- path: accessPath,
230405
- type: (input.type === "HTTP" ? 6 : 1),
230406
- auth: input.auth,
231324
+ result = await cloudbase.env.createHttpServiceRoute({
231325
+ EnvId: payload.EnvId,
231326
+ Domain: payload.Domain,
230407
231327
  });
230408
231328
  }
230409
231329
  catch (err) {
230410
- if (err.message && err.message.includes("An error has occurred")) {
230411
- let hint = "为目标资源配置访问路由失败(后端内部错误)。请确保:1) 目标云函数已成功创建并处于 Active 状态;2) 环境默认 HTTP 域名已完成初始化;3) 该访问路径未被占用。";
230412
- if (input.type === "HTTP") {
230413
- hint += "此外注意:如果目标函数最初是作为 Event 函数创建的,这里 type 仍必须传 Event;误传 HTTP 会导致此错误。";
231330
+ const message = err instanceof Error ? err.message : String(err);
231331
+ if (message.includes("An error has occurred")) {
231332
+ let hint = "为目标资源配置访问路由失败(后端内部错误)。请确保:1) 目标云函数已成功创建并处于 Active 状态;2) 环境默认 HTTP 域名已完成初始化(IsDefault);3) 该访问路径未被占用。";
231333
+ if (payload.resolved.upstreamResourceType === "WEB_SCF" ||
231334
+ payload.resolved.upstreamResourceType === "SCF") {
231335
+ hint +=
231336
+ "此外注意:HTTP 云函数必须用 upstreamResourceType=WEB_SCF,Event 云函数必须用 SCF;互标会导致此错误。";
230414
231337
  }
230415
- throw new Error(`${hint} 原始错误:${err.message}`);
231338
+ throw new Error(`${hint} 原始错误:${message}`);
230416
231339
  }
230417
231340
  throw err;
230418
231341
  }
230419
231342
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230420
231343
  return buildEnvelope({
230421
231344
  action: input.action,
230422
- targetType: input.targetType,
230423
- targetName: input.targetName,
230424
- path: accessPath,
231345
+ model: "httpServiceRoute",
231346
+ targetName: payload.resolved.upstreamResourceName,
231347
+ domain: payload.resolved.domain,
231348
+ path: payload.resolved.path,
231349
+ upstreamResourceType: payload.resolved.upstreamResourceType,
231350
+ upstreamResourceName: payload.resolved.upstreamResourceName,
231351
+ auth: payload.resolved.enableAuth ?? null,
231352
+ enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
230425
231353
  raw: result,
230426
- }, `已为目标 ${input.targetName} 创建网关访问路径。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。该操作只创建网关入口,不会自动放开函数安全规则;若需要匿名或浏览器直接访问,请继续检查函数资源权限。`, [
230427
- {
230428
- tool: "queryGateway",
230429
- action: "getAccess",
230430
- reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
230431
- },
230432
- {
230433
- tool: "queryPermissions",
230434
- action: "getResourcePermission",
230435
- reason: "确认函数安全规则是否允许预期访问方;网关 auth=false 不等于函数已允许匿名访问",
230436
- },
230437
- {
230438
- tool: "managePermissions",
230439
- action: "updateResourcePermission",
230440
- reason: "只有在确认需要匿名或浏览器直连访问时,才按实际安全要求更新函数权限",
230441
- },
230442
- ]);
230443
- }
230444
- case "createRoute": {
230445
- const cloudbase = await getManager();
230446
- const result = await cloudbase.env.createHttpServiceRoute((await normalizeRoutePayload(input.route, input, input.domain)));
230447
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230448
- return buildEnvelope({
230449
- action: input.action,
230450
- route: input.route ?? null,
230451
- raw: result,
230452
- }, "HTTP 路由创建成功");
231354
+ }, `已为目标 ${payload.resolved.upstreamResourceName} 在域名 ${payload.resolved.domain} 创建路由 ${payload.resolved.path}(${payload.resolved.upstreamResourceType})` +
231355
+ (payload.resolved.enablePathTransmission === true
231356
+ ? ";已开启路径透传(后端收到完整请求路径)"
231357
+ : payload.resolved.enablePathTransmission === false
231358
+ ? ";路径透传关闭(网关会剥掉触发路径前缀后再转发给后端)"
231359
+ : ";路径透传未显式设置(平台默认 false,会剥掉触发路径前缀)") +
231360
+ `。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。该操作只创建网关入口,不会自动放开上游权限;若上游是云函数且需要匿名或浏览器直接访问,请继续检查函数资源权限。`, routeMutationNextActions(payload.resolved.upstreamResourceName));
230453
231361
  }
230454
231362
  case "updateRoute": {
230455
231363
  const cloudbase = await getManager();
230456
- const result = await cloudbase.env.modifyHttpServiceRoute((await normalizeRoutePayload(input.route, input, input.domain)));
231364
+ const payload = await normalizeRoutePayload(input);
231365
+ const result = await cloudbase.env.modifyHttpServiceRoute({
231366
+ EnvId: payload.EnvId,
231367
+ Domain: payload.Domain,
231368
+ });
230457
231369
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230458
231370
  return buildEnvelope({
230459
231371
  action: input.action,
230460
- route: input.route ?? null,
231372
+ model: "httpServiceRoute",
231373
+ domain: payload.resolved.domain,
231374
+ path: payload.resolved.path,
231375
+ upstreamResourceType: payload.resolved.upstreamResourceType,
231376
+ upstreamResourceName: payload.resolved.upstreamResourceName,
231377
+ auth: payload.resolved.enableAuth ?? null,
231378
+ enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
230461
231379
  raw: result,
230462
- }, "HTTP 路由更新成功");
231380
+ }, `HTTP 路由更新成功(${payload.resolved.domain}${payload.resolved.path}` +
231381
+ (payload.resolved.enablePathTransmission === true
231382
+ ? ",路径透传=开启"
231383
+ : payload.resolved.enablePathTransmission === false
231384
+ ? ",路径透传=关闭"
231385
+ : "") +
231386
+ `)`, routeMutationNextActions(payload.resolved.upstreamResourceName));
230463
231387
  }
230464
231388
  case "deleteRoute": {
230465
231389
  const routePath = input.route?.path ?? input.path;
@@ -230468,16 +231392,18 @@ function registerGatewayTools(server) {
230468
231392
  }
230469
231393
  const cloudbase = await getManager();
230470
231394
  const domain = await resolveRouteDomain(input.domain);
231395
+ const normalizedPath = normalizeAccessPath(routePath);
230471
231396
  const result = await cloudbase.env.deleteHttpServiceRoute({
230472
231397
  EnvId: await resolveEnvId(),
230473
231398
  Domain: domain,
230474
- Paths: [normalizeAccessPath(routePath)],
231399
+ Paths: [normalizedPath],
230475
231400
  });
230476
231401
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230477
231402
  return buildEnvelope({
230478
231403
  action: input.action,
231404
+ model: "httpServiceRoute",
230479
231405
  domain,
230480
- path: normalizeAccessPath(routePath),
231406
+ path: normalizedPath,
230481
231407
  raw: result,
230482
231408
  }, "HTTP 路由删除成功");
230483
231409
  }
@@ -230517,114 +231443,32 @@ function registerGatewayTools(server) {
230517
231443
  raw: result,
230518
231444
  }, "自定义域名删除成功");
230519
231445
  }
230520
- case "deleteAccess": {
230521
- const cloudbase = await getManager();
230522
- const accessPath = input.path ? normalizeAccessPath(input.path) : undefined;
230523
- let resolvedAccessId = input.accessId;
230524
- let resolvedName;
230525
- if (!resolvedAccessId) {
230526
- if (!input.targetName && !accessPath) {
230527
- throw new Error("action=deleteAccess 时至少需要提供 accessId,或提供 targetName/path 用于定位入口");
230528
- }
230529
- const accessList = await cloudbase.access.getAccessList({
230530
- ...(input.targetName ? { name: input.targetName } : {}),
230531
- ...(accessPath ? { path: accessPath } : {}),
230532
- });
230533
- const candidates = (accessList.APISet ?? []).filter((item) => {
230534
- if (!accessPath) {
230535
- return true;
230536
- }
230537
- return normalizeAccessPath(item.Path) === accessPath;
230538
- });
230539
- if (candidates.length === 0) {
230540
- throw new Error("未找到可删除的访问入口,请确认 targetName/path 或直接传 accessId");
230541
- }
230542
- if (candidates.length > 1 && !accessPath) {
230543
- throw new Error("匹配到多个访问入口,请补充 path 或直接传 accessId");
230544
- }
230545
- resolvedAccessId = candidates[0]?.APIId;
230546
- resolvedName = candidates[0]?.Name;
230547
- }
230548
- else {
230549
- // 仅传入 accessId 时,先查询获取 name 等信息,确保删除参数完整
230550
- try {
230551
- const accessList = await cloudbase.access.getAccessList({});
230552
- const match = (accessList.APISet ?? []).find((item) => item.APIId === resolvedAccessId);
230553
- if (match) {
230554
- resolvedName = match.Name;
230555
- }
230556
- }
230557
- catch {
230558
- // 查询失败不影响主流程,继续用 accessId 删除
230559
- }
230560
- }
230561
- if (!resolvedAccessId) {
230562
- throw new Error("无法解析访问入口 accessId,请改为显式传入 accessId");
230563
- }
230564
- const result = await cloudbase.access.deleteAccess({
230565
- ...(resolvedName ? { name: resolvedName } : {}),
230566
- apiId: resolvedAccessId,
230567
- });
230568
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230569
- return buildEnvelope({
230570
- action: input.action,
230571
- targetName: resolvedName ?? input.targetName ?? null,
230572
- path: accessPath ?? null,
230573
- accessId: resolvedAccessId,
230574
- raw: result,
230575
- }, "网关访问入口删除成功");
230576
- }
230577
- case "updatePathAuth": {
230578
- if (!input.targetName && !input.path) {
230579
- throw new Error("action=updatePathAuth 时至少需要提供 targetName 或 path");
230580
- }
230581
- if (input.auth === undefined) {
230582
- throw new Error("action=updatePathAuth 时必须提供 auth");
230583
- }
230584
- const cloudbase = await getManager();
230585
- const accessPath = input.path ? normalizeAccessPath(input.path) : undefined;
230586
- const accessList = await cloudbase.access.getAccessList({
230587
- ...(input.targetName ? { name: input.targetName } : {}),
230588
- ...(accessPath ? { path: accessPath } : {}),
230589
- });
230590
- const apiIds = (accessList.APISet ?? []).map((item) => item.APIId).filter(Boolean);
230591
- if (apiIds.length === 0) {
230592
- throw new Error("未找到可更新鉴权状态的访问入口");
230593
- }
230594
- const result = await cloudbase.access.switchPathAuth({
230595
- apiIds,
230596
- auth: input.auth,
230597
- });
230598
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230599
- return buildEnvelope({
230600
- action: input.action,
230601
- targetName: input.targetName ?? null,
230602
- path: accessPath ?? null,
230603
- auth: input.auth,
230604
- apiIds,
230605
- raw: result,
230606
- }, "网关路径鉴权更新成功");
230607
- }
230608
231446
  default:
230609
231447
  throw new Error(`不支持的操作类型: ${input.action}`);
230610
231448
  }
230611
231449
  };
230612
231450
  server.registerTool?.("queryGateway", {
230613
231451
  title: "查询 CloudBase 网关",
230614
- description: "CloudBase 网关统一只读入口。通过 action 查询网关域名、访问入口和目标暴露情况。",
231452
+ description: "CloudBase HTTP 网关统一只读入口(Domain/Route)。查询域名下路径路由及其上游:" +
231453
+ "WEB_SCF/SCF=云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
231454
+ "主键为 Domain + Path;listRoutes / getRoute / listCustomDomains。",
230615
231455
  inputSchema: {
230616
231456
  action: zod_1.z
230617
231457
  .enum(QUERY_GATEWAY_ACTIONS)
230618
- .describe("只读操作类型,例如 getAccess、listDomains"),
230619
- targetType: zod_1.z
230620
- .enum(["function"])
230621
- .optional()
230622
- .describe("目标资源类型。当前支持 function,后续可扩展"),
231458
+ .describe("只读操作类型:listRoutes、getRoute、listCustomDomains"),
230623
231459
  targetName: zod_1.z
230624
231460
  .string()
230625
231461
  .optional()
230626
- .describe("目标资源名称。getAccess 时必填"),
231462
+ .describe("上游资源名过滤(UpstreamResourceName):云函数名、云托管服务名,或静态托管实例名(常见 staticstore)。"),
230627
231463
  routeId: zod_1.z.string().optional().describe("路由 ID。getRoute 时可选"),
231464
+ path: zod_1.z
231465
+ .string()
231466
+ .optional()
231467
+ .describe("路由路径。getRoute / listRoutes 过滤时可选"),
231468
+ domain: zod_1.z
231469
+ .string()
231470
+ .optional()
231471
+ .describe("域名。getRoute / listRoutes 过滤时可选"),
230628
231472
  },
230629
231473
  annotations: {
230630
231474
  readOnlyHint: true,
@@ -230634,45 +231478,76 @@ function registerGatewayTools(server) {
230634
231478
  }, async (input) => withEnvelope(() => handleQueryGateway(input)));
230635
231479
  server.registerTool?.("manageGateway", {
230636
231480
  title: "管理 CloudBase 网关",
230637
- description: "CloudBase 网关统一写入口。通过 action 创建目标访问入口,后续承接更通用的网关配置能力。为已存在的 HTTP 云函数补默认域名访问时,通常使用 createAccess 并提供 targetType=\"function\"、targetName、type=\"HTTP\" 与期望 path。注意 createAccess 只创建网关入口,不会自动修改函数资源权限。⚠️ 如需绑定带 SSL 证书的自定义域名供公网 HTTPS 访问,使用 action=\"bindCustomDomain\"(需要 domain 和 certificateId 参数);如需配置 CORS/安全域名(无证书),请使用 envDomainManagement 工具。",
231481
+ description: "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path 转到上游;未传 domain 时用 IsDefault 默认域名。" +
231482
+ "上游类型只用一个参数 upstreamResourceType(也可写在 route.upstreamResourceType,route 优先):" +
231483
+ "WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器;" +
231484
+ "配合 targetName 或 route.serviceName(云函数名/云托管服务名/静态托管实例名,常见 staticstore)。" +
231485
+ "createRoute 只建网关入口,不改上游权限。" +
231486
+ "enablePathTransmission:默认 false 剥触发路径前缀;true 透传完整路径(CBR 多路由、WEB_SCF 自管子路径常需 true;STATIC_STORE 自定义触发路径映射站点根通常 false)。" +
231487
+ "⚠️ SSL 自定义域名用 bindCustomDomain;CORS/安全域名用 envDomainManagement。",
230638
231488
  inputSchema: {
230639
231489
  action: zod_1.z
230640
231490
  .enum(MANAGE_GATEWAY_ACTIONS)
230641
- .describe('写操作类型,例如 createAccess。为已有函数补默认域名访问入口时使用 createAccess;若 action=createAccess 且 targetType=function,必须显式提供 type。'),
230642
- targetType: zod_1.z
230643
- .enum(["function"])
230644
- .optional()
230645
- .describe("目标资源类型。当前支持 function,后续可扩展"),
231491
+ .describe("写操作:createRoute/updateRoute/deleteRoute 管理路由;bindCustomDomain/deleteCustomDomain 管理自定义域名。" +
231492
+ "createRoute/updateRoute 必须提供 upstreamResourceType。"),
230646
231493
  targetName: zod_1.z
230647
231494
  .string()
230648
231495
  .optional()
230649
- .describe("目标资源名称。createAccess 到云函数时填写函数名"),
231496
+ .describe("上游资源名称(UpstreamResourceName),与 route.serviceName 二选一(route 优先)。" +
231497
+ "云函数=函数名;云托管=服务名;静态托管=实例名(常见 staticstore)。不会自动推断上游类型。"),
230650
231498
  path: zod_1.z
230651
231499
  .string()
230652
231500
  .optional()
230653
- .describe("访问路径,默认 /{targetName}。例如为 HTTP 函数暴露 /api/hello 时传 /api/hello。该参数只创建网关入口,不会自动放开函数资源权限。"),
230654
- type: zod_1.z
230655
- .enum(["Event", "HTTP"])
231501
+ .describe("触发路径(网关匹配前缀),默认 /{上游名}。例:云函数 /api/hello、云托管 /api、静态托管 / 或 /app。" +
231502
+ "只建网关入口;与 enablePathTransmission 共同决定上游实际收到的路径。"),
231503
+ upstreamResourceType: zod_1.z
231504
+ .enum(UPSTREAM_RESOURCE_TYPES)
230656
231505
  .optional()
230657
- .describe("目标函数的运行时类型,不是接入协议。createAccess 到已创建的 HTTP 云函数时传 HTTP;给 Event 函数补网关访问时传 Event 或省略。省略会默认按 Event 路由处理,可能让 HTTP 云函数访问后返回 FUNCTION_PARAM_INVALID。"),
231506
+ .describe("上游类型(与 route.upstreamResourceType 二选一,route 优先)。" +
231507
+ "WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
231508
+ "createRoute/updateRoute 必填其一;勿把 manageFunctions 的 type=HTTP|Event 传到本字段。"),
230658
231509
  auth: zod_1.z
230659
231510
  .boolean()
230660
231511
  .optional()
230661
- .describe("是否开启网关路径鉴权。若要走默认域名做匿名或浏览器访问,通常设为 false;该开关仅控制网关入口本身,不会修改函数资源权限,仍需检查并按需调整函数安全规则。"),
231512
+ .describe("网关路径鉴权(EnableAuth)。匿名/浏览器公网访问通常 false。" +
231513
+ "只控制网关入口;云函数安全规则、云托管鉴权、静态托管权限需各自工具另行配置。"),
231514
+ enablePathTransmission: zod_1.z
231515
+ .boolean()
231516
+ .optional()
231517
+ .describe("路径透传(EnablePathTransmission),平台默认 false。例 path=/api 且请求 /api/users:" +
231518
+ "false→上游收到 /users;true→上游收到 /api/users。" +
231519
+ "CBR 云托管(Express 等自管子路由)与 WEB_SCF 多路径函数常需 true;" +
231520
+ "STATIC_STORE 把触发路径映射到站点根目录(如 /app → 托管 /)时通常 false;" +
231521
+ "单入口/根路径处理保持 false。也可用 route.enablePathTransmission(route 优先)。"),
230662
231522
  route: zod_1.z
230663
231523
  .object({
230664
- routeId: zod_1.z.string().optional(),
230665
231524
  path: zod_1.z.string().optional(),
230666
- serviceType: zod_1.z.string().optional(),
230667
- serviceName: zod_1.z.string().optional(),
231525
+ serviceName: zod_1.z
231526
+ .string()
231527
+ .optional()
231528
+ .describe("上游实例名:云函数名 / 云托管服务名 / 静态托管实例名(常见 staticstore)/ LH 实例。优先于顶层 targetName。"),
231529
+ upstreamResourceType: zod_1.z
231530
+ .enum(UPSTREAM_RESOURCE_TYPES)
231531
+ .optional()
231532
+ .describe("同顶层 upstreamResourceType。route 内设置时优先于顶层。"),
230668
231533
  auth: zod_1.z.boolean().optional(),
231534
+ enablePathTransmission: zod_1.z
231535
+ .boolean()
231536
+ .optional()
231537
+ .describe("同顶层 enablePathTransmission。route 内设置时优先于顶层。"),
230669
231538
  })
230670
231539
  .optional()
230671
- .describe("HTTP 路由配置对象"),
230672
- domain: zod_1.z.string().optional().describe("自定义域名"),
230673
- certificateId: zod_1.z.string().optional().describe("证书 ID"),
230674
- accessName: zod_1.z.string().optional().describe("访问入口名称,保留字段"),
230675
- accessId: zod_1.z.string().optional().describe("访问入口 ID。deleteAccess 时可直接传入,避免参数歧义"),
231540
+ .describe("路由对象(可选写法)。例:云函数 {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"};" +
231541
+ "云托管 {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"};" +
231542
+ "静态托管 {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"}。"),
231543
+ domain: zod_1.z
231544
+ .string()
231545
+ .optional()
231546
+ .describe("域名。省略时自动使用环境 IsDefault 默认 HTTP 域名;自定义域名场景请显式传入"),
231547
+ certificateId: zod_1.z
231548
+ .string()
231549
+ .optional()
231550
+ .describe("证书 ID。bindCustomDomain 时必填"),
230676
231551
  },
230677
231552
  annotations: {
230678
231553
  readOnlyHint: false,
@@ -262997,6 +263872,17 @@ class CloudBase {
262997
263872
  get aiModel() {
262998
263873
  return this.currentEnvironment().getAiModelService();
262999
263874
  }
263875
+ /**
263876
+ * 集成中心服务(UserKey)
263877
+ * 提供集成模板/实例的增删改查与绑定云函数能力
263878
+ */
263879
+ get integration() {
263880
+ return this.currentEnvironment().getIntegrationService();
263881
+ }
263882
+ /**
263883
+ * 监控服务
263884
+ * 提供指标查询能力
263885
+ */
263000
263886
  get monitor() {
263001
263887
  return this.currentEnvironment().getMonitorService();
263002
263888
  }
@@ -264609,7 +265495,9 @@ function configToParams(options) {
264609
265495
  if ((_c = func === null || func === void 0 ? void 0 : func.layers) === null || _c === void 0 ? void 0 : _c.length) {
264610
265496
  params.Layers = func.layers.map(item => ({
264611
265497
  LayerName: item.name,
264612
- LayerVersion: item.version
265498
+ // item.version 可能是字符串(如 JSON 配置文件中的 "1" 或模板变量替换结果),
265499
+ // SCF API 要求 LayerVersion 为 int64,需显式转为 number
265500
+ LayerVersion: Number(item.version)
264613
265501
  }));
264614
265502
  }
264615
265503
  // 7. 代码保护
@@ -264648,8 +265536,8 @@ function configToParams(options) {
264648
265536
  params.Code.ImageConfig = buildImageConfig(imageConfig);
264649
265537
  }
264650
265538
  // 第三阶段:统一应用默认值
264651
- const runtime = params.Runtime || 'Nodejs18.15';
264652
- return Object.assign({ Handler: func.handler || 'index.main', Timeout: 10, Runtime: 'Nodejs18.15', MemorySize: 256, InstallDependency: isNodeFunction(runtime) ? 'TRUE' : 'FALSE' }, params);
265539
+ const runtime = params.Runtime || 'Nodejs20.19';
265540
+ return Object.assign({ Handler: func.handler || 'index.main', Runtime: 'Nodejs20.19', MemorySize: 256, InstallDependency: isNodeFunction(runtime) ? 'TRUE' : 'FALSE' }, params);
264653
265541
  }
264654
265542
  class FunctionService {
264655
265543
  constructor(environment) {
@@ -264705,7 +265593,7 @@ class FunctionService {
264705
265593
  async createFunction(funcParam) {
264706
265594
  var _a, _b, _c;
264707
265595
  const { namespace } = this.getFunctionConfig();
264708
- const { func, functionRootPath, force = false, base64Code, codeSecret, functionPath, deployMode } = funcParam;
265596
+ const { func, functionRootPath, force = false, base64Code, codeSecret, functionPath, deployMode, code } = funcParam;
264709
265597
  const funcName = func.name;
264710
265598
  const { TopicId, LogsetId } = this.getClsServiceConfig();
264711
265599
  const params = configToParams({
@@ -264719,20 +265607,30 @@ class FunctionService {
264719
265607
  ClsLogsetId: LogsetId
264720
265608
  }
264721
265609
  });
265610
+ const customCode = code;
265611
+ const customImageCode = customCode === null || customCode === void 0 ? void 0 : customCode.ImageConfig;
265612
+ const funcImageCode = (_a = params.Code) === null || _a === void 0 ? void 0 : _a.ImageConfig;
264722
265613
  // 根据部署方式处理 Code 参数
264723
- // 优先使用显式指定的 deployMode,如果未指定但存在 imageConfig 则认为是镜像部署
264724
- const isImageDeploy = deployMode === 'image' || (!deployMode && ((_a = params.Code) === null || _a === void 0 ? void 0 : _a.ImageConfig));
265614
+ // 优先级:deployMode=image > 显式传入 code > 原有自动打包逻辑(base64Code/functionPath/functionRootPath)
265615
+ const isImageDeploy = deployMode === 'image' || Boolean(customImageCode) || (!deployMode && Boolean(funcImageCode));
264725
265616
  if (isImageDeploy) {
264726
- // 镜像部署:Code 参数已在 configToParams 中通过 imageConfig 设置
265617
+ // 镜像部署优先使用显式 code.ImageConfig;未传时回退到 func.imageConfig(configToParams 生成)
265618
+ if (customImageCode) {
265619
+ params.Code = customCode;
265620
+ }
264727
265621
  if (!((_c = (_b = params.Code) === null || _b === void 0 ? void 0 : _b.ImageConfig) === null || _c === void 0 ? void 0 : _c.ImageUri)) {
264728
265622
  throw new error_1.CloudBaseError('镜像部署需要配置 imageConfig.imageUri');
264729
265623
  }
264730
- // 镜像部署的特殊配置
264731
- // 镜像函数不需要 Handler
265624
+ // 镜像函数不需要 Handler / InstallDependency
264732
265625
  delete params.Handler;
264733
- // 镜像函数不需要安装依赖
264734
265626
  delete params.InstallDependency;
264735
265627
  }
265628
+ else if (customCode) {
265629
+ if (base64Code || functionPath || functionRootPath) {
265630
+ console.warn(`[${funcName}] 检测到同时传入 code 与本地打包参数,已优先使用 code 参数`);
265631
+ }
265632
+ params.Code = customCode;
265633
+ }
264736
265634
  else {
264737
265635
  // 代码部署:通过 getCodeParams 获取代码参数
264738
265636
  params.Code = await this.getCodeParams({
@@ -264787,7 +265685,8 @@ class FunctionService {
264787
265685
  functionPath,
264788
265686
  functionRootPath,
264789
265687
  codeSecret: codeSecret,
264790
- deployMode: isImageDeploy ? 'image' : undefined
265688
+ deployMode: isImageDeploy ? 'image' : deployMode,
265689
+ code
264791
265690
  });
264792
265691
  // 等待函数状态正常
264793
265692
  await this.waitFunctionActive(funcName, codeSecret);
@@ -265235,7 +266134,9 @@ class FunctionService {
265235
266134
  if ((_c = func === null || func === void 0 ? void 0 : func.layers) === null || _c === void 0 ? void 0 : _c.length) {
265236
266135
  params.Layers = func.layers.map(item => ({
265237
266136
  LayerName: item.name,
265238
- LayerVersion: item.version
266137
+ // item.version 可能是字符串(如 JSON 配置文件中的 "1" 或模板变量替换结果),
266138
+ // SCF API 要求 LayerVersion 为 int64,需显式转为 number
266139
+ LayerVersion: Number(item.version)
265239
266140
  }));
265240
266141
  }
265241
266142
  // 特殊处理:HTTP 函数多并发配置(大小写不敏感)
@@ -265290,7 +266191,7 @@ class FunctionService {
265290
266191
  * @memberof FunctionService
265291
266192
  */
265292
266193
  async updateFunctionCode(funcParam) {
265293
- const { func, functionRootPath, base64Code, codeSecret, functionPath, deployMode } = funcParam;
266194
+ const { func, functionRootPath, base64Code, codeSecret, functionPath, deployMode, code } = funcParam;
265294
266195
  const funcName = func.name;
265295
266196
  const { namespace } = this.getFunctionConfig();
265296
266197
  // 镜像部署:使用镜像配置更新函数代码
@@ -265317,6 +266218,10 @@ class FunctionService {
265317
266218
  });
265318
266219
  }
265319
266220
  }
266221
+ const customCode = code;
266222
+ if (customCode && (base64Code || functionPath || functionRootPath)) {
266223
+ console.warn(`[${funcName}] 检测到同时传入 code 与本地打包参数,已优先使用 code 参数`);
266224
+ }
265320
266225
  // 代码部署:原有逻辑
265321
266226
  let installDependency;
265322
266227
  // Node 函数默认安装依赖
@@ -265325,7 +266230,7 @@ class FunctionService {
265325
266230
  if (func.installDependency !== undefined) {
265326
266231
  installDependency = toBooleanString(func.installDependency);
265327
266232
  }
265328
- const codeParams = await this.getCodeParams({
266233
+ const codeParams = customCode || await this.getCodeParams({
265329
266234
  func,
265330
266235
  functionPath,
265331
266236
  functionRootPath,
@@ -265919,7 +266824,7 @@ class FunctionService {
265919
266824
  const { func, functionPath, functionRootPath } = options;
265920
266825
  const { env, appId } = this.getFunctionConfig();
265921
266826
  if (!appId) {
265922
- throw new error_1.CloudBaseError('无法获取 AppId,请确认环境已开通存储服务');
266827
+ throw new error_1.CloudBaseError('无法获取 AppId,请确认环境初始化完成或已开通存储服务');
265923
266828
  }
265924
266829
  const objectPath = `${appId}/${env}/${func.name}.zip`;
265925
266830
  // 1. 生成存放函数包的临时 Cos 目录
@@ -266155,14 +267060,18 @@ class FunctionService {
266155
267060
  * @memberof FunctionService
266156
267061
  */
266157
267062
  getFunctionConfig() {
266158
- var _a, _b, _c, _d, _e;
267063
+ var _a, _b, _c, _d, _e, _f;
266159
267064
  const envConfig = this.environment.lazyEnvironmentConfig;
266160
267065
  // Functions 可能为空
266161
267066
  const namespace = ((_b = (_a = envConfig.Functions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.Namespace) || '';
266162
- // AppId 可能为空字符串,从 Bucket 名称中提取
267067
+ // AppId 优先从 DescribeEnvInfo 的 UserInfo 获取(账号级标识,不依赖存储服务)
267068
+ // 兜底从 Storages[0] 的 AppId 或 Bucket 名提取
266163
267069
  // COS Bucket 格式: {BucketName}-{APPID},APPID 为纯数字
266164
267070
  const storage = (_c = envConfig.Storages) === null || _c === void 0 ? void 0 : _c[0];
266165
- const appId = (storage === null || storage === void 0 ? void 0 : storage.AppId) || ((_e = (_d = storage === null || storage === void 0 ? void 0 : storage.Bucket) === null || _d === void 0 ? void 0 : _d.match(/-(\d+)$/)) === null || _e === void 0 ? void 0 : _e[1]) || '';
267071
+ const appId = ((_d = envConfig.UserInfo) === null || _d === void 0 ? void 0 : _d.AppId)
267072
+ || (storage === null || storage === void 0 ? void 0 : storage.AppId)
267073
+ || ((_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])
267074
+ || '';
266166
267075
  const { proxy } = this.environment.cloudBaseContext;
266167
267076
  return {
266168
267077
  proxy,