@cloudbase/cloudbase-mcp 2.25.4 → 2.25.6

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
@@ -1962,13 +1962,13 @@ function buildEnvDomainManagementResult(params) {
1962
1962
  operation: action,
1963
1963
  targetDomains: domains,
1964
1964
  asyncState: "PENDING",
1965
- message: '安全域名已提交添加请求。该变更通常需要约 10 分钟传播,请继续轮询 queryEnv(action="domains"),直到目标域名状态为 ENABLE。',
1965
+ message: '安全域名已提交添加请求。该变更通常需要数分钟传播(平台侧);请每 10 秒轮询 queryEnv(action="domains") 直到 Status 为 ENABLE,勿一次 sleep 满 10 分钟。',
1966
1966
  propagation: {
1967
1967
  requiresPolling: true,
1968
1968
  pollTool: "queryEnv",
1969
1969
  pollAction: "domains",
1970
1970
  pollIntervalSuggestionSeconds: 10,
1971
- timeoutSuggestionSeconds: 600,
1971
+ timeoutSuggestionSeconds: 300,
1972
1972
  successCondition: '目标域名出现在 queryEnv(action="domains") 返回中,且 Status 为 ENABLE。',
1973
1973
  },
1974
1974
  next_step: {
@@ -1987,13 +1987,13 @@ function buildEnvDomainManagementResult(params) {
1987
1987
  operation: action,
1988
1988
  targetDomains: domains,
1989
1989
  asyncState: "PENDING",
1990
- message: '安全域名已提交删除请求。该变更可能需要数分钟传播,请继续轮询 queryEnv(action="domains"),直到目标域名不再出现。',
1990
+ message: '安全域名已提交删除请求。该变更通常需要数分钟传播;请每 10 秒轮询 queryEnv(action="domains") 直到目标域名不再出现,勿一次 sleep 满数分钟。',
1991
1991
  propagation: {
1992
1992
  requiresPolling: true,
1993
1993
  pollTool: "queryEnv",
1994
1994
  pollAction: "domains",
1995
1995
  pollIntervalSuggestionSeconds: 10,
1996
- timeoutSuggestionSeconds: 600,
1996
+ timeoutSuggestionSeconds: 300,
1997
1997
  successCondition: '目标域名不再出现在 queryEnv(action="domains") 返回中。',
1998
1998
  },
1999
1999
  next_step: {
@@ -3701,7 +3701,7 @@ function registerEnvTools(server) {
3701
3701
  if (server.ide !== 'wxide') {
3702
3702
  server.registerTool?.("envDomainManagement", {
3703
3703
  title: "CloudBase 环境域名管理(安全域名 / CORS 白名单)",
3704
- description: "管理 CloudBase 环境的安全域名(安全域名 / CORS 白名单),支持添加和删除操作。(原工具名:createEnvDomain/deleteEnvDomain,为兼容旧AI规则可继续使用这些名称)当浏览器 Web 应用需要从本地 Vite / dev server 直接访问 CloudBase 资源时,应先用 queryEnv(action=domains) 检查当前实际浏览器 origin 对应的 host:port 是否已在白名单中,再按该实际值添加。新增或删除后通常需要继续轮询 queryEnv(action=domains) 确认状态收敛;安全域名一般约 10 分钟生效。⚠️ 重要:此工具仅用于 CORS/请求来源验证,不涉及 SSL 证书。自定义域名公网 HTTPS:先 queryGateway(listCustomDomains);已有域名则 manageGateway(createRoute) 显式传 domain(无需证书);仅首次绑定新域名才用 bindCustomDomain(需 certificateId)。",
3704
+ description: "管理 CloudBase 环境的安全域名(安全域名 / CORS 白名单),支持添加和删除操作。(原工具名:createEnvDomain/deleteEnvDomain,为兼容旧AI规则可继续使用这些名称)当浏览器 Web 应用需要从本地 Vite / dev server 直接访问 CloudBase 资源时,应先用 queryEnv(action=domains) 检查当前实际浏览器 origin 对应的 host:port 是否已在白名单中,再按该实际值添加。新增或删除后请每约 10 秒轮询 queryEnv(action=domains) 确认状态收敛,勿一次 sleep 满 10 分钟;多数环境数分钟内可收敛。⚠️ 重要:此工具仅用于 CORS/请求来源验证,不涉及 SSL 证书。自定义域名公网 HTTPS:先 queryGateway(listCustomDomains);已有域名则 manageGateway(createRoute) 显式传 domain(无需证书);仅首次绑定新域名才用 bindCustomDomain(需 certificateId)。",
3705
3705
  inputSchema: {
3706
3706
  action: zod_1.z
3707
3707
  .enum(["create", "delete"])
@@ -63516,6 +63516,177 @@ class DatabaseService {
63516
63516
  }
63517
63517
  return this.dbOpService.request('ExecutePGSql', params);
63518
63518
  }
63519
+ /**
63520
+ * 预览 PG 用户 migrations 的远端执行计划(不实际执行 SQL)
63521
+ * @param options 预览参数
63522
+ * @returns 预览结果(Pending、Applied、Conflicts、Executable)
63523
+ */
63524
+ async previewPGUserMigrations(options) {
63525
+ var _a;
63526
+ const { EnvId, Migrations, IncludeAll } = options;
63527
+ if (!Array.isArray(Migrations) || Migrations.length === 0) {
63528
+ throw new error_1.CloudBaseError('Migrations 至少需要 1 条');
63529
+ }
63530
+ for (const migration of Migrations) {
63531
+ if (!/^\d{14}$/.test(migration.Version)) {
63532
+ throw new error_1.CloudBaseError('Migration.Version 必须是 14 位数字');
63533
+ }
63534
+ if (!/^[a-z_]+$/.test(migration.Name)) {
63535
+ throw new error_1.CloudBaseError('Migration.Name 只允许小写字母和下划线');
63536
+ }
63537
+ if (!((_a = migration.Query) === null || _a === void 0 ? void 0 : _a.trim())) {
63538
+ throw new error_1.CloudBaseError('Migration.Query 不能为空');
63539
+ }
63540
+ }
63541
+ return this.dbOpService.request('PreviewPGUserMigrations', Object.assign({ EnvId: EnvId || this.envId, Migrations }, (IncludeAll !== undefined ? { IncludeAll } : {})));
63542
+ }
63543
+ /**
63544
+ * 批量应用 PG 用户 migrations
63545
+ * @param options 批量 migration 参数
63546
+ * @returns 任务信息(TaskId、RequestId)
63547
+ */
63548
+ async pushPGUserMigrations(options) {
63549
+ var _a;
63550
+ const { EnvId, Migrations, LockTimeoutMs, StatementTimeoutMs, IncludeAll } = options;
63551
+ if (!Array.isArray(Migrations) || Migrations.length === 0) {
63552
+ throw new error_1.CloudBaseError('Migrations 至少需要 1 条');
63553
+ }
63554
+ for (const migration of Migrations) {
63555
+ if (!/^\d{14}$/.test(migration.Version)) {
63556
+ throw new error_1.CloudBaseError('Migration.Version 必须是 14 位数字');
63557
+ }
63558
+ if (!/^[a-z_]+$/.test(migration.Name)) {
63559
+ throw new error_1.CloudBaseError('Migration.Name 只允许小写字母和下划线');
63560
+ }
63561
+ if (!((_a = migration.Query) === null || _a === void 0 ? void 0 : _a.trim())) {
63562
+ throw new error_1.CloudBaseError('Migration.Query 不能为空');
63563
+ }
63564
+ }
63565
+ if (LockTimeoutMs !== undefined && LockTimeoutMs < 0) {
63566
+ throw new error_1.CloudBaseError('LockTimeoutMs 不能小于 0');
63567
+ }
63568
+ if (StatementTimeoutMs !== undefined && StatementTimeoutMs < 0) {
63569
+ throw new error_1.CloudBaseError('StatementTimeoutMs 不能小于 0');
63570
+ }
63571
+ return this.dbOpService.request('PushPGUserMigrations', Object.assign(Object.assign(Object.assign({ EnvId: EnvId || this.envId, Migrations }, (LockTimeoutMs !== undefined ? { LockTimeoutMs } : {})), (StatementTimeoutMs !== undefined ? { StatementTimeoutMs } : {})), (IncludeAll !== undefined ? { IncludeAll } : {})));
63572
+ }
63573
+ /**
63574
+ * 修复 PG 用户 migration history,仅维护 user_schema_migrations 记录,不执行 SQL
63575
+ * @param options 修复参数
63576
+ * @returns RequestId
63577
+ */
63578
+ async repairPGUserMigrationHistory(options) {
63579
+ const { EnvId, MigrationVersion, Name, Status, Reason, Query } = options;
63580
+ if (!/^\d{14}$/.test(MigrationVersion)) {
63581
+ throw new error_1.CloudBaseError('MigrationVersion 必须是 14 位数字');
63582
+ }
63583
+ if (!/^[a-z_]+$/.test(Name)) {
63584
+ throw new error_1.CloudBaseError('Name 只允许小写字母和下划线');
63585
+ }
63586
+ if (Status !== 'applied' && Status !== 'reverted') {
63587
+ throw new error_1.CloudBaseError('Status 只允许 applied 或 reverted');
63588
+ }
63589
+ if (!(Reason === null || Reason === void 0 ? void 0 : Reason.trim())) {
63590
+ throw new error_1.CloudBaseError('Reason 不能为空');
63591
+ }
63592
+ if (Status === 'applied' && !(Query === null || Query === void 0 ? void 0 : Query.trim())) {
63593
+ throw new error_1.CloudBaseError('Status=applied 时 Query 不能为空');
63594
+ }
63595
+ const params = {
63596
+ EnvId: EnvId || this.envId,
63597
+ MigrationVersion,
63598
+ Name,
63599
+ Status,
63600
+ Reason
63601
+ };
63602
+ if (Status === 'applied') {
63603
+ params.Query = Query;
63604
+ }
63605
+ return this.dbOpService.request('RepairPGUserMigrationHistory', params);
63606
+ }
63607
+ /**
63608
+ * 查询目标环境已应用的用户 migration 列表
63609
+ * @param options 查询参数(EnvId 可选、Limit、Offset)
63610
+ * @returns 已应用 migration 列表、总数量、最新版本号
63611
+ */
63612
+ async listPGUserMigrations(options = {}) {
63613
+ const { EnvId, Limit, Offset } = options;
63614
+ if (Limit !== undefined && (Limit < 1 || Limit > 500)) {
63615
+ throw new error_1.CloudBaseError('Limit 取值范围为 [1, 500]');
63616
+ }
63617
+ if (Offset !== undefined && Offset < 0) {
63618
+ throw new error_1.CloudBaseError('Offset 不能小于 0');
63619
+ }
63620
+ return this.dbOpService.request('ListPGUserMigrations', Object.assign(Object.assign({ EnvId: EnvId || this.envId }, (Limit !== undefined ? { Limit } : {})), (Offset !== undefined ? { Offset } : {})));
63621
+ }
63622
+ /**
63623
+ * 查询目标环境全部已应用的用户 migration 列表
63624
+ * @param options 查询参数(EnvId 可选、PageSize)
63625
+ * @returns 已应用 migration 列表、总数量、最新版本号
63626
+ */
63627
+ async listAllPGUserMigrations(options = {}) {
63628
+ const { EnvId, PageSize = 500 } = options;
63629
+ if (PageSize < 1 || PageSize > 500) {
63630
+ throw new error_1.CloudBaseError('PageSize 取值范围为 [1, 500]');
63631
+ }
63632
+ const migrations = [];
63633
+ let offset = 0;
63634
+ let latestVersion = '';
63635
+ let total = 0;
63636
+ let requestId = '';
63637
+ while (true) {
63638
+ const page = await this.listPGUserMigrations({
63639
+ EnvId,
63640
+ Limit: PageSize,
63641
+ Offset: offset
63642
+ });
63643
+ const pageMigrations = page.Migrations || [];
63644
+ migrations.push(...pageMigrations);
63645
+ latestVersion = page.LatestVersion || latestVersion;
63646
+ total += pageMigrations.length;
63647
+ requestId = page.RequestId || requestId;
63648
+ if (pageMigrations.length < PageSize) {
63649
+ break;
63650
+ }
63651
+ offset += PageSize;
63652
+ }
63653
+ return {
63654
+ Total: total,
63655
+ LatestVersion: latestVersion,
63656
+ Migrations: migrations,
63657
+ RequestId: requestId
63658
+ };
63659
+ }
63660
+ /**
63661
+ * 查询目标环境指定 migration 详情
63662
+ * @param options 查询参数(EnvId 可选、MigrationVersion 必填)
63663
+ * @returns migration 详情(Version、Name、Query)
63664
+ */
63665
+ async describePGUserMigration(options) {
63666
+ const { EnvId, MigrationVersion } = options;
63667
+ if (!/^\d{14}$/.test(MigrationVersion)) {
63668
+ throw new error_1.CloudBaseError('MigrationVersion 必须是 14 位数字');
63669
+ }
63670
+ return this.dbOpService.request('DescribePGUserMigration', {
63671
+ EnvId: EnvId || this.envId,
63672
+ MigrationVersion
63673
+ });
63674
+ }
63675
+ /**
63676
+ * 查询任务结果
63677
+ * @param options 查询参数(EnvId 可选、TaskId 必填)
63678
+ * @returns 任务状态详情(TaskType、Status、Phase、Reason 等)
63679
+ */
63680
+ async describeTaskResult(options) {
63681
+ const { EnvId, TaskId } = options;
63682
+ if (!(TaskId === null || TaskId === void 0 ? void 0 : TaskId.trim())) {
63683
+ throw new error_1.CloudBaseError('TaskId 不能为空');
63684
+ }
63685
+ return this.dbOpService.request('DescribeTaskResult', {
63686
+ EnvId: EnvId || this.envId,
63687
+ TaskId
63688
+ });
63689
+ }
63519
63690
  }
63520
63691
  exports.DatabaseService = DatabaseService;
63521
63692
  DatabaseService.tcbServiceVersion = {
@@ -63559,6 +63730,27 @@ __decorate([
63559
63730
  __decorate([
63560
63731
  preLazy()
63561
63732
  ], DatabaseService.prototype, "executePGSql", null);
63733
+ __decorate([
63734
+ preLazy()
63735
+ ], DatabaseService.prototype, "previewPGUserMigrations", null);
63736
+ __decorate([
63737
+ preLazy()
63738
+ ], DatabaseService.prototype, "pushPGUserMigrations", null);
63739
+ __decorate([
63740
+ preLazy()
63741
+ ], DatabaseService.prototype, "repairPGUserMigrationHistory", null);
63742
+ __decorate([
63743
+ preLazy()
63744
+ ], DatabaseService.prototype, "listPGUserMigrations", null);
63745
+ __decorate([
63746
+ preLazy()
63747
+ ], DatabaseService.prototype, "listAllPGUserMigrations", null);
63748
+ __decorate([
63749
+ preLazy()
63750
+ ], DatabaseService.prototype, "describePGUserMigration", null);
63751
+ __decorate([
63752
+ preLazy()
63753
+ ], DatabaseService.prototype, "describeTaskResult", null);
63562
63754
 
63563
63755
 
63564
63756
  /***/ }),
@@ -90876,6 +91068,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
90876
91068
  exports.CUSTOM_IMAGE_RUNTIME = exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = void 0;
90877
91069
  exports.formatRuntimeList = formatRuntimeList;
90878
91070
  exports.validateTimerCron = validateTimerCron;
91071
+ exports.pickManageFunctionName = pickManageFunctionName;
90879
91072
  exports.shouldInstallDependencyForFunction = shouldInstallDependencyForFunction;
90880
91073
  exports.resolveEventFunctionRuntime = resolveEventFunctionRuntime;
90881
91074
  exports.buildFunctionOperationErrorMessage = buildFunctionOperationErrorMessage;
@@ -91104,6 +91297,19 @@ const MANAGE_LAYER_SCHEMA = zod_1.z.object({
91104
91297
  layerName: zod_1.z.string().describe("层名称"),
91105
91298
  layerVersion: zod_1.z.number().describe("层版本号"),
91106
91299
  });
91300
+ /**
91301
+ * Prefer top-level functionName; fall back to func.name for the common
91302
+ * mistake of nesting the target name under func (createFunction shape).
91303
+ */
91304
+ function pickManageFunctionName(input) {
91305
+ if (typeof input.functionName === "string" && input.functionName.trim()) {
91306
+ return input.functionName.trim();
91307
+ }
91308
+ if (typeof input.func?.name === "string" && input.func.name.trim()) {
91309
+ return input.func.name.trim();
91310
+ }
91311
+ return undefined;
91312
+ }
91107
91313
  function normalizeFunctionLayers(layers) {
91108
91314
  if (!Array.isArray(layers)) {
91109
91315
  return [];
@@ -91570,7 +91776,12 @@ function registerFunctionTools(server) {
91570
91776
  throw new Error(`不支持的操作类型: ${input.action}`);
91571
91777
  }
91572
91778
  };
91573
- const handleManageFunctions = async (input) => {
91779
+ const handleManageFunctions = async (rawInput) => {
91780
+ // Accept func.name as a fallback for top-level functionName (common agent mistake).
91781
+ const pickedName = pickManageFunctionName(rawInput);
91782
+ const input = pickedName && rawInput.functionName !== pickedName
91783
+ ? { ...rawInput, functionName: pickedName }
91784
+ : rawInput;
91574
91785
  ensureActionAllowedInCloudMode(input);
91575
91786
  switch (input.action) {
91576
91787
  case "createFunction": {
@@ -92276,7 +92487,12 @@ function registerFunctionTools(server) {
92276
92487
  "此参数传 cloudfunctions 或 functions 目录的绝对路径。" +
92277
92488
  "SDK 会自动拼接函数名子目录,无需预先压缩 zip 或 base64 编码。"),
92278
92489
  force: zod_1.z.boolean().optional().describe("createFunction 时是否覆盖"),
92279
- functionName: zod_1.z.string().optional().describe("函数名称。大多数 action 使用该字段作为统一目标"),
92490
+ functionName: zod_1.z
92491
+ .string()
92492
+ .optional()
92493
+ .describe("目标函数名称(顶层)。updateFunctionCode / updateFunctionConfig / invokeFunction 等 action 使用此字段。" +
92494
+ "不要只写在 func.name:createFunction 用 func.name,其它 action 用顶层 functionName。" +
92495
+ "若误传 func.name,也会被识别为 functionName。"),
92280
92496
  zipFile: zod_1.z.string().optional().describe("仅兼容特殊场景:预先准备好的代码包 base64 编码。普通 createFunction/updateFunctionCode 默认不要先压缩 zip,优先使用 functionRootPath。"),
92281
92497
  handler: zod_1.z.string().optional().describe("函数入口"),
92282
92498
  timeout: zod_1.z.number().optional().describe("配置更新时的超时时间"),
@@ -106113,7 +106329,7 @@ class CloudAppService {
106113
106329
  * @param params 上传参数
106114
106330
  */
106115
106331
  async uploadCode(params) {
106116
- const { serviceName, localPath, ignore = [] } = params;
106332
+ const { serviceName, localPath, ignore = [], onPhase } = params;
106117
106333
  // 1. 验证本地路径
106118
106334
  if (!fs_1.default.existsSync(localPath)) {
106119
106335
  throw new error_1.CloudBaseError(`本地路径不存在: ${localPath}`);
@@ -106125,20 +106341,27 @@ class CloudAppService {
106125
106341
  // 2. 打包本地文件为 ZIP
106126
106342
  const tmpZipPath = path_1.default.join(os_1.default.tmpdir(), `cloudapp-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`);
106127
106343
  const defaultIgnore = ['node_modules/**', '.git/**', '.DS_Store', '**/.DS_Store'];
106344
+ const packStart = Date.now();
106128
106345
  await (0, utils_1.compressToZip)({
106129
106346
  dirPath: localPath,
106130
106347
  outputPath: tmpZipPath,
106131
106348
  ignore: [...defaultIgnore, ...ignore]
106132
106349
  });
106350
+ const zipSize = fs_1.default.statSync(tmpZipPath).size;
106351
+ onPhase === null || onPhase === void 0 ? void 0 : onPhase({ phase: 'pack', durationMs: Date.now() - packStart, bytes: zipSize });
106133
106352
  try {
106134
106353
  // 3. 获取上传凭证
106354
+ const credStart = Date.now();
106135
106355
  const cosInfo = await this.describeCosInfo({
106136
106356
  deployType: params.deployType,
106137
106357
  serviceName,
106138
106358
  suffix: '.zip'
106139
106359
  });
106360
+ onPhase === null || onPhase === void 0 ? void 0 : onPhase({ phase: 'credential', durationMs: Date.now() - credStart });
106140
106361
  // 4. 读取 ZIP 文件
106362
+ const readStart = Date.now();
106141
106363
  const zipBuffer = fs_1.default.readFileSync(tmpZipPath);
106364
+ onPhase === null || onPhase === void 0 ? void 0 : onPhase({ phase: 'read', durationMs: Date.now() - readStart, bytes: zipBuffer.length });
106142
106365
  // 5. 构建请求头
106143
106366
  const headers = {
106144
106367
  'Content-Type': 'application/zip'
@@ -106149,6 +106372,7 @@ class CloudAppService {
106149
106372
  }
106150
106373
  }
106151
106374
  // 6. 使用预签名 URL 上传
106375
+ const putStart = Date.now();
106152
106376
  const response = await (0, http_request_1.fetchStream)(cosInfo.UploadUrl, {
106153
106377
  method: 'PUT',
106154
106378
  body: zipBuffer,
@@ -106165,6 +106389,7 @@ class CloudAppService {
106165
106389
  }
106166
106390
  throw new error_1.CloudBaseError(`上传失败: ${response.status} ${response.statusText}${errorDetail}`);
106167
106391
  }
106392
+ onPhase === null || onPhase === void 0 ? void 0 : onPhase({ phase: 'upload', durationMs: Date.now() - putStart, bytes: zipBuffer.length });
106168
106393
  // 7. 返回结果(返回 cosTimestamp,用于创建应用)
106169
106394
  return {
106170
106395
  cosTimestamp: cosInfo.UnixTimestamp,
@@ -138355,7 +138580,7 @@ class TelemetryReporter {
138355
138580
  const nodeVersion = process.version; // Node.js版本
138356
138581
  const arch = os_1.default.arch(); // 系统架构
138357
138582
  // 从构建时注入的版本号获取MCP版本信息
138358
- const mcpVersion = process.env.npm_package_version || "2.25.4" || 0;
138583
+ const mcpVersion = process.env.npm_package_version || "2.25.6" || 0;
138359
138584
  return {
138360
138585
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
138361
138586
  deviceId: this.deviceId,
@@ -162049,13 +162274,48 @@ exports["default"] = applicator;
162049
162274
  /***/ }),
162050
162275
 
162051
162276
  /***/ 56473:
162052
- /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
162277
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
162053
162278
 
162054
162279
  "use strict";
162055
162280
 
162281
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
162282
+ if (k2 === undefined) k2 = k;
162283
+ var desc = Object.getOwnPropertyDescriptor(m, k);
162284
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
162285
+ desc = { enumerable: true, get: function() { return m[k]; } };
162286
+ }
162287
+ Object.defineProperty(o, k2, desc);
162288
+ }) : (function(o, m, k, k2) {
162289
+ if (k2 === undefined) k2 = k;
162290
+ o[k2] = m[k];
162291
+ }));
162292
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
162293
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
162294
+ }) : function(o, v) {
162295
+ o["default"] = v;
162296
+ });
162297
+ var __importStar = (this && this.__importStar) || (function () {
162298
+ var ownKeys = function(o) {
162299
+ ownKeys = Object.getOwnPropertyNames || function (o) {
162300
+ var ar = [];
162301
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
162302
+ return ar;
162303
+ };
162304
+ return ownKeys(o);
162305
+ };
162306
+ return function (mod) {
162307
+ if (mod && mod.__esModule) return mod;
162308
+ var result = {};
162309
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
162310
+ __setModuleDefault(result, mod);
162311
+ return result;
162312
+ };
162313
+ })();
162056
162314
  Object.defineProperty(exports, "__esModule", ({ value: true }));
162057
162315
  exports.__resetPgReadyCache = __resetPgReadyCache;
162058
162316
  exports.registerPGDatabaseTools = registerPGDatabaseTools;
162317
+ const fs = __importStar(__webpack_require__(29021));
162318
+ const path = __importStar(__webpack_require__(39902));
162059
162319
  const zod_1 = __webpack_require__(21614);
162060
162320
  const cloudbase_manager_js_1 = __webpack_require__(3431);
162061
162321
  const tool_result_js_1 = __webpack_require__(9835);
@@ -162076,6 +162336,8 @@ const MANAGE_ACTIONS = [
162076
162336
  "applyMigration",
162077
162337
  "listMigrations",
162078
162338
  "migrationDetail",
162339
+ "describeMigrationTask",
162340
+ "fetchMigration",
162079
162341
  "rollbackMigration",
162080
162342
  "repairMigration",
162081
162343
  ];
@@ -162236,8 +162498,170 @@ function isSchemaDdlRisk(risk, sql) {
162236
162498
  const verb = getSqlVerb(stripLeadingSqlComments(sql));
162237
162499
  return ["DROP", "TRUNCATE", "ALTER"].includes(verb);
162238
162500
  }
162501
+ /**
162502
+ * Canonical local migration directory — must match CloudBase CLI
162503
+ * `MIGRATIONS_DIR` (`cloudbase/migrations`) so MCP+CLI mixed workflows share files.
162504
+ * Legacy bare `migrations/` is accepted only when an existing matching file is found.
162505
+ */
162506
+ const LOCAL_MIGRATIONS_DIR = "cloudbase/migrations";
162507
+ const LOCAL_MIGRATIONS_DIR_LEGACY = "migrations";
162508
+ /** Same constraint as applyMigration.migrationName (broader than CLI /^[a-z_]+$/ — digits allowed). */
162509
+ const MIGRATION_NAME_PATTERN = /^[a-z][a-z0-9_]*$/;
162510
+ function getMigrationProjectRoot() {
162511
+ const fromEnv = process.env.WORKSPACE_FOLDER_PATHS ||
162512
+ process.env.PROJECT_ROOT ||
162513
+ process.env.GITHUB_WORKSPACE ||
162514
+ process.env.CI_PROJECT_DIR ||
162515
+ process.env.BUILD_SOURCESDIRECTORY;
162516
+ if (fromEnv && fromEnv.trim()) {
162517
+ // IDEs may inject multiple folders separated by path.delimiter.
162518
+ const first = fromEnv.split(path.delimiter).map((p) => p.trim()).find(Boolean);
162519
+ if (first) {
162520
+ return first;
162521
+ }
162522
+ }
162523
+ return process.cwd();
162524
+ }
162525
+ function buildLocalMigrationFileName(version, name) {
162526
+ return `${version}_${name}.sql`;
162527
+ }
162239
162528
  function buildLocalMigrationFileHint(version, name) {
162240
- return `migrations/${version}_${name}.sql`;
162529
+ return `${LOCAL_MIGRATIONS_DIR}/${buildLocalMigrationFileName(version, name)}`;
162530
+ }
162531
+ /** Normalize SQL for local file compare/write (LF + single trailing newline). */
162532
+ function normalizeMigrationSqlForLocalFile(sql) {
162533
+ const normalized = sql.replace(/\r\n/g, "\n").replace(/\s+$/u, "");
162534
+ return `${normalized}\n`;
162535
+ }
162536
+ /**
162537
+ * Ensure the workspace has a Git-truth SQL file for this migration before Push.
162538
+ * Prefer write-to-workspace when missing; fail closed on mismatch or write failure
162539
+ * (Supabase MCP#241 class: remote apply without local truth source).
162540
+ */
162541
+ function ensureLocalMigrationSqlFile(version, name, sql) {
162542
+ const fileName = buildLocalMigrationFileName(version, name);
162543
+ const expected = normalizeMigrationSqlForLocalFile(sql);
162544
+ const projectRoot = getMigrationProjectRoot();
162545
+ const candidates = [
162546
+ {
162547
+ relativePath: `${LOCAL_MIGRATIONS_DIR}/${fileName}`,
162548
+ absolutePath: path.join(projectRoot, LOCAL_MIGRATIONS_DIR, fileName),
162549
+ },
162550
+ {
162551
+ relativePath: `${LOCAL_MIGRATIONS_DIR_LEGACY}/${fileName}`,
162552
+ absolutePath: path.join(projectRoot, LOCAL_MIGRATIONS_DIR_LEGACY, fileName),
162553
+ },
162554
+ ];
162555
+ for (const candidate of candidates) {
162556
+ if (!fs.existsSync(candidate.absolutePath)) {
162557
+ continue;
162558
+ }
162559
+ let existing;
162560
+ try {
162561
+ existing = fs.readFileSync(candidate.absolutePath, "utf8");
162562
+ }
162563
+ catch (error) {
162564
+ return {
162565
+ ok: false,
162566
+ errorCode: "LOCAL_MIGRATION_FILE_WRITE_FAILED",
162567
+ message: `Failed to read existing local migration file ${candidate.relativePath}: ` +
162568
+ `${error instanceof Error ? error.message : String(error)}`,
162569
+ relativePath: candidate.relativePath,
162570
+ absolutePath: candidate.absolutePath,
162571
+ };
162572
+ }
162573
+ if (normalizeMigrationSqlForLocalFile(existing) === expected) {
162574
+ return {
162575
+ ok: true,
162576
+ relativePath: candidate.relativePath,
162577
+ absolutePath: candidate.absolutePath,
162578
+ action: "matched",
162579
+ };
162580
+ }
162581
+ return {
162582
+ ok: false,
162583
+ errorCode: "LOCAL_MIGRATION_FILE_MISMATCH",
162584
+ message: `Local migration file ${candidate.relativePath} already exists but its content does not match the sql argument. ` +
162585
+ "Refuse to overwrite (fail closed). Update the local file to match, or use a new migrationVersion / migrationName. " +
162586
+ "Push was NOT submitted.",
162587
+ relativePath: candidate.relativePath,
162588
+ absolutePath: candidate.absolutePath,
162589
+ };
162590
+ }
162591
+ const target = candidates[0];
162592
+ try {
162593
+ fs.mkdirSync(path.dirname(target.absolutePath), { recursive: true });
162594
+ fs.writeFileSync(target.absolutePath, expected, "utf8");
162595
+ }
162596
+ catch (error) {
162597
+ return {
162598
+ ok: false,
162599
+ errorCode: "LOCAL_MIGRATION_FILE_WRITE_FAILED",
162600
+ message: `Failed to write local migration file ${target.relativePath} under workspace ${projectRoot}: ` +
162601
+ `${error instanceof Error ? error.message : String(error)}. ` +
162602
+ "Push was NOT submitted. Fix workspace permissions or write the file manually, then retry applyMigration.",
162603
+ relativePath: target.relativePath,
162604
+ absolutePath: target.absolutePath,
162605
+ };
162606
+ }
162607
+ return {
162608
+ ok: true,
162609
+ relativePath: target.relativePath,
162610
+ absolutePath: target.absolutePath,
162611
+ action: "created",
162612
+ };
162613
+ }
162614
+ /**
162615
+ * Sync remote migration Query bodies into cloudbase/migrations/ (CLI fetch parity).
162616
+ * Without force, existing files are skipped; with force, overwritten.
162617
+ */
162618
+ function syncFetchedMigrationsToLocalFiles(items, force) {
162619
+ const projectRoot = getMigrationProjectRoot();
162620
+ const migrationsDir = path.join(projectRoot, LOCAL_MIGRATIONS_DIR);
162621
+ const files = [];
162622
+ for (const item of items) {
162623
+ const version = item.Version.trim();
162624
+ const name = item.Name.trim();
162625
+ if (!MIGRATION_NAME_PATTERN.test(name)) {
162626
+ return {
162627
+ ok: false,
162628
+ errorCode: "LOCAL_MIGRATION_FETCH_INVALID_NAME",
162629
+ message: `Remote migration name "${name}" (version=${version}) is invalid. ` +
162630
+ "Names must match /^[a-z][a-z0-9_]*$/ (same as applyMigration.migrationName). " +
162631
+ "Repair the remote history record, then retry fetchMigration.",
162632
+ };
162633
+ }
162634
+ if (!item.Query.trim()) {
162635
+ return {
162636
+ ok: false,
162637
+ errorCode: "LOCAL_MIGRATION_FETCH_EMPTY_QUERY",
162638
+ message: `DescribePGUserMigration returned empty Query for version=${version} (${name}). ` +
162639
+ "Refuse to write an empty local migration file (would poison Git checksums).",
162640
+ };
162641
+ }
162642
+ const relativePath = buildLocalMigrationFileHint(version, name);
162643
+ const absolutePath = path.join(migrationsDir, buildLocalMigrationFileName(version, name));
162644
+ const exists = fs.existsSync(absolutePath);
162645
+ if (exists && !force) {
162646
+ files.push({ version, name, relativePath, absolutePath, action: "skipped" });
162647
+ continue;
162648
+ }
162649
+ try {
162650
+ fs.mkdirSync(migrationsDir, { recursive: true });
162651
+ fs.writeFileSync(absolutePath, normalizeMigrationSqlForLocalFile(item.Query), "utf8");
162652
+ }
162653
+ catch (error) {
162654
+ return {
162655
+ ok: false,
162656
+ errorCode: "LOCAL_MIGRATION_FILE_WRITE_FAILED",
162657
+ message: `Failed to write local migration file ${relativePath} under workspace ${projectRoot}: ` +
162658
+ `${error instanceof Error ? error.message : String(error)}`,
162659
+ relativePath,
162660
+ };
162661
+ }
162662
+ files.push({ version, name, relativePath, absolutePath, action: "written" });
162663
+ }
162664
+ return { ok: true, files };
162241
162665
  }
162242
162666
  /** How many recent migration records to scan when verifying a freshly pushed version. */
162243
162667
  const MIGRATION_VERIFY_LIMIT = 100;
@@ -162268,6 +162692,31 @@ function collectMigrationVersions(value, found = new Set()) {
162268
162692
  /** Brief retries before concluding a pushed migration never landed (Push can be async). */
162269
162693
  const MIGRATION_VERIFY_ATTEMPTS = 4;
162270
162694
  const MIGRATION_VERIFY_DELAY_MS = 1500;
162695
+ /** Page size when hydrating remote migration history into a Push/Preview payload. */
162696
+ const MIGRATION_LIST_PAGE_SIZE = 100;
162697
+ /**
162698
+ * PushPGUserMigrations is async: poll DescribeTaskResult until Succeed/Failed.
162699
+ * Default wait aligns with CLI `tcb db pg migration up` (10 minutes).
162700
+ * Override per call via applyMigration.taskPollTimeoutMs.
162701
+ */
162702
+ const MIGRATION_TASK_POLL_INTERVAL_MS = 1500;
162703
+ /** CLI parity: `TASK_MAX_WAIT_MS = 10 * 60 * 1000` in cloudbase-cli migration up. */
162704
+ const MIGRATION_TASK_DEFAULT_WAIT_MS = 10 * 60 * 1000;
162705
+ const MIGRATION_TASK_MIN_WAIT_MS = 5_000;
162706
+ const MIGRATION_TASK_MAX_WAIT_MS = MIGRATION_TASK_DEFAULT_WAIT_MS;
162707
+ function resolveMigrationTaskPollTimeoutMs(requested) {
162708
+ if (requested === undefined || !Number.isFinite(requested)) {
162709
+ return MIGRATION_TASK_DEFAULT_WAIT_MS;
162710
+ }
162711
+ const rounded = Math.floor(requested);
162712
+ if (rounded < MIGRATION_TASK_MIN_WAIT_MS) {
162713
+ return MIGRATION_TASK_MIN_WAIT_MS;
162714
+ }
162715
+ if (rounded > MIGRATION_TASK_MAX_WAIT_MS) {
162716
+ return MIGRATION_TASK_MAX_WAIT_MS;
162717
+ }
162718
+ return rounded;
162719
+ }
162271
162720
  /**
162272
162721
  * Confirm a pushed migration version is present in the remote migration history.
162273
162722
  *
@@ -162301,8 +162750,224 @@ async function verifyMigrationApplied(context, version, cloudBaseOptions) {
162301
162750
  }
162302
162751
  return { applied: null, error: lastError };
162303
162752
  }
162753
+ function extractMigrationSummaries(history) {
162754
+ const raw = history.Migrations;
162755
+ if (!Array.isArray(raw)) {
162756
+ return [];
162757
+ }
162758
+ const summaries = [];
162759
+ for (const item of raw) {
162760
+ if (!item || typeof item !== "object") {
162761
+ continue;
162762
+ }
162763
+ const row = item;
162764
+ const version = typeof row.Version === "string" ? row.Version.trim() : "";
162765
+ const name = typeof row.Name === "string" ? row.Name.trim() : "";
162766
+ if (version) {
162767
+ summaries.push({ Version: version, Name: name || version });
162768
+ }
162769
+ }
162770
+ return summaries;
162771
+ }
162772
+ /**
162773
+ * List every remote migration summary (paginated). Required before Push/Preview:
162774
+ * submitting only the pending migration while remote history exists yields
162775
+ * Executable=false with Conflicts reason remote_history_not_found_locally, and the
162776
+ * async task fails with "migration plan is not executable".
162777
+ */
162778
+ async function listAllRemoteMigrationSummaries(context, cloudBaseOptions) {
162779
+ const summaries = [];
162780
+ let offset = 0;
162781
+ let latestVersion = "";
162782
+ let total = 0;
162783
+ while (true) {
162784
+ const page = await callPgMigrationApi(context, "ListPGUserMigrations", { Limit: MIGRATION_LIST_PAGE_SIZE, Offset: offset }, cloudBaseOptions);
162785
+ const pageItems = extractMigrationSummaries(page);
162786
+ summaries.push(...pageItems);
162787
+ if (typeof page.LatestVersion === "string" && page.LatestVersion.trim()) {
162788
+ latestVersion = page.LatestVersion.trim();
162789
+ }
162790
+ if (typeof page.Total === "number" && Number.isFinite(page.Total)) {
162791
+ total = page.Total;
162792
+ }
162793
+ else {
162794
+ total = summaries.length;
162795
+ }
162796
+ if (pageItems.length < MIGRATION_LIST_PAGE_SIZE) {
162797
+ break;
162798
+ }
162799
+ offset += MIGRATION_LIST_PAGE_SIZE;
162800
+ }
162801
+ return { summaries, latestVersion, total };
162802
+ }
162803
+ /** `<version>_<name>.sql` — version is the identity key for completeness checks. */
162804
+ const LOCAL_MIGRATION_FILENAME_RE = /^(\d{14})_(.+)\.sql$/;
162805
+ function listLocalMigrationFilesInDir(dirAbsolute, dirRelative) {
162806
+ const map = new Map();
162807
+ if (!fs.existsSync(dirAbsolute)) {
162808
+ return map;
162809
+ }
162810
+ let stat;
162811
+ try {
162812
+ stat = fs.statSync(dirAbsolute);
162813
+ }
162814
+ catch {
162815
+ return map;
162816
+ }
162817
+ if (!stat.isDirectory()) {
162818
+ return map;
162819
+ }
162820
+ let fileNames;
162821
+ try {
162822
+ fileNames = fs.readdirSync(dirAbsolute);
162823
+ }
162824
+ catch {
162825
+ return map;
162826
+ }
162827
+ for (const fileName of fileNames) {
162828
+ const match = LOCAL_MIGRATION_FILENAME_RE.exec(fileName);
162829
+ if (!match) {
162830
+ continue;
162831
+ }
162832
+ const version = match[1];
162833
+ const name = match[2];
162834
+ const absolutePath = path.join(dirAbsolute, fileName);
162835
+ let query;
162836
+ try {
162837
+ query = fs.readFileSync(absolutePath, "utf8");
162838
+ }
162839
+ catch {
162840
+ continue;
162841
+ }
162842
+ if (!query.trim()) {
162843
+ continue;
162844
+ }
162845
+ map.set(version, {
162846
+ version,
162847
+ name,
162848
+ query,
162849
+ relativePath: `${dirRelative}/${fileName}`,
162850
+ });
162851
+ }
162852
+ return map;
162853
+ }
162854
+ /**
162855
+ * Index local migration SQL by version. Canonical `cloudbase/migrations/` wins over legacy `migrations/`.
162856
+ */
162857
+ function listLocalMigrationFilesByVersion() {
162858
+ const projectRoot = getMigrationProjectRoot();
162859
+ const legacy = listLocalMigrationFilesInDir(path.join(projectRoot, LOCAL_MIGRATIONS_DIR_LEGACY), LOCAL_MIGRATIONS_DIR_LEGACY);
162860
+ const canonical = listLocalMigrationFilesInDir(path.join(projectRoot, LOCAL_MIGRATIONS_DIR), LOCAL_MIGRATIONS_DIR);
162861
+ const merged = new Map(legacy);
162862
+ for (const [version, entry] of canonical) {
162863
+ merged.set(version, entry);
162864
+ }
162865
+ return merged;
162866
+ }
162867
+ function remoteHistoryCoveredByLocalFiles(summaries, localByVersion, pendingVersion) {
162868
+ for (const summary of summaries) {
162869
+ if (summary.Version === pendingVersion) {
162870
+ // Pending SQL comes from the tool args / ensureLocal write.
162871
+ continue;
162872
+ }
162873
+ const local = localByVersion.get(summary.Version);
162874
+ if (!local || !local.query.trim()) {
162875
+ return false;
162876
+ }
162877
+ }
162878
+ return true;
162879
+ }
162880
+ /**
162881
+ * Build Push/Preview Migrations = full candidate set (server skips already-applied).
162882
+ *
162883
+ * Prefer CLI parity when the workspace already has SQL for every remote version:
162884
+ * read local files (no per-version DescribePGUserMigration). Otherwise hydrate
162885
+ * remote Queries via List + N×Describe (agent/remote-first when the local tree is incomplete).
162886
+ */
162887
+ async function buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions) {
162888
+ const { summaries, latestVersion } = await listAllRemoteMigrationSummaries(context, cloudBaseOptions);
162889
+ const localByVersion = listLocalMigrationFilesByVersion();
162890
+ if (remoteHistoryCoveredByLocalFiles(summaries, localByVersion, pending.Version)) {
162891
+ const migrations = [];
162892
+ for (const summary of summaries) {
162893
+ if (summary.Version === pending.Version) {
162894
+ continue;
162895
+ }
162896
+ const local = localByVersion.get(summary.Version);
162897
+ migrations.push({
162898
+ Version: local.version,
162899
+ Name: local.name || summary.Name,
162900
+ Query: local.query,
162901
+ });
162902
+ }
162903
+ migrations.push(pending);
162904
+ return {
162905
+ migrations,
162906
+ remoteCount: summaries.length,
162907
+ latestVersion,
162908
+ payloadSource: "local",
162909
+ };
162910
+ }
162911
+ const migrations = [];
162912
+ for (const summary of summaries) {
162913
+ if (summary.Version === pending.Version) {
162914
+ // Caller supplies the authoritative SQL for this version.
162915
+ continue;
162916
+ }
162917
+ const detail = await callPgMigrationApi(context, "DescribePGUserMigration", { MigrationVersion: summary.Version }, cloudBaseOptions);
162918
+ const query = typeof detail.Query === "string" ? detail.Query : "";
162919
+ if (!query.trim()) {
162920
+ throw new Error(`DescribePGUserMigration returned empty Query for version=${summary.Version} (${summary.Name}). ` +
162921
+ "Cannot hydrate remote history for PushPGUserMigrations.");
162922
+ }
162923
+ migrations.push({
162924
+ Version: typeof detail.Version === "string" && detail.Version.trim() ? detail.Version.trim() : summary.Version,
162925
+ Name: typeof detail.Name === "string" && detail.Name.trim() ? detail.Name.trim() : summary.Name,
162926
+ Query: query,
162927
+ });
162928
+ }
162929
+ migrations.push(pending);
162930
+ return {
162931
+ migrations,
162932
+ remoteCount: summaries.length,
162933
+ latestVersion,
162934
+ payloadSource: "hydrate",
162935
+ };
162936
+ }
162937
+ function formatMigrationsPayloadSourceLabel(source, remoteCount) {
162938
+ if (remoteCount <= 0) {
162939
+ return "no remote history";
162940
+ }
162941
+ return source === "local"
162942
+ ? `local tree covered ${remoteCount} remote migration(s) (CLI parity, skipped Describe hydrate)`
162943
+ : `hydrated ${remoteCount} remote migration(s) via Describe (local tree incomplete)`;
162944
+ }
162945
+ async function waitPgMigrationTask(context, taskId, cloudBaseOptions, maxWaitMs = MIGRATION_TASK_DEFAULT_WAIT_MS) {
162946
+ const deadline = Date.now() + maxWaitMs;
162947
+ let last = { TaskId: taskId };
162948
+ while (Date.now() <= deadline) {
162949
+ last = (await callPgMigrationApi(context, "DescribeTaskResult", { TaskId: taskId }, cloudBaseOptions));
162950
+ const status = String(last.Status || "").toLowerCase();
162951
+ if (status === "succeed" || status === "failed") {
162952
+ return last;
162953
+ }
162954
+ await sleep(MIGRATION_TASK_POLL_INTERVAL_MS);
162955
+ }
162956
+ const error = new Error(`DescribeTaskResult timed out after ${Math.floor(maxWaitMs / 1000)}s ` +
162957
+ `for TaskId=${taskId} (last Status=${last.Status || "-"}, Phase=${last.Phase || "-"}).`);
162958
+ error.lastTask = last;
162959
+ throw error;
162960
+ }
162961
+ function buildMigrationTaskPendingNextActions(version, taskId) {
162962
+ const actions = [];
162963
+ if (taskId) {
162964
+ actions.push(buildNextAction(MANAGE_PG_DATABASE, "describeMigrationTask", "FIRST: poll DescribeTaskResult for Status/Phase/Reason. listMigrations alone cannot explain a Failed task (see issue #857).", { action: "describeMigrationTask", taskId }));
162965
+ }
162966
+ actions.push(buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Check whether migrationVersion landed in remote history. Do NOT re-push the same version while the task may still be running.", { action: "listMigrations", limit: 20 }), buildNextAction(MANAGE_PG_DATABASE, "migrationDetail", "If listMigrations already shows this version, inspect the applied record. If missing after several minutes and the task is Failed/terminal, inspect Conflicts/SQL before considering a NEW migrationVersion.", { action: "migrationDetail", migrationVersion: version }));
162967
+ return actions;
162968
+ }
162304
162969
  const MIGRATION_VERSION_REQUIRED_MESSAGE = "migrationVersion is required (14-digit UTC timestamp YYYYMMDDHHMMSS). " +
162305
- "Decide the version first, write local file migrations/<version>_<migrationName>.sql, " +
162970
+ "Decide the version first, write local file cloudbase/migrations/<version>_<migrationName>.sql, " +
162306
162971
  "then call planMigration/applyMigration with the same migrationVersion and migrationName.";
162307
162972
  function requireExplicitMigrationVersion(args, actionLabel) {
162308
162973
  const version = args.migrationVersion?.trim();
@@ -162639,6 +163304,30 @@ function createDefaultDependencies(server) {
162639
163304
  createClient: async (context) => createManagerPgClient(context, server?.cloudBaseOptions),
162640
163305
  };
162641
163306
  }
163307
+ function isPgMigrationApiDatabase(value) {
163308
+ return Boolean(value &&
163309
+ typeof value === "object" &&
163310
+ "previewPGUserMigrations" in value &&
163311
+ typeof value.previewPGUserMigrations === "function" &&
163312
+ "pushPGUserMigrations" in value &&
163313
+ typeof value.pushPGUserMigrations === "function" &&
163314
+ "listPGUserMigrations" in value &&
163315
+ typeof value.listPGUserMigrations === "function" &&
163316
+ "describeTaskResult" in value &&
163317
+ typeof value.describeTaskResult === "function");
163318
+ }
163319
+ /**
163320
+ * Actions without a native wrapper in manager-node (e.g. RollbackPGUserMigrations)
163321
+ * are intentionally absent and keep using the commonService fallback.
163322
+ */
163323
+ const PgMigrationNativeActionMethods = {
163324
+ PreviewPGUserMigrations: "previewPGUserMigrations",
163325
+ PushPGUserMigrations: "pushPGUserMigrations",
163326
+ RepairPGUserMigrationHistory: "repairPGUserMigrationHistory",
163327
+ ListPGUserMigrations: "listPGUserMigrations",
163328
+ DescribePGUserMigration: "describePGUserMigration",
163329
+ DescribeTaskResult: "describeTaskResult",
163330
+ };
162642
163331
  function isExecutePGSqlDatabase(value) {
162643
163332
  return Boolean(value &&
162644
163333
  typeof value === "object" &&
@@ -162679,13 +163368,22 @@ async function executeManagerPGSql(context, sql, cloudBaseOptions) {
162679
163368
  }
162680
163369
  throw new Error("Current @cloudbase/manager-node runtime does not expose database.executePGSql or commonService fallback. Upgrade to @cloudbase/manager-node >= 5.4.0.");
162681
163370
  }
162682
- /** Call a CloudBase PG migration API via commonService fallback */
163371
+ /**
163372
+ * Call a CloudBase PG migration API, preferring native manager-node methods
163373
+ * (>= 5.6.5) and falling back to the commonService channel for older runtimes
163374
+ * and actions without a native wrapper (RollbackPGUserMigrations).
163375
+ */
162683
163376
  async function callPgMigrationApi(context, action, params, cloudBaseOptions) {
162684
163377
  const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({
162685
163378
  cloudBaseOptions: cloudBaseOptions
162686
163379
  ? { ...cloudBaseOptions, envId: context.envId }
162687
163380
  : { envId: context.envId },
162688
163381
  });
163382
+ const nativeMethod = PgMigrationNativeActionMethods[action];
163383
+ if (nativeMethod && isPgMigrationApiDatabase(manager.database)) {
163384
+ const invoke = manager.database[nativeMethod];
163385
+ return invoke.call(manager.database, { EnvId: context.envId, ...params });
163386
+ }
162689
163387
  if (!isCloudBaseWithCommonService(manager)) {
162690
163388
  throw new Error("Current @cloudbase/manager-node runtime does not support migration APIs. Upgrade to @cloudbase/manager-node >= 5.4.0.");
162691
163389
  }
@@ -162926,11 +163624,11 @@ async function handleExecuteSql(args, context, deps) {
162926
163624
  success: false,
162927
163625
  errorCode: "DDL_USE_APPLY_MIGRATION",
162928
163626
  message: "Schema DDL (CREATE/ALTER/DROP/TRUNCATE/...) must use applyMigration with an explicit migrationVersion, " +
162929
- "not execute. Write local migrations/<version>_<name>.sql first, then call applyMigration. " +
163627
+ "not execute. Write local cloudbase/migrations/<version>_<name>.sql first, then call applyMigration. " +
162930
163628
  "Set allowDdlViaExecute=true only for exceptional one-off ops that intentionally bypass migration history.",
162931
163629
  data: {
162932
163630
  classification,
162933
- localFileHintPattern: "migrations/<migrationVersion>_<migrationName>.sql",
163631
+ localFileHintPattern: "cloudbase/migrations/<migrationVersion>_<migrationName>.sql",
162934
163632
  },
162935
163633
  nextActions: [
162936
163634
  buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Preview the schema change as a versioned migration (provide migrationVersion + migrationName).", {
@@ -163039,7 +163737,7 @@ async function handleDryRun(args) {
163039
163737
  wouldExecute: false,
163040
163738
  sqlPreview: args.sql.trim().slice(0, 500),
163041
163739
  preferredAction: "applyMigration",
163042
- localFileHintPattern: "migrations/<migrationVersion>_<migrationName>.sql",
163740
+ localFileHintPattern: "cloudbase/migrations/<migrationVersion>_<migrationName>.sql",
163043
163741
  },
163044
163742
  message: "SQL dry run completed. Schema DDL should use applyMigration with an explicit migrationVersion, not execute.",
163045
163743
  nextActions: [
@@ -163087,35 +163785,50 @@ async function handlePlanMigration(args, context, deps, cloudBaseOptions) {
163087
163785
  }
163088
163786
  const version = versionOrError;
163089
163787
  const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
163090
- const migration = {
163788
+ const pending = {
163091
163789
  Version: version,
163092
163790
  Name: args.migrationName,
163093
163791
  Query: args.sql,
163094
163792
  };
163095
163793
  if (args.rollbackSql?.trim()) {
163096
- migration.Rollback = args.rollbackSql;
163794
+ pending.Rollback = args.rollbackSql;
163097
163795
  }
163098
163796
  try {
163797
+ const hydrated = await buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions);
163798
+ const includeAll = Boolean(args.includeAll);
163099
163799
  const result = await callPgMigrationApi(context, "PreviewPGUserMigrations", {
163100
- Migrations: [migration],
163800
+ Migrations: hydrated.migrations,
163801
+ IncludeAll: includeAll,
163101
163802
  }, cloudBaseOptions);
163803
+ const executable = result.Executable === true;
163804
+ const payloadSourceLabel = formatMigrationsPayloadSourceLabel(hydrated.payloadSource, hydrated.remoteCount);
163102
163805
  return buildPgToolResult({
163103
163806
  success: true,
163104
163807
  data: {
163105
163808
  migrationVersion: version,
163106
163809
  migrationName: args.migrationName,
163107
163810
  localFileHint,
163811
+ hydratedRemoteCount: hydrated.remoteCount,
163812
+ latestRemoteVersion: hydrated.latestVersion || null,
163813
+ payloadSource: hydrated.payloadSource,
163814
+ includeAll,
163815
+ executable,
163108
163816
  apiResult: result,
163109
163817
  },
163110
- message: `Migration plan generated via PreviewPGUserMigrations. Reuse migrationVersion=${version} on applyMigration. Ensure local file ${localFileHint} exists and matches.`,
163818
+ message: executable
163819
+ ? `Migration plan generated via PreviewPGUserMigrations (${payloadSourceLabel}${includeAll ? ", includeAll=true" : ""}). Reuse migrationVersion=${version} on applyMigration. Ensure local file ${localFileHint} exists and matches.`
163820
+ : `Migration plan is NOT executable (PreviewPGUserMigrations.Executable=false after ${payloadSourceLabel}${includeAll ? ", includeAll=true" : ""}). Inspect apiResult.Conflicts before applyMigration. Common causes: version older than latest remote (${hydrated.latestVersion || "unknown"}) — retry with includeAll=true (CLI --include-all) if intentionally out-of-order, or pick a newer version; checksum mismatch.`,
163111
163821
  nextActions: [
163112
- buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Review the plan above. If it looks correct, call applyMigration with the same migrationVersion.", {
163822
+ buildNextAction(MANAGE_PG_DATABASE, "applyMigration", executable
163823
+ ? "Review the plan above. If it looks correct, call applyMigration with the same migrationVersion."
163824
+ : "Resolve Conflicts (often pick a migrationVersion newer than LatestVersion, or set includeAll=true for intentional out-of-order), then retry planMigration/applyMigration.", {
163113
163825
  action: "applyMigration",
163114
163826
  migrationName: args.migrationName,
163115
163827
  migrationVersion: version,
163116
163828
  sql: args.sql,
163117
163829
  confirm: true,
163118
163830
  ...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
163831
+ ...(includeAll ? { includeAll: true } : {}),
163119
163832
  }),
163120
163833
  ],
163121
163834
  });
@@ -163156,26 +163869,181 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
163156
163869
  }
163157
163870
  const version = versionOrError;
163158
163871
  const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
163159
- const migration = {
163872
+ // Prefer write-to-workspace (or fail closed) so remote history cannot advance without a Git truth source.
163873
+ const localFile = ensureLocalMigrationSqlFile(version, args.migrationName, args.sql);
163874
+ if (!localFile.ok) {
163875
+ return buildPgToolResult({
163876
+ success: false,
163877
+ errorCode: localFile.errorCode,
163878
+ data: {
163879
+ migrationVersion: version,
163880
+ migrationName: args.migrationName,
163881
+ localFileHint,
163882
+ localFilePath: localFile.relativePath ?? localFileHint,
163883
+ localFileAbsolutePath: localFile.absolutePath ?? null,
163884
+ verified: false,
163885
+ },
163886
+ message: localFile.message,
163887
+ nextActions: [
163888
+ buildNextAction(MANAGE_PG_DATABASE, "planMigration", "After aligning the local SQL file with the sql argument (or choosing a new migrationVersion), preview again.", {
163889
+ action: "planMigration",
163890
+ migrationName: args.migrationName,
163891
+ migrationVersion: version,
163892
+ sql: args.sql,
163893
+ ...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
163894
+ ...(args.includeAll ? { includeAll: true } : {}),
163895
+ }),
163896
+ ],
163897
+ });
163898
+ }
163899
+ const pending = {
163160
163900
  Version: version,
163161
163901
  Name: args.migrationName,
163162
163902
  Query: args.sql,
163163
163903
  };
163164
163904
  if (args.rollbackSql?.trim()) {
163165
- migration.Rollback = args.rollbackSql;
163166
- }
163167
- const params = { Migrations: [migration] };
163168
- if (args.lockTimeoutMs !== undefined) {
163169
- params.LockTimeoutMs = args.lockTimeoutMs;
163170
- }
163171
- if (args.statementTimeoutMs !== undefined) {
163172
- params.StatementTimeoutMs = args.statementTimeoutMs;
163905
+ pending.Rollback = args.rollbackSql;
163173
163906
  }
163907
+ const includeAll = Boolean(args.includeAll);
163174
163908
  try {
163909
+ // Pushing only the pending migration while remote history exists makes the plan
163910
+ // non-executable (remote_history_not_found_locally). Prefer local tree when complete
163911
+ // (CLI parity); otherwise hydrate remote Queries (List + N×Describe).
163912
+ const hydrated = await buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions);
163913
+ const payloadSourceLabel = formatMigrationsPayloadSourceLabel(hydrated.payloadSource, hydrated.remoteCount);
163914
+ const preview = await callPgMigrationApi(context, "PreviewPGUserMigrations", { Migrations: hydrated.migrations, IncludeAll: includeAll }, cloudBaseOptions);
163915
+ if (preview.Executable !== true) {
163916
+ return buildPgToolResult({
163917
+ success: false,
163918
+ errorCode: "MIGRATION_NOT_EXECUTABLE",
163919
+ data: {
163920
+ migrationVersion: version,
163921
+ migrationName: args.migrationName,
163922
+ localFileHint,
163923
+ hydratedRemoteCount: hydrated.remoteCount,
163924
+ latestRemoteVersion: hydrated.latestVersion || null,
163925
+ payloadSource: hydrated.payloadSource,
163926
+ includeAll,
163927
+ previewResult: preview,
163928
+ verified: false,
163929
+ },
163930
+ message: `PreviewPGUserMigrations reported Executable=false for migrationVersion=${version} ` +
163931
+ `(latest remote=${hydrated.latestVersion || "unknown"}, ${payloadSourceLabel}, includeAll=${includeAll}). ` +
163932
+ "Push was NOT submitted. Inspect previewResult.Conflicts — common reasons: " +
163933
+ "local_migration_before_latest_remote (pick a newer 14-digit version, or set includeAll=true / CLI --include-all for intentional out-of-order) or checksum_mismatch. " +
163934
+ "If the schema change is urgent you may fall back to action=execute with allowDdlViaExecute=true, but that bypasses migration history.",
163935
+ nextActions: [
163936
+ buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Check LatestVersion and pick a migrationVersion strictly newer than it.", { action: "listMigrations", limit: 20 }),
163937
+ buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Re-run planMigration after adjusting migrationVersion/SQL (or includeAll=true for out-of-order).", {
163938
+ action: "planMigration",
163939
+ migrationName: args.migrationName,
163940
+ migrationVersion: version,
163941
+ sql: args.sql,
163942
+ ...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
163943
+ ...(includeAll ? { includeAll: true } : {}),
163944
+ }),
163945
+ ],
163946
+ });
163947
+ }
163948
+ const params = {
163949
+ Migrations: hydrated.migrations,
163950
+ IncludeAll: includeAll,
163951
+ };
163952
+ if (args.lockTimeoutMs !== undefined) {
163953
+ params.LockTimeoutMs = args.lockTimeoutMs;
163954
+ }
163955
+ if (args.statementTimeoutMs !== undefined) {
163956
+ params.StatementTimeoutMs = args.statementTimeoutMs;
163957
+ }
163175
163958
  const result = await callPgMigrationApi(context, "PushPGUserMigrations", params, cloudBaseOptions);
163176
- // PushPGUserMigrations may return a TaskId without the migration having taken
163177
- // effect. Confirm against the remote history before reporting success, so callers
163178
- // never get a false-positive success for a silently dropped migration.
163959
+ const taskId = typeof result.TaskId === "string" ? result.TaskId.trim() : "";
163960
+ const pollTimeoutMs = resolveMigrationTaskPollTimeoutMs(args.taskPollTimeoutMs);
163961
+ const shouldWaitForTask = args.waitForTask !== false;
163962
+ let taskResult = null;
163963
+ if (taskId && !shouldWaitForTask) {
163964
+ return buildPgToolResult({
163965
+ success: false,
163966
+ errorCode: "MIGRATION_TASK_PENDING",
163967
+ data: {
163968
+ migrationVersion: version,
163969
+ migrationName: args.migrationName,
163970
+ localFileHint,
163971
+ hydratedRemoteCount: hydrated.remoteCount,
163972
+ payloadSource: hydrated.payloadSource,
163973
+ apiResult: result,
163974
+ taskResult: { TaskId: taskId },
163975
+ taskPollTimeoutMs: pollTimeoutMs,
163976
+ waitForTask: false,
163977
+ verified: null,
163978
+ },
163979
+ message: `PushPGUserMigrations accepted TaskId=${taskId} (waitForTask=false). ` +
163980
+ "TaskId means accepted, not applied. Call action=describeMigrationTask FIRST for Status/Phase/Reason, " +
163981
+ `then listMigrations to see whether migrationVersion=${version} landed. ` +
163982
+ "Do NOT re-push the same version while the task may still be running.",
163983
+ nextActions: buildMigrationTaskPendingNextActions(version, taskId),
163984
+ });
163985
+ }
163986
+ if (taskId) {
163987
+ try {
163988
+ taskResult = await waitPgMigrationTask(context, taskId, cloudBaseOptions, pollTimeoutMs);
163989
+ }
163990
+ catch (error) {
163991
+ const lastTask = error.lastTask;
163992
+ return buildPgToolResult({
163993
+ success: false,
163994
+ errorCode: "MIGRATION_TASK_TIMEOUT",
163995
+ data: {
163996
+ migrationVersion: version,
163997
+ migrationName: args.migrationName,
163998
+ localFileHint,
163999
+ hydratedRemoteCount: hydrated.remoteCount,
164000
+ apiResult: result,
164001
+ taskResult: lastTask ?? { TaskId: taskId },
164002
+ taskPollTimeoutMs: pollTimeoutMs,
164003
+ verified: null,
164004
+ },
164005
+ message: `PushPGUserMigrations returned TaskId=${taskId}, but waiting for DescribeTaskResult timed out ` +
164006
+ `after ${Math.floor(pollTimeoutMs / 1000)}s: ${error instanceof Error ? error.message : String(error)}. ` +
164007
+ "The migration may STILL be running in the background (large DDL / lock waits). " +
164008
+ "Call action=describeMigrationTask FIRST (Status/Phase/Reason), then listMigrations. " +
164009
+ "Do NOT re-push the same migrationVersion, and do NOT fall back to execute until the task is terminal " +
164010
+ "and listMigrations confirms the version is missing.",
164011
+ nextActions: buildMigrationTaskPendingNextActions(version, taskId),
164012
+ });
164013
+ }
164014
+ const status = String(taskResult.Status || "").toLowerCase();
164015
+ if (status === "failed") {
164016
+ return buildPgToolResult({
164017
+ success: false,
164018
+ errorCode: "MIGRATION_TASK_FAILED",
164019
+ data: {
164020
+ migrationVersion: version,
164021
+ migrationName: args.migrationName,
164022
+ localFileHint,
164023
+ hydratedRemoteCount: hydrated.remoteCount,
164024
+ apiResult: result,
164025
+ taskResult: taskResult,
164026
+ verified: false,
164027
+ },
164028
+ message: `PushPGUserMigrations TaskId=${taskId} failed at phase=${taskResult.Phase || "-"}: ` +
164029
+ `${taskResult.Reason || "unknown reason"}. ` +
164030
+ "The migration did NOT take effect. Fix Conflicts/SQL (or use a newer migrationVersion), then retry applyMigration. " +
164031
+ "Urgent bypass: action=execute with allowDdlViaExecute=true (skips migration history).",
164032
+ nextActions: [
164033
+ buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Preview again to inspect Conflicts before retrying apply.", {
164034
+ action: "planMigration",
164035
+ migrationName: args.migrationName,
164036
+ migrationVersion: version,
164037
+ sql: args.sql,
164038
+ ...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
164039
+ ...(includeAll ? { includeAll: true } : {}),
164040
+ }),
164041
+ buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Confirm the version is still absent from remote history.", { action: "listMigrations", limit: 20 }),
164042
+ ],
164043
+ });
164044
+ }
164045
+ }
164046
+ // Task Succeed (or sync response without TaskId): confirm history records the version.
163179
164047
  const verification = await verifyMigrationApplied(context, version, cloudBaseOptions);
163180
164048
  if (verification.applied === false) {
163181
164049
  return buildPgToolResult({
@@ -163185,10 +164053,13 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
163185
164053
  migrationVersion: version,
163186
164054
  migrationName: args.migrationName,
163187
164055
  localFileHint,
164056
+ hydratedRemoteCount: hydrated.remoteCount,
164057
+ includeAll,
163188
164058
  apiResult: result,
164059
+ taskResult: taskResult,
163189
164060
  verified: false,
163190
164061
  },
163191
- message: `PushPGUserMigrations returned without error, but migrationVersion=${version} is not present in the remote migration history, ` +
164062
+ message: `PushPGUserMigrations completed (TaskId=${taskId || "none"}), but migrationVersion=${version} is not present in the remote migration history, ` +
163192
164063
  "so the migration did NOT take effect. Do not assume the schema change is applied. " +
163193
164064
  "Re-check the SQL and migrationVersion, inspect the migration with action=migrationDetail, and retry action=applyMigration. " +
163194
164065
  "If the schema change is urgent you may fall back to action=execute with allowDdlViaExecute=true, but that bypasses migration history.",
@@ -163206,7 +164077,9 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
163206
164077
  migrationVersion: version,
163207
164078
  migrationName: args.migrationName,
163208
164079
  localFileHint,
164080
+ hydratedRemoteCount: hydrated.remoteCount,
163209
164081
  apiResult: result,
164082
+ taskResult: taskResult,
163210
164083
  verified: null,
163211
164084
  },
163212
164085
  message: `PushPGUserMigrations was submitted, but verifying migrationVersion=${version} against the remote history failed: ${verification.error}. ` +
@@ -163222,10 +164095,18 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
163222
164095
  migrationVersion: version,
163223
164096
  migrationName: args.migrationName,
163224
164097
  localFileHint,
164098
+ localFilePath: localFile.relativePath,
164099
+ localFileAbsolutePath: localFile.absolutePath,
164100
+ localFileAction: localFile.action,
164101
+ hydratedRemoteCount: hydrated.remoteCount,
164102
+ payloadSource: hydrated.payloadSource,
164103
+ includeAll,
163225
164104
  apiResult: result,
164105
+ taskResult: taskResult,
163226
164106
  verified: true,
163227
164107
  },
163228
- message: `Migrations applied via PushPGUserMigrations and verified present in the remote migration history. Ensure local file ${localFileHint} exists and matches.`,
164108
+ message: `Migrations applied via PushPGUserMigrations (${payloadSourceLabel}${includeAll ? ", includeAll=true" : ""}), task ${taskId ? `TaskId=${taskId} ` : ""}verified present in the remote migration history. ` +
164109
+ `Local SQL ${localFile.action === "created" ? "written" : "matched"} at ${localFile.relativePath}.`,
163229
164110
  });
163230
164111
  }
163231
164112
  catch (error) {
@@ -163286,6 +164167,156 @@ async function handleMigrationDetail(args, context, deps, cloudBaseOptions) {
163286
164167
  });
163287
164168
  }
163288
164169
  }
164170
+ /**
164171
+ * One-shot DescribeTaskResult for a PushPGUserMigrations TaskId.
164172
+ * Complements listMigrations: history shows whether a version landed; task result shows
164173
+ * Status/Phase/Reason while the job is running or after it Failed (issue #857 evidence path).
164174
+ */
164175
+ async function handleDescribeMigrationTask(args, context, _deps, cloudBaseOptions) {
164176
+ const taskId = args.taskId?.trim();
164177
+ if (!taskId) {
164178
+ return buildPgToolResult({
164179
+ success: false,
164180
+ errorCode: "TASK_ID_REQUIRED",
164181
+ message: "Provide taskId when action=describeMigrationTask " +
164182
+ "(from applyMigration Push / MIGRATION_TASK_PENDING / MIGRATION_TASK_TIMEOUT data.taskResult.TaskId).",
164183
+ });
164184
+ }
164185
+ try {
164186
+ const taskResult = (await callPgMigrationApi(context, "DescribeTaskResult", { TaskId: taskId }, cloudBaseOptions));
164187
+ const status = String(taskResult.Status || "").toLowerCase();
164188
+ const versionHint = args.migrationVersion?.trim();
164189
+ const nextActions = [];
164190
+ if (status === "succeed") {
164191
+ nextActions.push(buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Task Succeed — confirm migrationVersion is present in remote history before dependent SQL.", { action: "listMigrations", limit: 20 }));
164192
+ if (versionHint) {
164193
+ nextActions.push(buildNextAction(MANAGE_PG_DATABASE, "migrationDetail", "Inspect the applied migration record.", { action: "migrationDetail", migrationVersion: versionHint }));
164194
+ }
164195
+ }
164196
+ else if (status === "failed") {
164197
+ nextActions.push(buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Confirm the version is absent from history, then fix Conflicts/SQL and retry with a NEW migrationVersion (do not re-push the failed version blindly).", { action: "listMigrations", limit: 20 }));
164198
+ }
164199
+ else {
164200
+ nextActions.push(buildNextAction(MANAGE_PG_DATABASE, "describeMigrationTask", "Task still running — poll again shortly. Do NOT re-push the same migrationVersion.", { action: "describeMigrationTask", taskId }), buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Optionally check whether the version already landed while Status is still non-terminal.", { action: "listMigrations", limit: 20 }));
164201
+ }
164202
+ const statusLabel = taskResult.Status || "unknown";
164203
+ return buildPgToolResult({
164204
+ success: true,
164205
+ data: {
164206
+ taskId,
164207
+ taskResult: taskResult,
164208
+ terminal: status === "succeed" || status === "failed",
164209
+ ...(versionHint ? { migrationVersion: versionHint } : {}),
164210
+ },
164211
+ message: status === "succeed"
164212
+ ? `DescribeTaskResult TaskId=${taskId} Status=Succeed (Phase=${taskResult.Phase || "-"}). Verify with listMigrations before treating schema as applied.`
164213
+ : status === "failed"
164214
+ ? `DescribeTaskResult TaskId=${taskId} Status=Failed at phase=${taskResult.Phase || "-"}: ${taskResult.Reason || "unknown reason"}. Do NOT re-push the same migrationVersion until Conflicts/SQL are fixed.`
164215
+ : `DescribeTaskResult TaskId=${taskId} Status=${statusLabel} Phase=${taskResult.Phase || "-"}. Task is not terminal yet — poll again; do not re-push.`,
164216
+ nextActions,
164217
+ });
164218
+ }
164219
+ catch (error) {
164220
+ return buildPgToolResult({
164221
+ success: false,
164222
+ errorCode: "MIGRATION_API_ERROR",
164223
+ message: `DescribeTaskResult failed for TaskId=${taskId}: ${error instanceof Error ? error.message : String(error)}`,
164224
+ });
164225
+ }
164226
+ }
164227
+ /**
164228
+ * Pull remote migration history SQL into cloudbase/migrations/ (CLI `tcb db pg migration fetch`).
164229
+ * Optional migrationVersion fetches one record; omit for full history. force overwrites existing files.
164230
+ */
164231
+ async function handleFetchMigration(args, context, _deps, cloudBaseOptions) {
164232
+ const force = args.force === true;
164233
+ const singleVersion = args.migrationVersion?.trim();
164234
+ try {
164235
+ const remoteItems = [];
164236
+ if (singleVersion) {
164237
+ const detail = await callPgMigrationApi(context, "DescribePGUserMigration", { MigrationVersion: singleVersion }, cloudBaseOptions);
164238
+ const version = typeof detail.Version === "string" && detail.Version.trim()
164239
+ ? detail.Version.trim()
164240
+ : singleVersion;
164241
+ const name = typeof detail.Name === "string" && detail.Name.trim()
164242
+ ? detail.Name.trim()
164243
+ : "";
164244
+ const query = typeof detail.Query === "string" ? detail.Query : "";
164245
+ remoteItems.push({ Version: version, Name: name || version, Query: query });
164246
+ }
164247
+ else {
164248
+ const { summaries } = await listAllRemoteMigrationSummaries(context, cloudBaseOptions);
164249
+ for (const summary of summaries) {
164250
+ const detail = await callPgMigrationApi(context, "DescribePGUserMigration", { MigrationVersion: summary.Version }, cloudBaseOptions);
164251
+ const version = typeof detail.Version === "string" && detail.Version.trim()
164252
+ ? detail.Version.trim()
164253
+ : summary.Version;
164254
+ const name = typeof detail.Name === "string" && detail.Name.trim()
164255
+ ? detail.Name.trim()
164256
+ : summary.Name;
164257
+ const query = typeof detail.Query === "string" ? detail.Query : "";
164258
+ remoteItems.push({ Version: version, Name: name, Query: query });
164259
+ }
164260
+ }
164261
+ const sync = syncFetchedMigrationsToLocalFiles(remoteItems, force);
164262
+ if (!sync.ok) {
164263
+ return buildPgToolResult({
164264
+ success: false,
164265
+ errorCode: sync.errorCode,
164266
+ message: sync.message,
164267
+ data: {
164268
+ migrationsDir: LOCAL_MIGRATIONS_DIR,
164269
+ force,
164270
+ localFilePath: sync.relativePath,
164271
+ },
164272
+ });
164273
+ }
164274
+ const written = sync.files.filter((f) => f.action === "written");
164275
+ const skipped = sync.files.filter((f) => f.action === "skipped");
164276
+ return buildPgToolResult({
164277
+ success: true,
164278
+ data: {
164279
+ migrationsDir: LOCAL_MIGRATIONS_DIR,
164280
+ force,
164281
+ migrationVersion: singleVersion || null,
164282
+ total: remoteItems.length,
164283
+ writtenCount: written.length,
164284
+ skippedCount: skipped.length,
164285
+ written: written.map((f) => f.relativePath),
164286
+ skipped: skipped.map((f) => f.relativePath),
164287
+ files: sync.files.map(({ version, name, relativePath, action }) => ({
164288
+ version,
164289
+ name,
164290
+ relativePath,
164291
+ action,
164292
+ })),
164293
+ },
164294
+ message: remoteItems.length === 0
164295
+ ? "No remote migrations to fetch. Local cloudbase/migrations/ unchanged."
164296
+ : `Fetched ${remoteItems.length} remote migration(s): wrote ${written.length}, skipped ${skipped.length}` +
164297
+ (skipped.length > 0 && !force
164298
+ ? " (existing files left untouched; pass force=true to overwrite, matching CLI --force)."
164299
+ : ".") +
164300
+ ` Directory: ${LOCAL_MIGRATIONS_DIR}/.`,
164301
+ nextActions: skipped.length > 0 && !force
164302
+ ? [
164303
+ buildNextAction(MANAGE_PG_DATABASE, "fetchMigration", "Re-run with force=true to overwrite skipped local files from remote history (checksum realign).", {
164304
+ action: "fetchMigration",
164305
+ force: true,
164306
+ ...(singleVersion ? { migrationVersion: singleVersion } : {}),
164307
+ }),
164308
+ ]
164309
+ : undefined,
164310
+ });
164311
+ }
164312
+ catch (error) {
164313
+ return buildPgToolResult({
164314
+ success: false,
164315
+ errorCode: "MIGRATION_API_ERROR",
164316
+ message: `fetchMigration failed: ${error instanceof Error ? error.message : String(error)}`,
164317
+ });
164318
+ }
164319
+ }
163289
164320
  async function handleRollbackMigration(args, context, deps, cloudBaseOptions) {
163290
164321
  if (args.lastN === undefined || args.lastN < 1 || !Number.isInteger(args.lastN)) {
163291
164322
  return buildPgToolResult({
@@ -163500,11 +164531,11 @@ function registerPGDatabaseTools(server, providedDeps) {
163500
164531
  });
163501
164532
  server.registerTool?.(MANAGE_PG_DATABASE, {
163502
164533
  title: "管理 PostgreSQL 上下文或执行写入 SQL",
163503
- description: "管理 CloudBase PostgreSQL:执行已确认的写入 SQL、SQL 风险预检、迁移管理。建表/ALTER/DROP 等 schema 变更必须使用 applyMigration(显式 migrationVersion + 本地 migrations/ 留档),不要默认用 execute。execute 主要用于 DML 与 GRANT/RLS 等运维 SQL。",
164534
+ description: "管理 CloudBase PostgreSQL:执行已确认的写入 SQL、SQL 风险预检、迁移管理。建表/ALTER/DROP 等 schema 变更必须使用 applyMigration(显式 migrationVersion;成功前自动写入或校验本地 cloudbase/migrations/<version>_<name>.sql,与 CLI tcb db pg migration 一致),不要默认用 execute。execute 主要用于 DML 与 GRANT/RLS 等运维 SQL。",
163504
164535
  inputSchema: {
163505
164536
  action: zod_1.z
163506
164537
  .enum(MANAGE_ACTIONS)
163507
- .describe("操作类型:execute=执行已确认的写入 SQL(DML/GRANT/RLS;schema DDL 默认拒绝,需 allowDdlViaExecute=true);dryRun=只分析 SQL 风险不执行;planMigration=预览迁移计划(需 migrationName + migrationVersion + sql);applyMigration=应用迁移,建表/改 schema 首选(需 migrationName + migrationVersion + sql + confirm=true;成功返回前会自动校验该 migrationVersion 已落入远端迁移历史,未落库时返回 success=false 且 errorCode=MIGRATION_NOT_APPLIED);listMigrations=查询已应用的 Migration 列表(可传 limit/offset 分页);migrationDetail=查看单条 Migration 详情(需 migrationVersion);rollbackMigration=回滚最近 N 条 Migration(需 lastN + confirm=true);repairMigration=修复 Migration 历史记录(需 migrationVersion + migrationName + repairStatus + repairReason)"),
164538
+ .describe("操作类型:execute=执行已确认的写入 SQL(DML/GRANT/RLS;schema DDL 默认拒绝,需 allowDdlViaExecute=true);dryRun=只分析 SQL 风险不执行;planMigration=预览迁移计划(需 migrationName + migrationVersion + sql;可选 includeAll=true 允许乱序,对齐 CLI --include-all);applyMigration=应用迁移,建表/改 schema 首选(需 migrationName + migrationVersion + sql + confirm=true;可选 includeAll;本地 SQL 缺失则自动写入 cloudbase/migrations/,内容不一致则 LOCAL_MIGRATION_FILE_MISMATCH fail-closed;成功返回前会轮询 DescribeTaskResult(默认最长 10 分钟,可用 taskPollTimeoutMs / waitForTask 调整)并校验 migrationVersion 已落入远端历史;超时返回 MIGRATION_TASK_TIMEOUT,必须先 describeMigrationTask 再 listMigrations,禁止立刻重推同 version;未落库时返回 success=false 且 errorCode=MIGRATION_NOT_APPLIED);listMigrations=查询已应用的 Migration 列表(可传 limit/offset 分页);migrationDetail=查看单条 Migration 详情(需 migrationVersion);describeMigrationTask=按 TaskId 查询 Push 异步任务状态(DescribeTaskResult:Status/Phase/Reason;需 taskId;用于 waitForTask=false / MIGRATION_TASK_TIMEOUT / 失败诊断,listMigrations 看不到 Reason);fetchMigration=从远端 history 拉取 SQL 写入本地 cloudbase/migrations/(对齐 CLI tcb db pg migration fetch;可选 migrationVersion 拉单条,省略则全量;force=true 覆盖已存在文件,默认跳过);rollbackMigration=回滚最近 N 条 Migration(需 lastN + confirm=true);repairMigration=修复 Migration 历史记录(需 migrationVersion + migrationName + repairStatus + repairReason)"),
163508
164539
  sql: zod_1.z
163509
164540
  .string()
163510
164541
  .optional()
@@ -163542,7 +164573,7 @@ function registerPGDatabaseTools(server, providedDeps) {
163542
164573
  .string()
163543
164574
  .regex(/^\d{14}$/)
163544
164575
  .optional()
163545
- .describe("14 位时间戳 YYYYMMDDHHMMSS。plan/apply/detail/repair 必填;禁止由服务端静默生成,避免与本地 migrations/<version>_<name>.sql 分叉。"),
164576
+ .describe("14 位时间戳 YYYYMMDDHHMMSS。plan/apply/detail/repair 必填;fetchMigration 可选(传入则只拉该条,省略则拉全量远端 history);禁止由服务端静默生成,避免与本地 cloudbase/migrations/<version>_<name>.sql 分叉。"),
163546
164577
  rollbackSql: zod_1.z
163547
164578
  .string()
163548
164579
  .optional()
@@ -163576,6 +164607,21 @@ function registerPGDatabaseTools(server, providedDeps) {
163576
164607
  .int()
163577
164608
  .optional()
163578
164609
  .describe("apply 可选:单条 SQL 执行最长时间(毫秒),默认 300000。"),
164610
+ taskPollTimeoutMs: zod_1.z
164611
+ .number()
164612
+ .int()
164613
+ .min(MIGRATION_TASK_MIN_WAIT_MS)
164614
+ .max(MIGRATION_TASK_MAX_WAIT_MS)
164615
+ .optional()
164616
+ .describe(`apply 可选:轮询 DescribeTaskResult 的最长等待(毫秒)。默认 ${MIGRATION_TASK_DEFAULT_WAIT_MS}(与 CLI tcb db pg migration up 的 10 分钟对齐)。范围 ${MIGRATION_TASK_MIN_WAIT_MS}-${MIGRATION_TASK_MAX_WAIT_MS}。超时后务必先 describeMigrationTask(taskId) 再 listMigrations,禁止立刻重推同 version。`),
164617
+ waitForTask: zod_1.z
164618
+ .boolean()
164619
+ .optional()
164620
+ .describe("apply 可选,默认 true。设为 false 时 Push 后立即返回 TaskId(errorCode=MIGRATION_TASK_PENDING),由调用方用 describeMigrationTask 轮询任务终态,再用 listMigrations 确认是否落库;适合 MCP host 工具调用超时较短的场景。默认 true 会同步等到任务终态。"),
164621
+ taskId: zod_1.z
164622
+ .string()
164623
+ .optional()
164624
+ .describe("describeMigrationTask 必填:PushPGUserMigrations / applyMigration 返回的 TaskId。用于一次性查询 DescribeTaskResult(Status/Phase/Reason),不轮询等待。"),
163579
164625
  repairStatus: zod_1.z
163580
164626
  .enum(["applied", "reverted"])
163581
164627
  .optional()
@@ -163584,6 +164630,14 @@ function registerPGDatabaseTools(server, providedDeps) {
163584
164630
  .string()
163585
164631
  .optional()
163586
164632
  .describe("repair 必填:修复原因。"),
164633
+ force: zod_1.z
164634
+ .boolean()
164635
+ .optional()
164636
+ .describe("fetchMigration 可选,默认 false。true=覆盖本地已存在的同名 SQL 文件(对齐 CLI tcb db pg migration fetch --force);false=跳过已存在文件。用于从远端 history 重新对齐 Git checksum。"),
164637
+ includeAll: zod_1.z
164638
+ .boolean()
164639
+ .optional()
164640
+ .describe("planMigration / applyMigration 可选,默认 false。true=允许 out-of-order(version 小于远端 LatestVersion)仍可 Preview/Push,对齐 CLI tcb db pg migration up --include-all;仅在确认要补历史/乱序迁移时使用,日常应选更大的 migrationVersion。"),
163587
164641
  allowDdlViaExecute: zod_1.z
163588
164642
  .boolean()
163589
164643
  .optional()
@@ -163631,6 +164685,8 @@ function registerPGDatabaseTools(server, providedDeps) {
163631
164685
  case "applyMigration":
163632
164686
  case "listMigrations":
163633
164687
  case "migrationDetail":
164688
+ case "describeMigrationTask":
164689
+ case "fetchMigration":
163634
164690
  case "rollbackMigration":
163635
164691
  case "repairMigration": {
163636
164692
  switch (args.action) {
@@ -163638,6 +164694,8 @@ function registerPGDatabaseTools(server, providedDeps) {
163638
164694
  case "applyMigration": return handleApplyMigration(args, context, deps, cbOpts);
163639
164695
  case "listMigrations": return handleListMigrations(args, context, deps, cbOpts);
163640
164696
  case "migrationDetail": return handleMigrationDetail(args, context, deps, cbOpts);
164697
+ case "describeMigrationTask": return handleDescribeMigrationTask(args, context, deps, cbOpts);
164698
+ case "fetchMigration": return handleFetchMigration(args, context, deps, cbOpts);
163641
164699
  case "rollbackMigration": return handleRollbackMigration(args, context, deps, cbOpts);
163642
164700
  case "repairMigration": return handleRepairMigration(args, context, deps, cbOpts);
163643
164701
  default: return buildPgToolResult({ success: false, errorCode: "UNSUPPORTED_ACTION", message: `Unsupported action: ${args.action}` });
@@ -185100,7 +186158,7 @@ const KnowledgeBaseIdMap = {
185100
186158
  const CACHE_BASE_DIR = path.join(os.homedir(), ".cloudbase-mcp");
185101
186159
  const CACHE_META_FILE = path.join(CACHE_BASE_DIR, "cache-meta.json");
185102
186160
  const LOCK_FILE = path.join(CACHE_BASE_DIR, ".download.lock");
185103
- const DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 默认 24 小时
186161
+ const DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000; // default 1 hour (was 24h; templates/docs refresh more often)
185104
186162
  // Promise wrapper for lockfile methods
185105
186163
  function acquireLock(lockPath, options) {
185106
186164
  return new Promise((resolve, reject) => {
@@ -193493,10 +194551,11 @@ class AgentService {
193493
194551
  // ==================== 日志查询方法 ====================
193494
194552
  /**
193495
194553
  * 获取 Agent 调用日志(自动判断类型)
194554
+ *
194555
+ * SCF / TCBR 均直接走 CLS `searchClsLog`,返回 CLS 原始检索结果 `ISearchClsLogResponse`。
194556
+ * 分页请透传上次返回的 `LogResults.Context` 到入参 `context`。
193496
194557
  * @param params 查询参数
193497
- * @returns 日志结果,根据 Agent 类型返回不同结构:
193498
- * - SCF: IFunctionLogDetailRes[]
193499
- * - TCBR: ISearchClsLogResponse
194558
+ * @returns CLS 检索结果 ISearchClsLogResponse
193500
194559
  * @example
193501
194560
  * ```typescript
193502
194561
  * const logs = await agentService.getAgentLogs({
@@ -193516,25 +194575,18 @@ class AgentService {
193516
194575
  }
193517
194576
  const agentInfo = agentListRes.AgentList[0];
193518
194577
  const agentType = agentInfo.AgentType;
193519
- // 2. 根据 Agent 类型获取日志
193520
- if (agentType === 'scf') {
193521
- const functionService = this.environment.getFunctionService();
193522
- return functionService.getCompleteFunctionLogs({
193523
- name: AgentId,
193524
- offset: params.offset,
193525
- limit: params.limit,
193526
- startTime: params.startTime,
193527
- endTime: params.endTime,
193528
- requestId: params.requestId
193529
- });
193530
- }
193531
- else if (agentType === 'tcbr') {
194578
+ // scf / tcbr 均直接走 CLS searchClsLog(getFunctionLogs 系列接口已下线)
194579
+ if (agentType === 'scf' || agentType === 'tcbr') {
193532
194580
  const logService = this.environment.getLogService();
193533
- const { startTime, endTime, limit = 100, requestId } = params;
194581
+ const { startTime, endTime, limit = 100, requestId, context, sort } = params;
193534
194582
  // 构建查询语句
193535
- let queryString = `SCF_FunctionName:${AgentId}`;
194583
+ // 采用双引号短语检索保证精确匹配(AgentId/RequestId 含 '-' 会被分词,
194584
+ // 不加引号会因分词命中相似 ID 的其它日志);并对入参做转义,避免检索语法被注入。
194585
+ // CLS 双引号短语检索下需转义 '"'、'*' 与 '\\'。
194586
+ const escapeClsPhrase = (value) => value.replace(/["*\\]/g, '\\$&');
194587
+ let queryString = `SCF_FunctionName:"${escapeClsPhrase(AgentId)}"`;
193536
194588
  if (requestId) {
193537
- queryString += ` AND SCF_RequestId:${requestId}`;
194589
+ queryString += ` AND SCF_RequestId:"${escapeClsPhrase(requestId)}"`;
193538
194590
  }
193539
194591
  // 辅助函数:格式化时间
193540
194592
  const formatDateTime = (date) => {
@@ -193548,7 +194600,9 @@ class AgentService {
193548
194600
  StartTime: startTime || defaultStartTime,
193549
194601
  EndTime: endTime || defaultEndTime,
193550
194602
  queryString: queryString,
193551
- Limit: limit > 100 ? 100 : limit
194603
+ Limit: limit > 100 ? 100 : limit,
194604
+ Context: context,
194605
+ Sort: sort
193552
194606
  });
193553
194607
  }
193554
194608
  else {
@@ -197331,6 +198385,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
197331
198385
  Object.defineProperty(exports, "__esModule", ({ value: true }));
197332
198386
  exports.AccessService = void 0;
197333
198387
  const utils_1 = __webpack_require__(62358);
198388
+ /**
198389
+ * HTTP Service(CloudBaseGW)访问服务。
198390
+ * 注意:HTTP Service 模块自 v5.0.0 起已废弃,建议迁移到 EnvService 的域名与路由能力
198391
+ * (如 createHttpServiceRoute / modifyHttpServiceRoute / deleteHttpServiceRoute)。
198392
+ */
197334
198393
  class AccessService {
197335
198394
  constructor(environment) {
197336
198395
  this.environment = environment;
@@ -198768,7 +199827,7 @@ ${envIdSection}
198768
199827
  ## 环境信息
198769
199828
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
198770
199829
  - Node.js版本: ${process.version}
198771
- - MCP 版本:${process.env.npm_package_version || "2.25.4" || 0}
199830
+ - MCP 版本:${process.env.npm_package_version || "2.25.6" || 0}
198772
199831
  - 系统架构: ${os_1.default.arch()}
198773
199832
  - 时间: ${new Date().toISOString()}
198774
199833
  - 请求ID: ${requestId}
@@ -198849,7 +199908,7 @@ function createWrappedHandler(name, handler, server) {
198849
199908
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
198850
199909
  ide: server.ide || process.env.INTEGRATION_IDE || ''
198851
199910
  });
198852
- const mcpVersion = true ? "2.25.4" : 0;
199911
+ const mcpVersion = true ? "2.25.6" : 0;
198853
199912
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
198854
199913
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
198855
199914
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -214269,7 +215328,7 @@ function buildWriteVerificationHint(resourceId) {
214269
215328
  return `对于 ${resourceId} 这类有后端权限控制的集合,前端调用 .doc(id).update() / .doc(id).remove() 后,不能只看是否没有抛异常。请显式检查返回结果中的 updated / deleted 是否大于 0;如果 result.code、result.message 存在,或 updated / deleted 为 0,要把它当作真实失败并向上抛错。`;
214270
215329
  }
214271
215330
  function buildPermissionPropagationHint(resourceId) {
214272
- return `刚更新完 ${resourceId} 的安全规则时,后端权限可能需要一小段传播时间。若紧接着的真实写操作仍返回 DATABASE_PERMISSION_DENIED,请先等待一小段时间,再用同一登录态重试同一条 .doc(id).update() / .doc(id).remove();不要立刻连续重写规则,也不要在传播窗口里把旧拒绝直接当成规则表达式仍然错误。`;
215331
+ return `刚更新完 ${resourceId} 的安全规则时,后端权限通常在数秒到约 30 秒内生效。若紧接着的真实写操作仍返回 DATABASE_PERMISSION_DENIED,请先间隔数秒用同一登录态重试同一条 .doc(id).update() / .doc(id).remove();不要盲等数分钟,也不要立刻连续重写规则,更不要在短暂传播窗口里把旧拒绝直接当成规则表达式仍然错误。`;
214273
215332
  }
214274
215333
  function buildEnvelope(data, message) {
214275
215334
  return {
@@ -214294,6 +215353,171 @@ function mapResourceType(resourceType) {
214294
215353
  };
214295
215354
  return resourceTypeMap[resourceType];
214296
215355
  }
215356
+ /**
215357
+ * Platform ModifyResourcePermission / DescribeResourcePermission reject PG envs.
215358
+ * CLI migrated `tcb permission` → `tcb policy` (OPA Rego via
215359
+ * permission.modifyEnvAuthzConfig / describeEnvAuthzConfig).
215360
+ */
215361
+ function isPostgresqlPermissionApiUnsupported(error) {
215362
+ const message = error instanceof Error ? error.message : String(error);
215363
+ return /does not support PostgreSQL type environments/i.test(message);
215364
+ }
215365
+ const AUTHZ_USER_REGO_KEY = "authz.user.rego";
215366
+ function looksLikeUserRego(value) {
215367
+ return /^\s*package\s+authz\.user\b/m.test(value);
215368
+ }
215369
+ /** Detect legacy function securityRule shapes that mean "public invoke". */
215370
+ function isPublicFunctionInvokeRule(securityRule) {
215371
+ if (!securityRule || securityRule.trim() === "") {
215372
+ return false;
215373
+ }
215374
+ const trimmed = securityRule.trim();
215375
+ if (trimmed === "true") {
215376
+ return true;
215377
+ }
215378
+ try {
215379
+ const parsed = JSON.parse(trimmed);
215380
+ if (parsed === true || parsed === "true") {
215381
+ return true;
215382
+ }
215383
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
215384
+ return false;
215385
+ }
215386
+ const obj = parsed;
215387
+ if (obj.invoke === true || obj.invoke === "true") {
215388
+ return true;
215389
+ }
215390
+ for (const value of Object.values(obj)) {
215391
+ if (value &&
215392
+ typeof value === "object" &&
215393
+ !Array.isArray(value) &&
215394
+ (value.invoke === true ||
215395
+ value.invoke === "true")) {
215396
+ return true;
215397
+ }
215398
+ }
215399
+ }
215400
+ catch {
215401
+ return false;
215402
+ }
215403
+ return false;
215404
+ }
215405
+ /**
215406
+ * Build a CLI-aligned user Rego that opens cloud-function access for
215407
+ * anonymous + unauthenticated callers (tcb policy set example + HTTP no-token).
215408
+ */
215409
+ function buildPublicFunctionsUserRego(resourceId) {
215410
+ const comment = resourceId && resourceId !== "*"
215411
+ ? `# Public HTTP/API access for function ${resourceId} (aligned with tcb policy set)`
215412
+ : `# Public HTTP/API access for cloud functions (aligned with tcb policy set)`;
215413
+ return [
215414
+ "package authz.user",
215415
+ "",
215416
+ "default allow := false",
215417
+ "",
215418
+ comment,
215419
+ "allow if {",
215420
+ ' input.cloudbase.resource_type == "functions"',
215421
+ ' input.subject.auth_type in {"anonymous", "unauthenticated"}',
215422
+ "}",
215423
+ "",
215424
+ ].join("\n");
215425
+ }
215426
+ function resolveFunctionAuthzRegoInput(securityRule, resourceId) {
215427
+ if (securityRule && looksLikeUserRego(securityRule)) {
215428
+ return securityRule;
215429
+ }
215430
+ if (isPublicFunctionInvokeRule(securityRule)) {
215431
+ return buildPublicFunctionsUserRego(resourceId);
215432
+ }
215433
+ throw new Error(`PostgreSQL environments manage HTTP/function gateway auth via OPA Rego ` +
215434
+ `(same as CLI \`tcb policy set\`), not ModifyResourcePermission / function security-rule JSON. ` +
215435
+ `Pass either:\n` +
215436
+ `1) permission="CUSTOM" with securityRule as a full Rego document starting with \`package authz.user\`, or\n` +
215437
+ `2) permission="CUSTOM" with securityRule='{"invoke":true}' to generate a public-functions allow policy.\n` +
215438
+ `See https://docs.cloudbase.net/cli-v1/policy/management`);
215439
+ }
215440
+ async function describeEnvAuthzUserRego(cloudbase) {
215441
+ if (!cloudbase?.permission?.describeEnvAuthzConfig) {
215442
+ throw new Error("Current @cloudbase/manager-node does not expose permission.describeEnvAuthzConfig. Upgrade manager-node (>= 5.5.5) to align with CLI tcb policy get.");
215443
+ }
215444
+ const result = await cloudbase.permission.describeEnvAuthzConfig({
215445
+ key: AUTHZ_USER_REGO_KEY,
215446
+ });
215447
+ const value = typeof result?.Item?.Value === "string"
215448
+ ? result.Item.Value
215449
+ : typeof result?.Value === "string"
215450
+ ? result.Value
215451
+ : "";
215452
+ return { value, raw: result };
215453
+ }
215454
+ async function modifyEnvAuthzUserRego(cloudbase, value) {
215455
+ if (!cloudbase?.permission?.modifyEnvAuthzConfig) {
215456
+ throw new Error("Current @cloudbase/manager-node does not expose permission.modifyEnvAuthzConfig. Upgrade manager-node (>= 5.5.5) to align with CLI tcb policy set.");
215457
+ }
215458
+ return cloudbase.permission.modifyEnvAuthzConfig({
215459
+ key: AUTHZ_USER_REGO_KEY,
215460
+ value,
215461
+ });
215462
+ }
215463
+ async function describeResourcePermissionWithFunctionPgFallback(options) {
215464
+ const { cloudbase, envId, resourceType, resources } = options;
215465
+ try {
215466
+ const result = await cloudbase.permission.describeResourcePermission({
215467
+ resourceType: mapResourceType(resourceType),
215468
+ resources,
215469
+ });
215470
+ return result;
215471
+ }
215472
+ catch (error) {
215473
+ if (resourceType !== "function" || !isPostgresqlPermissionApiUnsupported(error)) {
215474
+ throw error;
215475
+ }
215476
+ const fallback = await describeEnvAuthzUserRego(cloudbase);
215477
+ const targets = resources && resources.length > 0 ? resources : ["*"];
215478
+ const permissionList = targets.map((resourceId) => ({
215479
+ ResourceType: "function",
215480
+ Resource: resourceId,
215481
+ Permission: "CUSTOM",
215482
+ SecurityRule: fallback.value || "",
215483
+ }));
215484
+ return {
215485
+ Data: {
215486
+ TotalCount: permissionList.length,
215487
+ PermissionList: permissionList,
215488
+ },
215489
+ RequestId: fallback.raw?.RequestId,
215490
+ fallback: "describeEnvAuthzConfig",
215491
+ raw: fallback.raw,
215492
+ };
215493
+ }
215494
+ }
215495
+ async function modifyFunctionPermissionWithPgFallback(options) {
215496
+ const { cloudbase, resourceId, permission, securityRule } = options;
215497
+ try {
215498
+ const result = await cloudbase.permission.modifyResourcePermission({
215499
+ resourceType: "function",
215500
+ resource: resourceId,
215501
+ permission,
215502
+ securityRule,
215503
+ });
215504
+ return { result };
215505
+ }
215506
+ catch (error) {
215507
+ if (!isPostgresqlPermissionApiUnsupported(error)) {
215508
+ throw error;
215509
+ }
215510
+ if (permission !== "CUSTOM") {
215511
+ throw new Error(`PostgreSQL environments do not support ModifyResourcePermission. ` +
215512
+ `Align with CLI \`tcb policy set\`: use permission="CUSTOM" and pass OPA Rego ` +
215513
+ `(package authz.user) or securityRule='{"invoke":true}' for public functions. ` +
215514
+ `Underlying error: ${error instanceof Error ? error.message : String(error)}`);
215515
+ }
215516
+ const rego = resolveFunctionAuthzRegoInput(securityRule, resourceId);
215517
+ const result = await modifyEnvAuthzUserRego(cloudbase, rego);
215518
+ return { result, fallback: "modifyEnvAuthzConfig", rego };
215519
+ }
215520
+ }
214297
215521
  function normalizeRecordArray(value, label) {
214298
215522
  if (value === undefined) {
214299
215523
  return undefined;
@@ -214469,7 +215693,7 @@ function registerPermissionTools(server) {
214469
215693
  };
214470
215694
  server.registerTool?.("queryPermissions", {
214471
215695
  title: "查询 CloudBase 权限与用户配置",
214472
- description: "查询 CloudBase 权限与用户配置,支持查询资源权限(数据库/云函数/存储桶等)、角色列表/详情、应用用户列表/详情。\n\n示例:\n- 查询存储桶权限:`action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\"`\n\n📌 跨后端边界提示:调用前先用 `envQuery(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`。`resourceType=\"noSqlDatabase\"` 查询的是 CloudBase NoSQL 集合规则,与 CloudBase PostgreSQL(PG)表的行级安全(RLS)是两套独立机制——同一个 PG 环境里 NoSQL 集合若仍在使用,对那些集合查询本工具结果**仍然有效**。要查 PG 表 RLS,请改用 `queryPgDatabase(action=\"sql\", sql=\"SELECT * FROM pg_policies WHERE tablename=...\")`。本工具不涉及 MySQL 权限。",
215696
+ description: "查询 CloudBase 权限与用户配置,支持查询资源权限(数据库/云函数/存储桶等)、角色列表/详情、应用用户列表/详情。\n\n示例:\n- 查询存储桶权限:`action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\"`\n\n📌 跨后端边界提示:调用前先用 `envQuery(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`。`resourceType=\"noSqlDatabase\"` 查询的是 CloudBase NoSQL 集合规则,与 CloudBase PostgreSQL(PG)表的行级安全(RLS)是两套独立机制——同一个 PG 环境里 NoSQL 集合若仍在使用,对那些集合查询本工具结果**仍然有效**。要查 PG 表 RLS,请改用 `queryPgDatabase(action=\"sql\", sql=\"SELECT * FROM pg_policies WHERE tablename=...\")`。本工具不涉及 MySQL 权限。\n\n⚠️ PostgreSQL 环境:平台 `DescribeResourcePermission` 对 PG 环境会直接拒绝。当 `resourceType=\"function\"` 时,本工具会自动回退到 Manager SDK `describeEnvAuthzConfig`(与 CLI `tcb policy get` 一致,读取 `authz.user.rego`)。",
214473
215697
  inputSchema: {
214474
215698
  action: zod_1.z.enum(QUERY_PERMISSION_ACTIONS),
214475
215699
  resourceType: zod_1.z
@@ -214501,8 +215725,10 @@ function registerPermissionTools(server) {
214501
215725
  if (resourceType === "storage") {
214502
215726
  await ensureStorageBucketsExist(cloudbase, [resourceId]);
214503
215727
  }
214504
- const result = await cloudbase.permission.describeResourcePermission({
214505
- resourceType: mapResourceType(resourceType),
215728
+ const result = await describeResourcePermissionWithFunctionPgFallback({
215729
+ cloudbase,
215730
+ envId,
215731
+ resourceType,
214506
215732
  resources: [resourceId],
214507
215733
  });
214508
215734
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
@@ -214518,8 +215744,11 @@ function registerPermissionTools(server) {
214518
215744
  aclTag: matchedPermission?.Permission,
214519
215745
  permissions,
214520
215746
  hints,
214521
- raw: result,
214522
- }, "资源权限查询成功");
215747
+ ...(result.fallback ? { fallback: result.fallback } : {}),
215748
+ raw: result.raw ?? result,
215749
+ }, result.fallback
215750
+ ? "资源权限查询成功(PostgreSQL 环境已回退到 describeEnvAuthzConfig / tcb policy get)"
215751
+ : "资源权限查询成功");
214523
215752
  }
214524
215753
  case "listResourcePermissions": {
214525
215754
  if (!resourceType) {
@@ -214528,8 +215757,10 @@ function registerPermissionTools(server) {
214528
215757
  if (resourceType === "storage" && resourceIds?.length) {
214529
215758
  await ensureStorageBucketsExist(cloudbase, resourceIds);
214530
215759
  }
214531
- const result = await cloudbase.permission.describeResourcePermission({
214532
- resourceType: mapResourceType(resourceType),
215760
+ const result = await describeResourcePermissionWithFunctionPgFallback({
215761
+ cloudbase,
215762
+ envId,
215763
+ resourceType,
214533
215764
  resources: resourceIds,
214534
215765
  });
214535
215766
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
@@ -214550,8 +215781,11 @@ function registerPermissionTools(server) {
214550
215781
  permissions,
214551
215782
  resourceHints,
214552
215783
  total: result.Data.TotalCount ?? 0,
214553
- raw: result,
214554
- }, "资源权限列表查询成功");
215784
+ ...(result.fallback ? { fallback: result.fallback } : {}),
215785
+ raw: result.raw ?? result,
215786
+ }, result.fallback
215787
+ ? "资源权限列表查询成功(PostgreSQL 环境已回退到 describeEnvAuthzConfig / tcb policy get)"
215788
+ : "资源权限列表查询成功");
214555
215789
  }
214556
215790
  case "listRoles": {
214557
215791
  const result = await cloudbase.permission.describeRoleList({
@@ -214630,7 +215864,7 @@ function registerPermissionTools(server) {
214630
215864
  }));
214631
215865
  server.registerTool?.("managePermissions", {
214632
215866
  title: "管理 CloudBase 权限与用户配置",
214633
- description: "管理 CloudBase 权限与用户配置,支持修改资源权限(数据库/云函数/存储桶等)、角色管理、成员与策略增删、应用用户 CRUD。\n\n示例:\n- 设置存储桶为私有:`action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"PRIVATE\"`\n- 创建角色:`action=\"createRole\", roleName=\"admin\", roleIdentity=\"admin\"`\n\n注意:`createUser` / `updateUser` 是环境侧应用用户管理能力,适合测试账号、管理员或预置用户,不应替代浏览器里的 Web SDK 注册表单;前端用户名密码注册应使用 `auth.signUp({ username, password })`,登录应使用 `auth.signInWithPassword({ username, password })`。直接在浏览器里用 `auth.signUp` 创建用户名密码用户取决于 SDK/provider 支持,使用前必须验证;不支持时应走后端或管理端边界,不能在浏览器暴露密钥。`securityRule` 的详细语义取决于 `resourceType`:`doc._openid`、`auth.openid`、查询条件子集校验,以及 `create` / `update` / `delete` JSON 模板仅适用于 `resourceType=\"noSqlDatabase\"` 的文档数据库安全规则;配置 `function` 或 `storage` 时,请参考各自官方安全规则文档,而不是复用 NoSQL 模板。\n\n📌 跨后端边界提示:调用前先用 `envQuery(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`:\n- `resourceType=\"noSqlDatabase\"` 仅作用于 CloudBase NoSQL 文档数据库的集合;CloudBase PostgreSQL(PG)表的行级权限**不**受它控制——PG 表请改用 RLS:`managePgDatabase(action=\"execute\", confirm=true)` 跑 `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` 与 `CREATE POLICY ...`。同一个 PG 环境里如果还有 NoSQL 集合在用,对那些**集合**继续使用 `noSqlDatabase` 规则是正确的——不是\"PG 环境就禁用本工具\"。\n- `resourceType=\"storage\"` 控制的是 NoSQL/COS 存储桶 ACL;PG 的 `pgstore` bucket 不在此 `resourceType` 覆盖范围内。\n- 本工具不涉及 MySQL;MySQL 数据库权限请走 MySQL 自身的 GRANT/REVOKE 语句(通过 `manageMysqlDatabase`)。",
215867
+ description: "管理 CloudBase 权限与用户配置,支持修改资源权限(数据库/云函数/存储桶等)、角色管理、成员与策略增删、应用用户 CRUD。\n\n示例:\n- 设置存储桶为私有:`action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"PRIVATE\"`\n- 创建角色:`action=\"createRole\", roleName=\"admin\", roleIdentity=\"admin\"`\n- 放开云函数匿名/未登录访问(PG 会走 OPA,对齐 CLI `tcb policy set`):`action=\"updateResourcePermission\", resourceType=\"function\", resourceId=\"myFn\", permission=\"CUSTOM\", securityRule='{\"invoke\":true}'`\n\n注意:`createUser` / `updateUser` 是环境侧应用用户管理能力,适合测试账号、管理员或预置用户,不应替代浏览器里的 Web SDK 注册表单;前端用户名密码注册应使用 `auth.signUp({ username, password })`,登录应使用 `auth.signInWithPassword({ username, password })`。直接在浏览器里用 `auth.signUp` 创建用户名密码用户取决于 SDK/provider 支持,使用前必须验证;不支持时应走后端或管理端边界,不能在浏览器暴露密钥。`securityRule` 的详细语义取决于 `resourceType`:`doc._openid`、`auth.openid`、查询条件子集校验,以及 `create` / `update` / `delete` JSON 模板仅适用于 `resourceType=\"noSqlDatabase\"` 的文档数据库安全规则;配置 `function` 或 `storage` 时,请参考各自官方安全规则文档,而不是复用 NoSQL 模板。\n\n📌 跨后端边界提示:调用前先用 `envQuery(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`:\n- `resourceType=\"noSqlDatabase\"` 仅作用于 CloudBase NoSQL 文档数据库的集合;CloudBase PostgreSQL(PG)表的行级权限**不**受它控制——PG 表请改用 RLS:`managePgDatabase(action=\"execute\", confirm=true)` 跑 `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` 与 `CREATE POLICY ...`。同一个 PG 环境里如果还有 NoSQL 集合在用,对那些**集合**继续使用 `noSqlDatabase` 规则是正确的——不是\"PG 环境就禁用本工具\"。\n- `resourceType=\"storage\"` 控制的是 NoSQL/COS 存储桶 ACL;PG 的 `pgstore` bucket 不在此 `resourceType` 覆盖范围内。\n- 本工具不涉及 MySQL;MySQL 数据库权限请走 MySQL 自身的 GRANT/REVOKE 语句(通过 `manageMysqlDatabase`)。\n\n⚠️ PostgreSQL 环境:平台 `ModifyResourcePermission` 对 PG 环境会直接拒绝。当 `resourceType=\"function\"` 时,本工具会自动回退到 Manager SDK `modifyEnvAuthzConfig`(与 CLI `tcb policy set` 一致,写入 `authz.user.rego`)。`securityRule` 可传完整 Rego(`package authz.user`)或 `'{\"invoke\":true}'`(自动生成放通 anonymous/unauthenticated 调 functions 的策略)。设置 Rego 后旧网关鉴权会失效,行为与 CLI 相同。",
214634
215868
  inputSchema: {
214635
215869
  action: zod_1.z.enum(MANAGE_PERMISSION_ACTIONS),
214636
215870
  resourceType: zod_1.z
@@ -214678,12 +215912,29 @@ function registerPermissionTools(server) {
214678
215912
  if (!resourceType || !resourceId || !permission) {
214679
215913
  throw new Error("action=updateResourcePermission 时必须提供 resourceType、resourceId 和 permission");
214680
215914
  }
214681
- const result = await cloudbase.permission.modifyResourcePermission({
214682
- resourceType: mapResourceType(resourceType),
214683
- resource: resourceId,
214684
- permission,
214685
- securityRule,
214686
- });
215915
+ let result;
215916
+ let fallback;
215917
+ let appliedRego;
215918
+ if (resourceType === "function") {
215919
+ const updated = await modifyFunctionPermissionWithPgFallback({
215920
+ cloudbase,
215921
+ envId,
215922
+ resourceId,
215923
+ permission,
215924
+ securityRule,
215925
+ });
215926
+ result = updated.result;
215927
+ fallback = updated.fallback;
215928
+ appliedRego = updated.rego;
215929
+ }
215930
+ else {
215931
+ result = await cloudbase.permission.modifyResourcePermission({
215932
+ resourceType: mapResourceType(resourceType),
215933
+ resource: resourceId,
215934
+ permission,
215935
+ securityRule,
215936
+ });
215937
+ }
214687
215938
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
214688
215939
  const hints = permission === "CUSTOM" ? buildPermissionHints(securityRule, resourceId) : [];
214689
215940
  return buildEnvelope({
@@ -214693,6 +215944,8 @@ function registerPermissionTools(server) {
214693
215944
  resourceId,
214694
215945
  permission,
214695
215946
  hints,
215947
+ ...(fallback ? { fallback } : {}),
215948
+ ...(appliedRego ? { rego: appliedRego } : {}),
214696
215949
  verificationHint: resourceType === "noSqlDatabase" && permission === "CUSTOM"
214697
215950
  ? buildWriteVerificationHint(resourceId)
214698
215951
  : undefined,
@@ -214700,7 +215953,9 @@ function registerPermissionTools(server) {
214700
215953
  ? buildPermissionPropagationHint(resourceId)
214701
215954
  : undefined,
214702
215955
  raw: result,
214703
- }, "资源权限更新成功");
215956
+ }, fallback
215957
+ ? "资源权限更新成功(PostgreSQL 环境已回退到 modifyEnvAuthzConfig / tcb policy set)"
215958
+ : "资源权限更新成功");
214704
215959
  }
214705
215960
  case "createRole": {
214706
215961
  if (!roleName || !roleIdentity) {
@@ -215437,7 +216692,7 @@ function registerSetupTools(server) {
215437
216692
  server.registerTool("downloadTemplate", {
215438
216693
  title: "下载项目模板",
215439
216694
  description: `自动下载并部署CloudBase项目模板。
215440
- **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.4" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
216695
+ **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.6" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
215441
216696
  inputSchema: {
215442
216697
  template: zod_1.z
215443
216698
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -232661,7 +233916,11 @@ function registerGatewayTools(server) {
232661
233916
  return result ?? {};
232662
233917
  };
232663
233918
  const buildPrivilegeDescription = (privilege) => {
232664
- const serviceStatus = privilege.EnableService === true ? "已开启" : "未开启";
233919
+ const serviceStatus = privilege.EnableService === undefined
233920
+ ? "未知"
233921
+ : privilege.EnableService === true
233922
+ ? "已开启"
233923
+ : "未开启";
232665
233924
  const authStatus = privilege.EnableAuth === undefined
232666
233925
  ? "未知"
232667
233926
  : privilege.EnableAuth === true
@@ -232753,7 +234012,7 @@ function registerGatewayTools(server) {
232753
234012
  {
232754
234013
  tool: "queryGateway",
232755
234014
  action: "getRoute",
232756
- reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
234015
+ reason: "创建后通常数秒到约 30 秒内生效;请立刻轮询 getRoute 或探测 accessUrl,勿盲等 60 秒以上",
232757
234016
  },
232758
234017
  {
232759
234018
  tool: "queryPermissions",
@@ -232901,7 +234160,7 @@ function registerGatewayTools(server) {
232901
234160
  const privilege = await getGatewayPrivilege();
232902
234161
  if (privilege.EnableService !== true) {
232903
234162
  privilegeHint =
232904
- "⚠️ HTTP 网关总开关未开启,路由创建成功后访问仍将返回 HTTPSERVICE_NONACTIVATED(403);请先调用 manageGateway(action=\"enableService\", enable=true) 开启,再等待 30 秒到 3 分钟访问。";
234163
+ "⚠️ HTTP 网关总开关未开启,路由创建成功后访问仍将返回 HTTPSERVICE_NONACTIVATED(403);请先调用 manageGateway(action=\"enableService\", enable=true) 开启,再立刻探测 accessUrl(通常数秒到约 30 秒内生效,勿盲等 60 秒以上)。";
232905
234164
  privilegeNextActions = [
232906
234165
  {
232907
234166
  tool: "manageGateway",
@@ -232937,7 +234196,7 @@ function registerGatewayTools(server) {
232937
234196
  : payload.resolved.enablePathTransmission === false
232938
234197
  ? ";路径透传关闭(网关会剥掉触发路径前缀后再转发给后端)"
232939
234198
  : ";路径透传未显式设置(平台默认 false,会剥掉触发路径前缀)") +
232940
- `。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。该操作只创建网关入口,不会自动放开上游权限;若上游是云函数且需要匿名或浏览器直接访问,请继续检查函数资源权限。` +
234199
+ `。注意:路由配置传播通常数秒到约 30 秒;请立刻用 queryGateway(getRoute) 或探测 accessUrl 确认,勿盲等 60 秒以上。该操作只创建网关入口,不会自动放开上游权限;若上游是云函数且需要匿名或浏览器直接访问,请继续检查函数资源权限。` +
232941
234200
  (privilegeHint ? ` ${privilegeHint}` : ""), [
232942
234201
  ...privilegeNextActions,
232943
234202
  ...(routeMutationNextActions(payload.resolved.upstreamResourceName) ??
@@ -233001,12 +234260,28 @@ function registerGatewayTools(server) {
233001
234260
  if (!input.domain || !input.certificateId) {
233002
234261
  throw new Error("action=bindCustomDomain 时必须提供 domain 和 certificateId");
233003
234262
  }
234263
+ const accessType = input.accessType ?? "DIRECT";
234264
+ if (accessType === "CUSTOM" && !input.customCname) {
234265
+ throw new Error("action=bindCustomDomain 且 accessType=CUSTOM 时必须提供 customCname(自有 CDN/WAF 的回源/回填地址,不是 DNS 解析目标)。" +
234266
+ "说明:https://docs.cloudbase.net/service/custom-domain");
234267
+ }
234268
+ if (accessType !== "CUSTOM" && input.customCname) {
234269
+ throw new Error("customCname 仅在 accessType=CUSTOM 时可用;普通绑定用默认 DIRECT,不要传 customCname。" +
234270
+ "说明:https://docs.cloudbase.net/service/custom-domain");
234271
+ }
233004
234272
  const cloudbase = await getManager();
233005
234273
  const result = await cloudbase.env.bindCustomDomain({
233006
234274
  EnvId: await resolveEnvId(),
233007
234275
  Domain: {
233008
234276
  Domain: input.domain,
233009
234277
  CertId: input.certificateId,
234278
+ AccessType: accessType,
234279
+ ...(input.enable !== undefined
234280
+ ? { Enable: input.enable }
234281
+ : {}),
234282
+ ...(input.customCname
234283
+ ? { CustomCname: input.customCname }
234284
+ : {}),
233010
234285
  },
233011
234286
  });
233012
234287
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
@@ -233014,24 +234289,64 @@ function registerGatewayTools(server) {
233014
234289
  action: input.action,
233015
234290
  domain: input.domain,
233016
234291
  certificateId: input.certificateId,
234292
+ accessType,
234293
+ ...(input.customCname
234294
+ ? { customCname: input.customCname }
234295
+ : {}),
234296
+ ...(input.enable !== undefined
234297
+ ? { enable: input.enable }
234298
+ : {}),
233017
234299
  raw: result,
233018
- }, "自定义域名绑定成功");
234300
+ }, `自定义域名绑定成功(${accessType})`, [
234301
+ {
234302
+ tool: "manageGateway",
234303
+ action: "createRoute",
234304
+ reason: "绑定后需 createRoute 添加访问路径,并完成 DNS CNAME 解析后才可访问",
234305
+ },
234306
+ ]);
233019
234307
  }
233020
234308
  case "deleteCustomDomain": {
233021
234309
  if (!input.domain) {
233022
234310
  throw new Error("action=deleteCustomDomain 时必须提供 domain");
233023
234311
  }
233024
234312
  const cloudbase = await getManager();
233025
- const result = await cloudbase.env.deleteCustomDomain({
233026
- EnvId: await resolveEnvId(),
233027
- Domain: input.domain,
233028
- });
233029
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
233030
- return buildEnvelope({
233031
- action: input.action,
233032
- domain: input.domain,
233033
- raw: result,
233034
- }, "自定义域名删除成功");
234313
+ try {
234314
+ const result = await cloudbase.env.deleteCustomDomain({
234315
+ EnvId: await resolveEnvId(),
234316
+ Domain: input.domain,
234317
+ });
234318
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
234319
+ return buildEnvelope({
234320
+ action: input.action,
234321
+ domain: input.domain,
234322
+ raw: result,
234323
+ }, "自定义域名删除成功");
234324
+ }
234325
+ catch (error) {
234326
+ const message = error instanceof Error ? error.message : String(error);
234327
+ // SDK 在域名下仍有路由绑定时抛错,需先删路由再删域名
234328
+ if (/route binding/i.test(message)) {
234329
+ return {
234330
+ success: false,
234331
+ data: { action: input.action, domain: input.domain },
234332
+ message: `域名 ${input.domain} 下仍有路由绑定,需先删除路由再删除域名。` +
234333
+ `原始错误:${message}`,
234334
+ nextActions: [
234335
+ {
234336
+ tool: "queryGateway",
234337
+ action: "listRoutes",
234338
+ reason: "查看该域名下的路由,确认需要删除的路径",
234339
+ },
234340
+ {
234341
+ tool: "manageGateway",
234342
+ action: "deleteRoute",
234343
+ reason: "先删除该域名下的全部路由(逐个 deleteRoute),再重试 deleteCustomDomain",
234344
+ },
234345
+ ],
234346
+ };
234347
+ }
234348
+ throw error;
234349
+ }
233035
234350
  }
233036
234351
  case "enableService": {
233037
234352
  if (typeof input.enable !== "boolean") {
@@ -233144,7 +234459,8 @@ function registerGatewayTools(server) {
233144
234459
  "enableService/authSwitch 开关 HTTP 网关总开关与访问鉴权(需配合 enable 参数)。" +
233145
234460
  "createRoute/updateRoute 必须提供 upstreamResourceType。" +
233146
234461
  "已有自定义域名时优先 createRoute(domain=已有域名) 实现访问,不必再次 bindCustomDomain / 传入 certificateId;" +
233147
- "bindCustomDomain 仅用于首次绑定新域名。"),
234462
+ "bindCustomDomain 仅用于首次绑定新域名(需 certificateId;可选 accessType=DIRECT|CDN|CUSTOM,CUSTOM 需 customCname;普通场景用默认 DIRECT)。" +
234463
+ "接入说明:https://docs.cloudbase.net/service/custom-domain"),
233148
234464
  targetName: zod_1.z
233149
234465
  .string()
233150
234466
  .optional()
@@ -233207,11 +234523,24 @@ function registerGatewayTools(server) {
233207
234523
  .optional()
233208
234524
  .describe("证书 ID。仅首次 bindCustomDomain 必填。" +
233209
234525
  "在已有自定义域名上 createRoute / updateRoute / deleteRoute 不需要 certificateId。"),
234526
+ accessType: zod_1.z
234527
+ .enum(["DIRECT", "CDN", "CUSTOM"])
234528
+ .optional()
234529
+ .describe("绑定类型(仅 bindCustomDomain,默认 DIRECT)。" +
234530
+ "DIRECT=直连(普通绑域名用这个);CDN=云开发 CDN;CUSTOM=自有 CDN/WAF(需 customCname)。" +
234531
+ "详见 https://docs.cloudbase.net/service/custom-domain"),
234532
+ customCname: zod_1.z
234533
+ .string()
234534
+ .optional()
234535
+ .describe("自有 CDN/WAF 的回源/回填地址(仅 bindCustomDomain 且 accessType=CUSTOM)。" +
234536
+ "不是 DNS 里用户域名要解析到的那个 CNAME;DIRECT/CDN 不要传。" +
234537
+ "详见 https://docs.cloudbase.net/service/custom-domain"),
233210
234538
  enable: zod_1.z
233211
234539
  .boolean()
233212
234540
  .optional()
233213
- .describe("开关目标状态(仅 enableService / authSwitch 使用,必填):" +
233214
- "enable=true 开启,enable=false 关闭。省略或非布尔值会返回参数错误。"),
234541
+ .describe("开关目标状态(enableService / authSwitch 必填):enable=true 开启,enable=false 关闭。" +
234542
+ "bindCustomDomain 可选:enable=false 表示绑定后禁用域名(默认启用)。" +
234543
+ "省略或非布尔值会返回参数错误。"),
233215
234544
  },
233216
234545
  annotations: {
233217
234546
  readOnlyHint: false,
@@ -265478,6 +266807,10 @@ class CloudBase {
265478
266807
  get hosting() {
265479
266808
  return this.currentEnvironment().getHostingService();
265480
266809
  }
266810
+ /**
266811
+ * HTTP Service(CloudBaseGW)访问服务
266812
+ * @deprecated 自 v5.0.0 起 HTTP Service 模块已废弃,请改用 env 下的域名与路由相关 API。
266813
+ */
265481
266814
  get access() {
265482
266815
  return this.currentEnvironment().getAccessService();
265483
266816
  }
@@ -267590,48 +268923,27 @@ class FunctionService {
267590
268923
  return data;
267591
268924
  }
267592
268925
  /**
267593
- * 获取函数日志
267594
- * @deprecated 请使用 getFunctionLogsV2 代替
267595
- * @param {{
267596
- * name: string
267597
- * offset: number
267598
- * limit: number
267599
- * order: string
267600
- * orderBy: string
267601
- * startTime: string
267602
- * endTime: string
267603
- * requestId: string
267604
- * }} options
267605
- * @returns {Promise<IFunctionLogRes>}
267606
- */
267607
- async getFunctionLogs(options) {
267608
- let { name, offset = 0, limit = 10, order, orderBy, startTime, endTime, requestId } = options;
267609
- const { namespace } = this.getFunctionConfig();
267610
- if (startTime && !endTime) {
267611
- // 自动补 endTime 为 startTime + 1d
267612
- endTime = (0, utils_1.formatTimeByMs)(new Date(startTime).getTime() + 24 * 3600 * 1000);
267613
- }
267614
- if (endTime && !startTime) {
267615
- // 自动补 startTime 为 endTime - 1d
267616
- startTime = (0, utils_1.formatTimeByMs)(new Date(endTime).getTime() - 24 * 3600 * 1000);
267617
- }
267618
- const params = {
267619
- Namespace: namespace,
267620
- FunctionName: name,
267621
- Offset: offset,
267622
- Limit: limit,
267623
- Order: order,
267624
- OrderBy: orderBy,
267625
- StartTime: startTime,
267626
- EndTime: endTime,
267627
- FunctionRequestId: requestId
267628
- };
267629
- const res = await this.scfService.request('GetFunctionLogs', params);
267630
- return res;
267631
- }
267632
- /**
267633
- * 获取函数日志 ID 列表
268926
+ * 获取函数日志
268927
+ * @deprecated 底层 GetFunctionLogs 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志
267634
268928
  * @param {{
268929
+ * name: string
268930
+ * offset: number
268931
+ * limit: number
268932
+ * order: string
268933
+ * orderBy: string
268934
+ * startTime: string
268935
+ * endTime: string
268936
+ * requestId: string
268937
+ * }} options
268938
+ * @returns {Promise<IFunctionLogRes>}
268939
+ */
268940
+ async getFunctionLogs(_options) {
268941
+ throw new error_1.CloudBaseError('getFunctionLogs 已废弃:底层 GetFunctionLogs 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志');
268942
+ }
268943
+ /**
268944
+ * 获取函数日志 ID 列表
268945
+ * @deprecated 底层 GetFunctionLogs 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志
268946
+ * @param {{
267635
268947
  * name: string
267636
268948
  * offset: number
267637
268949
  * limit: number
@@ -267642,28 +268954,12 @@ class FunctionService {
267642
268954
  * }} options
267643
268955
  * @returns {Promise<IFunctionLogResV2>}
267644
268956
  */
267645
- async getFunctionLogsV2(options) {
267646
- let { name, offset = 0, limit = 10, startTime, endTime, requestId, qualifier } = options;
267647
- const { env } = this.getFunctionConfig();
267648
- ({ startTime, endTime } = (0, utils_1.getCompleteTimeRange)({
267649
- startTime,
267650
- endTime
267651
- }));
267652
- const params = {
267653
- EnvId: env,
267654
- FunctionName: name,
267655
- Offset: offset,
267656
- Limit: limit,
267657
- StartTime: startTime,
267658
- EndTime: endTime,
267659
- LogRequestId: requestId,
267660
- Qualifier: qualifier || "$LATEST" // 云函数版本,不传则用 $LATEST
267661
- };
267662
- const res = await this.tcbService.request('GetFunctionLogs', params);
267663
- return res;
268957
+ async getFunctionLogsV2(_options) {
268958
+ throw new error_1.CloudBaseError('getFunctionLogsV2 已废弃:底层 GetFunctionLogs 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志');
267664
268959
  }
267665
268960
  /**
267666
268961
  * 根据函数日志 ID 查询日志详情
268962
+ * @deprecated 底层 GetFunctionLogDetail 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志
267667
268963
  * @param {{
267668
268964
  * startTime: string
267669
268965
  * endTime: string
@@ -267671,46 +268967,18 @@ class FunctionService {
267671
268967
  * }} options
267672
268968
  * @returns {Promise<IFunctionLogDetailRes>}
267673
268969
  */
267674
- async getFunctionLogDetail(options) {
267675
- let { logRequestId, startTime, endTime } = options;
267676
- ({ startTime, endTime } = (0, utils_1.getCompleteTimeRange)({
267677
- startTime,
267678
- endTime
267679
- }));
267680
- const params = {
267681
- StartTime: startTime,
267682
- LogRequestId: logRequestId,
267683
- EndTime: endTime
267684
- };
267685
- const res = await this.tcbService.request('GetFunctionLogDetail', params);
267686
- return res;
268970
+ async getFunctionLogDetail(_options) {
268971
+ throw new error_1.CloudBaseError('getFunctionLogDetail 已废弃:底层 GetFunctionLogDetail 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志');
267687
268972
  }
267688
268973
  /**
267689
268974
  * 获取函数的完整调用日志
267690
268975
  * 该方法会自动完成两步操作:1. 获取日志请求ID列表。 2. 根据ID列表获取每条日志的详细内容。
268976
+ * @deprecated 底层 GetFunctionLogs/GetFunctionLogDetail 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志
267691
268977
  * @param {IFunctionLogOptionsV2} options - 查询选项
267692
268978
  * @returns {Promise<IFunctionLogDetailRes[]>} 返回包含完整日志详情的数组
267693
268979
  */
267694
- async getCompleteFunctionLogs(options) {
267695
- // 调用 getFunctionLogsV2 获取日志请求ID列表
267696
- const { name } = options;
267697
- const logs = await this.getFunctionLogsV2(options);
267698
- // 如果没有日志,直接返回空数组
267699
- if (logs.LogList.length === 0) {
267700
- return [];
267701
- }
267702
- const detailPromises = logs.LogList.map(async (log) => {
267703
- // 对每一个日志ID,调用 getFunctionLogDetail
267704
- const res = await this.getFunctionLogDetail({
267705
- logRequestId: log.RequestId,
267706
- startTime: options.startTime,
267707
- endTime: options.endTime
267708
- });
267709
- return Object.assign(Object.assign({}, res), { RetCode: log.RetCode, FunctionName: name });
267710
- });
267711
- // 并发执行所有详情查询,等待它们全部完成
267712
- const detailedLogs = await Promise.all(detailPromises);
267713
- return detailedLogs;
268980
+ async getCompleteFunctionLogs(_options) {
268981
+ throw new error_1.CloudBaseError('getCompleteFunctionLogs 已废弃:底层 GetFunctionLogs/GetFunctionLogDetail 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志');
267714
268982
  }
267715
268983
  /**
267716
268984
  * 更新云函数配置