@cloudbase/cloudbase-mcp 2.25.5 → 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/cli.cjs +3 -3
- package/dist/index.cjs +1169 -183
- package/dist/index.js +914 -69
- package/package.json +1 -1
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: '
|
|
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:
|
|
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: '
|
|
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:
|
|
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
|
|
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 (
|
|
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
|
|
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.
|
|
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
|
|
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;
|
|
@@ -162272,10 +162696,27 @@ const MIGRATION_VERIFY_DELAY_MS = 1500;
|
|
|
162272
162696
|
const MIGRATION_LIST_PAGE_SIZE = 100;
|
|
162273
162697
|
/**
|
|
162274
162698
|
* PushPGUserMigrations is async: poll DescribeTaskResult until Succeed/Failed.
|
|
162275
|
-
*
|
|
162699
|
+
* Default wait aligns with CLI `tcb db pg migration up` (10 minutes).
|
|
162700
|
+
* Override per call via applyMigration.taskPollTimeoutMs.
|
|
162276
162701
|
*/
|
|
162277
162702
|
const MIGRATION_TASK_POLL_INTERVAL_MS = 1500;
|
|
162278
|
-
|
|
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
|
+
}
|
|
162279
162720
|
/**
|
|
162280
162721
|
* Confirm a pushed migration version is present in the remote migration history.
|
|
162281
162722
|
*
|
|
@@ -162359,12 +162800,114 @@ async function listAllRemoteMigrationSummaries(context, cloudBaseOptions) {
|
|
|
162359
162800
|
}
|
|
162360
162801
|
return { summaries, latestVersion, total };
|
|
162361
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
|
+
}
|
|
162362
162854
|
/**
|
|
162363
|
-
*
|
|
162364
|
-
|
|
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).
|
|
162365
162886
|
*/
|
|
162366
162887
|
async function buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions) {
|
|
162367
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
|
+
}
|
|
162368
162911
|
const migrations = [];
|
|
162369
162912
|
for (const summary of summaries) {
|
|
162370
162913
|
if (summary.Version === pending.Version) {
|
|
@@ -162384,10 +162927,23 @@ async function buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBa
|
|
|
162384
162927
|
});
|
|
162385
162928
|
}
|
|
162386
162929
|
migrations.push(pending);
|
|
162387
|
-
return {
|
|
162930
|
+
return {
|
|
162931
|
+
migrations,
|
|
162932
|
+
remoteCount: summaries.length,
|
|
162933
|
+
latestVersion,
|
|
162934
|
+
payloadSource: "hydrate",
|
|
162935
|
+
};
|
|
162388
162936
|
}
|
|
162389
|
-
|
|
162390
|
-
|
|
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;
|
|
162391
162947
|
let last = { TaskId: taskId };
|
|
162392
162948
|
while (Date.now() <= deadline) {
|
|
162393
162949
|
last = (await callPgMigrationApi(context, "DescribeTaskResult", { TaskId: taskId }, cloudBaseOptions));
|
|
@@ -162397,13 +162953,21 @@ async function waitPgMigrationTask(context, taskId, cloudBaseOptions) {
|
|
|
162397
162953
|
}
|
|
162398
162954
|
await sleep(MIGRATION_TASK_POLL_INTERVAL_MS);
|
|
162399
162955
|
}
|
|
162400
|
-
const error = new Error(`DescribeTaskResult timed out after ${Math.floor(
|
|
162956
|
+
const error = new Error(`DescribeTaskResult timed out after ${Math.floor(maxWaitMs / 1000)}s ` +
|
|
162401
162957
|
`for TaskId=${taskId} (last Status=${last.Status || "-"}, Phase=${last.Phase || "-"}).`);
|
|
162402
162958
|
error.lastTask = last;
|
|
162403
162959
|
throw error;
|
|
162404
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
|
+
}
|
|
162405
162969
|
const MIGRATION_VERSION_REQUIRED_MESSAGE = "migrationVersion is required (14-digit UTC timestamp YYYYMMDDHHMMSS). " +
|
|
162406
|
-
"Decide the version first, write local file migrations/<version>_<migrationName>.sql, " +
|
|
162970
|
+
"Decide the version first, write local file cloudbase/migrations/<version>_<migrationName>.sql, " +
|
|
162407
162971
|
"then call planMigration/applyMigration with the same migrationVersion and migrationName.";
|
|
162408
162972
|
function requireExplicitMigrationVersion(args, actionLabel) {
|
|
162409
162973
|
const version = args.migrationVersion?.trim();
|
|
@@ -162740,6 +163304,30 @@ function createDefaultDependencies(server) {
|
|
|
162740
163304
|
createClient: async (context) => createManagerPgClient(context, server?.cloudBaseOptions),
|
|
162741
163305
|
};
|
|
162742
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
|
+
};
|
|
162743
163331
|
function isExecutePGSqlDatabase(value) {
|
|
162744
163332
|
return Boolean(value &&
|
|
162745
163333
|
typeof value === "object" &&
|
|
@@ -162780,13 +163368,22 @@ async function executeManagerPGSql(context, sql, cloudBaseOptions) {
|
|
|
162780
163368
|
}
|
|
162781
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.");
|
|
162782
163370
|
}
|
|
162783
|
-
/**
|
|
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
|
+
*/
|
|
162784
163376
|
async function callPgMigrationApi(context, action, params, cloudBaseOptions) {
|
|
162785
163377
|
const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({
|
|
162786
163378
|
cloudBaseOptions: cloudBaseOptions
|
|
162787
163379
|
? { ...cloudBaseOptions, envId: context.envId }
|
|
162788
163380
|
: { envId: context.envId },
|
|
162789
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
|
+
}
|
|
162790
163387
|
if (!isCloudBaseWithCommonService(manager)) {
|
|
162791
163388
|
throw new Error("Current @cloudbase/manager-node runtime does not support migration APIs. Upgrade to @cloudbase/manager-node >= 5.4.0.");
|
|
162792
163389
|
}
|
|
@@ -163027,11 +163624,11 @@ async function handleExecuteSql(args, context, deps) {
|
|
|
163027
163624
|
success: false,
|
|
163028
163625
|
errorCode: "DDL_USE_APPLY_MIGRATION",
|
|
163029
163626
|
message: "Schema DDL (CREATE/ALTER/DROP/TRUNCATE/...) must use applyMigration with an explicit migrationVersion, " +
|
|
163030
|
-
"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. " +
|
|
163031
163628
|
"Set allowDdlViaExecute=true only for exceptional one-off ops that intentionally bypass migration history.",
|
|
163032
163629
|
data: {
|
|
163033
163630
|
classification,
|
|
163034
|
-
localFileHintPattern: "migrations/<migrationVersion>_<migrationName>.sql",
|
|
163631
|
+
localFileHintPattern: "cloudbase/migrations/<migrationVersion>_<migrationName>.sql",
|
|
163035
163632
|
},
|
|
163036
163633
|
nextActions: [
|
|
163037
163634
|
buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Preview the schema change as a versioned migration (provide migrationVersion + migrationName).", {
|
|
@@ -163140,7 +163737,7 @@ async function handleDryRun(args) {
|
|
|
163140
163737
|
wouldExecute: false,
|
|
163141
163738
|
sqlPreview: args.sql.trim().slice(0, 500),
|
|
163142
163739
|
preferredAction: "applyMigration",
|
|
163143
|
-
localFileHintPattern: "migrations/<migrationVersion>_<migrationName>.sql",
|
|
163740
|
+
localFileHintPattern: "cloudbase/migrations/<migrationVersion>_<migrationName>.sql",
|
|
163144
163741
|
},
|
|
163145
163742
|
message: "SQL dry run completed. Schema DDL should use applyMigration with an explicit migrationVersion, not execute.",
|
|
163146
163743
|
nextActions: [
|
|
@@ -163198,10 +163795,13 @@ async function handlePlanMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163198
163795
|
}
|
|
163199
163796
|
try {
|
|
163200
163797
|
const hydrated = await buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions);
|
|
163798
|
+
const includeAll = Boolean(args.includeAll);
|
|
163201
163799
|
const result = await callPgMigrationApi(context, "PreviewPGUserMigrations", {
|
|
163202
163800
|
Migrations: hydrated.migrations,
|
|
163801
|
+
IncludeAll: includeAll,
|
|
163203
163802
|
}, cloudBaseOptions);
|
|
163204
163803
|
const executable = result.Executable === true;
|
|
163804
|
+
const payloadSourceLabel = formatMigrationsPayloadSourceLabel(hydrated.payloadSource, hydrated.remoteCount);
|
|
163205
163805
|
return buildPgToolResult({
|
|
163206
163806
|
success: true,
|
|
163207
163807
|
data: {
|
|
@@ -163210,22 +163810,25 @@ async function handlePlanMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163210
163810
|
localFileHint,
|
|
163211
163811
|
hydratedRemoteCount: hydrated.remoteCount,
|
|
163212
163812
|
latestRemoteVersion: hydrated.latestVersion || null,
|
|
163813
|
+
payloadSource: hydrated.payloadSource,
|
|
163814
|
+
includeAll,
|
|
163213
163815
|
executable,
|
|
163214
163816
|
apiResult: result,
|
|
163215
163817
|
},
|
|
163216
163818
|
message: executable
|
|
163217
|
-
? `Migration plan generated via PreviewPGUserMigrations (
|
|
163218
|
-
: `Migration plan is NOT executable (PreviewPGUserMigrations.Executable=false after
|
|
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.`,
|
|
163219
163821
|
nextActions: [
|
|
163220
163822
|
buildNextAction(MANAGE_PG_DATABASE, "applyMigration", executable
|
|
163221
163823
|
? "Review the plan above. If it looks correct, call applyMigration with the same migrationVersion."
|
|
163222
|
-
: "Resolve Conflicts (often pick a migrationVersion newer than LatestVersion), then retry planMigration/applyMigration.", {
|
|
163824
|
+
: "Resolve Conflicts (often pick a migrationVersion newer than LatestVersion, or set includeAll=true for intentional out-of-order), then retry planMigration/applyMigration.", {
|
|
163223
163825
|
action: "applyMigration",
|
|
163224
163826
|
migrationName: args.migrationName,
|
|
163225
163827
|
migrationVersion: version,
|
|
163226
163828
|
sql: args.sql,
|
|
163227
163829
|
confirm: true,
|
|
163228
163830
|
...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
|
|
163831
|
+
...(includeAll ? { includeAll: true } : {}),
|
|
163229
163832
|
}),
|
|
163230
163833
|
],
|
|
163231
163834
|
});
|
|
@@ -163266,6 +163869,33 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163266
163869
|
}
|
|
163267
163870
|
const version = versionOrError;
|
|
163268
163871
|
const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
|
|
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
|
+
}
|
|
163269
163899
|
const pending = {
|
|
163270
163900
|
Version: version,
|
|
163271
163901
|
Name: args.migrationName,
|
|
@@ -163274,12 +163904,14 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163274
163904
|
if (args.rollbackSql?.trim()) {
|
|
163275
163905
|
pending.Rollback = args.rollbackSql;
|
|
163276
163906
|
}
|
|
163907
|
+
const includeAll = Boolean(args.includeAll);
|
|
163277
163908
|
try {
|
|
163278
163909
|
// Pushing only the pending migration while remote history exists makes the plan
|
|
163279
|
-
// non-executable (remote_history_not_found_locally).
|
|
163280
|
-
//
|
|
163910
|
+
// non-executable (remote_history_not_found_locally). Prefer local tree when complete
|
|
163911
|
+
// (CLI parity); otherwise hydrate remote Queries (List + N×Describe).
|
|
163281
163912
|
const hydrated = await buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions);
|
|
163282
|
-
const
|
|
163913
|
+
const payloadSourceLabel = formatMigrationsPayloadSourceLabel(hydrated.payloadSource, hydrated.remoteCount);
|
|
163914
|
+
const preview = await callPgMigrationApi(context, "PreviewPGUserMigrations", { Migrations: hydrated.migrations, IncludeAll: includeAll }, cloudBaseOptions);
|
|
163283
163915
|
if (preview.Executable !== true) {
|
|
163284
163916
|
return buildPgToolResult({
|
|
163285
163917
|
success: false,
|
|
@@ -163290,27 +163922,33 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163290
163922
|
localFileHint,
|
|
163291
163923
|
hydratedRemoteCount: hydrated.remoteCount,
|
|
163292
163924
|
latestRemoteVersion: hydrated.latestVersion || null,
|
|
163925
|
+
payloadSource: hydrated.payloadSource,
|
|
163926
|
+
includeAll,
|
|
163293
163927
|
previewResult: preview,
|
|
163294
163928
|
verified: false,
|
|
163295
163929
|
},
|
|
163296
163930
|
message: `PreviewPGUserMigrations reported Executable=false for migrationVersion=${version} ` +
|
|
163297
|
-
`(latest remote=${hydrated.latestVersion || "unknown"},
|
|
163931
|
+
`(latest remote=${hydrated.latestVersion || "unknown"}, ${payloadSourceLabel}, includeAll=${includeAll}). ` +
|
|
163298
163932
|
"Push was NOT submitted. Inspect previewResult.Conflicts — common reasons: " +
|
|
163299
|
-
"local_migration_before_latest_remote (pick a newer 14-digit version) or checksum_mismatch. " +
|
|
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. " +
|
|
163300
163934
|
"If the schema change is urgent you may fall back to action=execute with allowDdlViaExecute=true, but that bypasses migration history.",
|
|
163301
163935
|
nextActions: [
|
|
163302
163936
|
buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Check LatestVersion and pick a migrationVersion strictly newer than it.", { action: "listMigrations", limit: 20 }),
|
|
163303
|
-
buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Re-run planMigration after adjusting migrationVersion/SQL.", {
|
|
163937
|
+
buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Re-run planMigration after adjusting migrationVersion/SQL (or includeAll=true for out-of-order).", {
|
|
163304
163938
|
action: "planMigration",
|
|
163305
163939
|
migrationName: args.migrationName,
|
|
163306
163940
|
migrationVersion: version,
|
|
163307
163941
|
sql: args.sql,
|
|
163308
163942
|
...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
|
|
163943
|
+
...(includeAll ? { includeAll: true } : {}),
|
|
163309
163944
|
}),
|
|
163310
163945
|
],
|
|
163311
163946
|
});
|
|
163312
163947
|
}
|
|
163313
|
-
const params = {
|
|
163948
|
+
const params = {
|
|
163949
|
+
Migrations: hydrated.migrations,
|
|
163950
|
+
IncludeAll: includeAll,
|
|
163951
|
+
};
|
|
163314
163952
|
if (args.lockTimeoutMs !== undefined) {
|
|
163315
163953
|
params.LockTimeoutMs = args.lockTimeoutMs;
|
|
163316
163954
|
}
|
|
@@ -163319,10 +163957,35 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163319
163957
|
}
|
|
163320
163958
|
const result = await callPgMigrationApi(context, "PushPGUserMigrations", params, cloudBaseOptions);
|
|
163321
163959
|
const taskId = typeof result.TaskId === "string" ? result.TaskId.trim() : "";
|
|
163960
|
+
const pollTimeoutMs = resolveMigrationTaskPollTimeoutMs(args.taskPollTimeoutMs);
|
|
163961
|
+
const shouldWaitForTask = args.waitForTask !== false;
|
|
163322
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
|
+
}
|
|
163323
163986
|
if (taskId) {
|
|
163324
163987
|
try {
|
|
163325
|
-
taskResult = await waitPgMigrationTask(context, taskId, cloudBaseOptions);
|
|
163988
|
+
taskResult = await waitPgMigrationTask(context, taskId, cloudBaseOptions, pollTimeoutMs);
|
|
163326
163989
|
}
|
|
163327
163990
|
catch (error) {
|
|
163328
163991
|
const lastTask = error.lastTask;
|
|
@@ -163336,14 +163999,16 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163336
163999
|
hydratedRemoteCount: hydrated.remoteCount,
|
|
163337
164000
|
apiResult: result,
|
|
163338
164001
|
taskResult: lastTask ?? { TaskId: taskId },
|
|
164002
|
+
taskPollTimeoutMs: pollTimeoutMs,
|
|
163339
164003
|
verified: null,
|
|
163340
164004
|
},
|
|
163341
|
-
message: `PushPGUserMigrations returned TaskId=${taskId}, but waiting for DescribeTaskResult timed out
|
|
163342
|
-
|
|
163343
|
-
"The migration may
|
|
163344
|
-
|
|
163345
|
-
|
|
163346
|
-
|
|
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),
|
|
163347
164012
|
});
|
|
163348
164013
|
}
|
|
163349
164014
|
const status = String(taskResult.Status || "").toLowerCase();
|
|
@@ -163371,6 +164036,7 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163371
164036
|
migrationVersion: version,
|
|
163372
164037
|
sql: args.sql,
|
|
163373
164038
|
...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
|
|
164039
|
+
...(includeAll ? { includeAll: true } : {}),
|
|
163374
164040
|
}),
|
|
163375
164041
|
buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Confirm the version is still absent from remote history.", { action: "listMigrations", limit: 20 }),
|
|
163376
164042
|
],
|
|
@@ -163388,6 +164054,7 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163388
164054
|
migrationName: args.migrationName,
|
|
163389
164055
|
localFileHint,
|
|
163390
164056
|
hydratedRemoteCount: hydrated.remoteCount,
|
|
164057
|
+
includeAll,
|
|
163391
164058
|
apiResult: result,
|
|
163392
164059
|
taskResult: taskResult,
|
|
163393
164060
|
verified: false,
|
|
@@ -163428,12 +164095,18 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
163428
164095
|
migrationVersion: version,
|
|
163429
164096
|
migrationName: args.migrationName,
|
|
163430
164097
|
localFileHint,
|
|
164098
|
+
localFilePath: localFile.relativePath,
|
|
164099
|
+
localFileAbsolutePath: localFile.absolutePath,
|
|
164100
|
+
localFileAction: localFile.action,
|
|
163431
164101
|
hydratedRemoteCount: hydrated.remoteCount,
|
|
164102
|
+
payloadSource: hydrated.payloadSource,
|
|
164103
|
+
includeAll,
|
|
163432
164104
|
apiResult: result,
|
|
163433
164105
|
taskResult: taskResult,
|
|
163434
164106
|
verified: true,
|
|
163435
164107
|
},
|
|
163436
|
-
message: `Migrations applied via PushPGUserMigrations (
|
|
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}.`,
|
|
163437
164110
|
});
|
|
163438
164111
|
}
|
|
163439
164112
|
catch (error) {
|
|
@@ -163494,6 +164167,156 @@ async function handleMigrationDetail(args, context, deps, cloudBaseOptions) {
|
|
|
163494
164167
|
});
|
|
163495
164168
|
}
|
|
163496
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
|
+
}
|
|
163497
164320
|
async function handleRollbackMigration(args, context, deps, cloudBaseOptions) {
|
|
163498
164321
|
if (args.lastN === undefined || args.lastN < 1 || !Number.isInteger(args.lastN)) {
|
|
163499
164322
|
return buildPgToolResult({
|
|
@@ -163708,11 +164531,11 @@ function registerPGDatabaseTools(server, providedDeps) {
|
|
|
163708
164531
|
});
|
|
163709
164532
|
server.registerTool?.(MANAGE_PG_DATABASE, {
|
|
163710
164533
|
title: "管理 PostgreSQL 上下文或执行写入 SQL",
|
|
163711
|
-
description: "管理 CloudBase PostgreSQL:执行已确认的写入 SQL、SQL 风险预检、迁移管理。建表/ALTER/DROP 等 schema 变更必须使用 applyMigration(显式 migrationVersion
|
|
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。",
|
|
163712
164535
|
inputSchema: {
|
|
163713
164536
|
action: zod_1.z
|
|
163714
164537
|
.enum(MANAGE_ACTIONS)
|
|
163715
|
-
.describe("操作类型:execute=执行已确认的写入 SQL(DML/GRANT/RLS;schema DDL 默认拒绝,需 allowDdlViaExecute=true);dryRun=只分析 SQL 风险不执行;planMigration=预览迁移计划(需 migrationName + migrationVersion + sql);applyMigration=应用迁移,建表/改 schema 首选(需 migrationName + migrationVersion + sql + confirm=true
|
|
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)"),
|
|
163716
164539
|
sql: zod_1.z
|
|
163717
164540
|
.string()
|
|
163718
164541
|
.optional()
|
|
@@ -163750,7 +164573,7 @@ function registerPGDatabaseTools(server, providedDeps) {
|
|
|
163750
164573
|
.string()
|
|
163751
164574
|
.regex(/^\d{14}$/)
|
|
163752
164575
|
.optional()
|
|
163753
|
-
.describe("14 位时间戳 YYYYMMDDHHMMSS。plan/apply/detail/repair
|
|
164576
|
+
.describe("14 位时间戳 YYYYMMDDHHMMSS。plan/apply/detail/repair 必填;fetchMigration 可选(传入则只拉该条,省略则拉全量远端 history);禁止由服务端静默生成,避免与本地 cloudbase/migrations/<version>_<name>.sql 分叉。"),
|
|
163754
164577
|
rollbackSql: zod_1.z
|
|
163755
164578
|
.string()
|
|
163756
164579
|
.optional()
|
|
@@ -163784,6 +164607,21 @@ function registerPGDatabaseTools(server, providedDeps) {
|
|
|
163784
164607
|
.int()
|
|
163785
164608
|
.optional()
|
|
163786
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),不轮询等待。"),
|
|
163787
164625
|
repairStatus: zod_1.z
|
|
163788
164626
|
.enum(["applied", "reverted"])
|
|
163789
164627
|
.optional()
|
|
@@ -163792,6 +164630,14 @@ function registerPGDatabaseTools(server, providedDeps) {
|
|
|
163792
164630
|
.string()
|
|
163793
164631
|
.optional()
|
|
163794
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。"),
|
|
163795
164641
|
allowDdlViaExecute: zod_1.z
|
|
163796
164642
|
.boolean()
|
|
163797
164643
|
.optional()
|
|
@@ -163839,6 +164685,8 @@ function registerPGDatabaseTools(server, providedDeps) {
|
|
|
163839
164685
|
case "applyMigration":
|
|
163840
164686
|
case "listMigrations":
|
|
163841
164687
|
case "migrationDetail":
|
|
164688
|
+
case "describeMigrationTask":
|
|
164689
|
+
case "fetchMigration":
|
|
163842
164690
|
case "rollbackMigration":
|
|
163843
164691
|
case "repairMigration": {
|
|
163844
164692
|
switch (args.action) {
|
|
@@ -163846,6 +164694,8 @@ function registerPGDatabaseTools(server, providedDeps) {
|
|
|
163846
164694
|
case "applyMigration": return handleApplyMigration(args, context, deps, cbOpts);
|
|
163847
164695
|
case "listMigrations": return handleListMigrations(args, context, deps, cbOpts);
|
|
163848
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);
|
|
163849
164699
|
case "rollbackMigration": return handleRollbackMigration(args, context, deps, cbOpts);
|
|
163850
164700
|
case "repairMigration": return handleRepairMigration(args, context, deps, cbOpts);
|
|
163851
164701
|
default: return buildPgToolResult({ success: false, errorCode: "UNSUPPORTED_ACTION", message: `Unsupported action: ${args.action}` });
|
|
@@ -185308,7 +186158,7 @@ const KnowledgeBaseIdMap = {
|
|
|
185308
186158
|
const CACHE_BASE_DIR = path.join(os.homedir(), ".cloudbase-mcp");
|
|
185309
186159
|
const CACHE_META_FILE = path.join(CACHE_BASE_DIR, "cache-meta.json");
|
|
185310
186160
|
const LOCK_FILE = path.join(CACHE_BASE_DIR, ".download.lock");
|
|
185311
|
-
const DEFAULT_CACHE_TTL_MS =
|
|
186161
|
+
const DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000; // default 1 hour (was 24h; templates/docs refresh more often)
|
|
185312
186162
|
// Promise wrapper for lockfile methods
|
|
185313
186163
|
function acquireLock(lockPath, options) {
|
|
185314
186164
|
return new Promise((resolve, reject) => {
|
|
@@ -193701,10 +194551,11 @@ class AgentService {
|
|
|
193701
194551
|
// ==================== 日志查询方法 ====================
|
|
193702
194552
|
/**
|
|
193703
194553
|
* 获取 Agent 调用日志(自动判断类型)
|
|
194554
|
+
*
|
|
194555
|
+
* SCF / TCBR 均直接走 CLS `searchClsLog`,返回 CLS 原始检索结果 `ISearchClsLogResponse`。
|
|
194556
|
+
* 分页请透传上次返回的 `LogResults.Context` 到入参 `context`。
|
|
193704
194557
|
* @param params 查询参数
|
|
193705
|
-
* @returns
|
|
193706
|
-
* - SCF: IFunctionLogDetailRes[]
|
|
193707
|
-
* - TCBR: ISearchClsLogResponse
|
|
194558
|
+
* @returns CLS 检索结果 ISearchClsLogResponse
|
|
193708
194559
|
* @example
|
|
193709
194560
|
* ```typescript
|
|
193710
194561
|
* const logs = await agentService.getAgentLogs({
|
|
@@ -193724,25 +194575,18 @@ class AgentService {
|
|
|
193724
194575
|
}
|
|
193725
194576
|
const agentInfo = agentListRes.AgentList[0];
|
|
193726
194577
|
const agentType = agentInfo.AgentType;
|
|
193727
|
-
//
|
|
193728
|
-
if (agentType === 'scf') {
|
|
193729
|
-
const functionService = this.environment.getFunctionService();
|
|
193730
|
-
return functionService.getCompleteFunctionLogs({
|
|
193731
|
-
name: AgentId,
|
|
193732
|
-
offset: params.offset,
|
|
193733
|
-
limit: params.limit,
|
|
193734
|
-
startTime: params.startTime,
|
|
193735
|
-
endTime: params.endTime,
|
|
193736
|
-
requestId: params.requestId
|
|
193737
|
-
});
|
|
193738
|
-
}
|
|
193739
|
-
else if (agentType === 'tcbr') {
|
|
194578
|
+
// scf / tcbr 均直接走 CLS searchClsLog(getFunctionLogs 系列接口已下线)
|
|
194579
|
+
if (agentType === 'scf' || agentType === 'tcbr') {
|
|
193740
194580
|
const logService = this.environment.getLogService();
|
|
193741
|
-
const { startTime, endTime, limit = 100, requestId } = params;
|
|
194581
|
+
const { startTime, endTime, limit = 100, requestId, context, sort } = params;
|
|
193742
194582
|
// 构建查询语句
|
|
193743
|
-
|
|
194583
|
+
// 采用双引号短语检索保证精确匹配(AgentId/RequestId 含 '-' 会被分词,
|
|
194584
|
+
// 不加引号会因分词命中相似 ID 的其它日志);并对入参做转义,避免检索语法被注入。
|
|
194585
|
+
// CLS 双引号短语检索下需转义 '"'、'*' 与 '\\'。
|
|
194586
|
+
const escapeClsPhrase = (value) => value.replace(/["*\\]/g, '\\$&');
|
|
194587
|
+
let queryString = `SCF_FunctionName:"${escapeClsPhrase(AgentId)}"`;
|
|
193744
194588
|
if (requestId) {
|
|
193745
|
-
queryString += ` AND SCF_RequestId
|
|
194589
|
+
queryString += ` AND SCF_RequestId:"${escapeClsPhrase(requestId)}"`;
|
|
193746
194590
|
}
|
|
193747
194591
|
// 辅助函数:格式化时间
|
|
193748
194592
|
const formatDateTime = (date) => {
|
|
@@ -193756,7 +194600,9 @@ class AgentService {
|
|
|
193756
194600
|
StartTime: startTime || defaultStartTime,
|
|
193757
194601
|
EndTime: endTime || defaultEndTime,
|
|
193758
194602
|
queryString: queryString,
|
|
193759
|
-
Limit: limit > 100 ? 100 : limit
|
|
194603
|
+
Limit: limit > 100 ? 100 : limit,
|
|
194604
|
+
Context: context,
|
|
194605
|
+
Sort: sort
|
|
193760
194606
|
});
|
|
193761
194607
|
}
|
|
193762
194608
|
else {
|
|
@@ -197539,6 +198385,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
197539
198385
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
197540
198386
|
exports.AccessService = void 0;
|
|
197541
198387
|
const utils_1 = __webpack_require__(62358);
|
|
198388
|
+
/**
|
|
198389
|
+
* HTTP Service(CloudBaseGW)访问服务。
|
|
198390
|
+
* 注意:HTTP Service 模块自 v5.0.0 起已废弃,建议迁移到 EnvService 的域名与路由能力
|
|
198391
|
+
* (如 createHttpServiceRoute / modifyHttpServiceRoute / deleteHttpServiceRoute)。
|
|
198392
|
+
*/
|
|
197542
198393
|
class AccessService {
|
|
197543
198394
|
constructor(environment) {
|
|
197544
198395
|
this.environment = environment;
|
|
@@ -198976,7 +199827,7 @@ ${envIdSection}
|
|
|
198976
199827
|
## 环境信息
|
|
198977
199828
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
198978
199829
|
- Node.js版本: ${process.version}
|
|
198979
|
-
- MCP 版本:${process.env.npm_package_version || "2.25.
|
|
199830
|
+
- MCP 版本:${process.env.npm_package_version || "2.25.6" || 0}
|
|
198980
199831
|
- 系统架构: ${os_1.default.arch()}
|
|
198981
199832
|
- 时间: ${new Date().toISOString()}
|
|
198982
199833
|
- 请求ID: ${requestId}
|
|
@@ -199057,7 +199908,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
199057
199908
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
199058
199909
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
199059
199910
|
});
|
|
199060
|
-
const mcpVersion = true ? "2.25.
|
|
199911
|
+
const mcpVersion = true ? "2.25.6" : 0;
|
|
199061
199912
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
199062
199913
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
199063
199914
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -214477,7 +215328,7 @@ function buildWriteVerificationHint(resourceId) {
|
|
|
214477
215328
|
return `对于 ${resourceId} 这类有后端权限控制的集合,前端调用 .doc(id).update() / .doc(id).remove() 后,不能只看是否没有抛异常。请显式检查返回结果中的 updated / deleted 是否大于 0;如果 result.code、result.message 存在,或 updated / deleted 为 0,要把它当作真实失败并向上抛错。`;
|
|
214478
215329
|
}
|
|
214479
215330
|
function buildPermissionPropagationHint(resourceId) {
|
|
214480
|
-
return `刚更新完 ${resourceId}
|
|
215331
|
+
return `刚更新完 ${resourceId} 的安全规则时,后端权限通常在数秒到约 30 秒内生效。若紧接着的真实写操作仍返回 DATABASE_PERMISSION_DENIED,请先间隔数秒用同一登录态重试同一条 .doc(id).update() / .doc(id).remove();不要盲等数分钟,也不要立刻连续重写规则,更不要在短暂传播窗口里把旧拒绝直接当成规则表达式仍然错误。`;
|
|
214481
215332
|
}
|
|
214482
215333
|
function buildEnvelope(data, message) {
|
|
214483
215334
|
return {
|
|
@@ -214502,6 +215353,171 @@ function mapResourceType(resourceType) {
|
|
|
214502
215353
|
};
|
|
214503
215354
|
return resourceTypeMap[resourceType];
|
|
214504
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
|
+
}
|
|
214505
215521
|
function normalizeRecordArray(value, label) {
|
|
214506
215522
|
if (value === undefined) {
|
|
214507
215523
|
return undefined;
|
|
@@ -214677,7 +215693,7 @@ function registerPermissionTools(server) {
|
|
|
214677
215693
|
};
|
|
214678
215694
|
server.registerTool?.("queryPermissions", {
|
|
214679
215695
|
title: "查询 CloudBase 权限与用户配置",
|
|
214680
|
-
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`)。",
|
|
214681
215697
|
inputSchema: {
|
|
214682
215698
|
action: zod_1.z.enum(QUERY_PERMISSION_ACTIONS),
|
|
214683
215699
|
resourceType: zod_1.z
|
|
@@ -214709,8 +215725,10 @@ function registerPermissionTools(server) {
|
|
|
214709
215725
|
if (resourceType === "storage") {
|
|
214710
215726
|
await ensureStorageBucketsExist(cloudbase, [resourceId]);
|
|
214711
215727
|
}
|
|
214712
|
-
const result = await
|
|
214713
|
-
|
|
215728
|
+
const result = await describeResourcePermissionWithFunctionPgFallback({
|
|
215729
|
+
cloudbase,
|
|
215730
|
+
envId,
|
|
215731
|
+
resourceType,
|
|
214714
215732
|
resources: [resourceId],
|
|
214715
215733
|
});
|
|
214716
215734
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
@@ -214726,8 +215744,11 @@ function registerPermissionTools(server) {
|
|
|
214726
215744
|
aclTag: matchedPermission?.Permission,
|
|
214727
215745
|
permissions,
|
|
214728
215746
|
hints,
|
|
214729
|
-
|
|
214730
|
-
|
|
215747
|
+
...(result.fallback ? { fallback: result.fallback } : {}),
|
|
215748
|
+
raw: result.raw ?? result,
|
|
215749
|
+
}, result.fallback
|
|
215750
|
+
? "资源权限查询成功(PostgreSQL 环境已回退到 describeEnvAuthzConfig / tcb policy get)"
|
|
215751
|
+
: "资源权限查询成功");
|
|
214731
215752
|
}
|
|
214732
215753
|
case "listResourcePermissions": {
|
|
214733
215754
|
if (!resourceType) {
|
|
@@ -214736,8 +215757,10 @@ function registerPermissionTools(server) {
|
|
|
214736
215757
|
if (resourceType === "storage" && resourceIds?.length) {
|
|
214737
215758
|
await ensureStorageBucketsExist(cloudbase, resourceIds);
|
|
214738
215759
|
}
|
|
214739
|
-
const result = await
|
|
214740
|
-
|
|
215760
|
+
const result = await describeResourcePermissionWithFunctionPgFallback({
|
|
215761
|
+
cloudbase,
|
|
215762
|
+
envId,
|
|
215763
|
+
resourceType,
|
|
214741
215764
|
resources: resourceIds,
|
|
214742
215765
|
});
|
|
214743
215766
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
@@ -214758,8 +215781,11 @@ function registerPermissionTools(server) {
|
|
|
214758
215781
|
permissions,
|
|
214759
215782
|
resourceHints,
|
|
214760
215783
|
total: result.Data.TotalCount ?? 0,
|
|
214761
|
-
|
|
214762
|
-
|
|
215784
|
+
...(result.fallback ? { fallback: result.fallback } : {}),
|
|
215785
|
+
raw: result.raw ?? result,
|
|
215786
|
+
}, result.fallback
|
|
215787
|
+
? "资源权限列表查询成功(PostgreSQL 环境已回退到 describeEnvAuthzConfig / tcb policy get)"
|
|
215788
|
+
: "资源权限列表查询成功");
|
|
214763
215789
|
}
|
|
214764
215790
|
case "listRoles": {
|
|
214765
215791
|
const result = await cloudbase.permission.describeRoleList({
|
|
@@ -214838,7 +215864,7 @@ function registerPermissionTools(server) {
|
|
|
214838
215864
|
}));
|
|
214839
215865
|
server.registerTool?.("managePermissions", {
|
|
214840
215866
|
title: "管理 CloudBase 权限与用户配置",
|
|
214841
|
-
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 相同。",
|
|
214842
215868
|
inputSchema: {
|
|
214843
215869
|
action: zod_1.z.enum(MANAGE_PERMISSION_ACTIONS),
|
|
214844
215870
|
resourceType: zod_1.z
|
|
@@ -214886,12 +215912,29 @@ function registerPermissionTools(server) {
|
|
|
214886
215912
|
if (!resourceType || !resourceId || !permission) {
|
|
214887
215913
|
throw new Error("action=updateResourcePermission 时必须提供 resourceType、resourceId 和 permission");
|
|
214888
215914
|
}
|
|
214889
|
-
|
|
214890
|
-
|
|
214891
|
-
|
|
214892
|
-
|
|
214893
|
-
|
|
214894
|
-
|
|
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
|
+
}
|
|
214895
215938
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
214896
215939
|
const hints = permission === "CUSTOM" ? buildPermissionHints(securityRule, resourceId) : [];
|
|
214897
215940
|
return buildEnvelope({
|
|
@@ -214901,6 +215944,8 @@ function registerPermissionTools(server) {
|
|
|
214901
215944
|
resourceId,
|
|
214902
215945
|
permission,
|
|
214903
215946
|
hints,
|
|
215947
|
+
...(fallback ? { fallback } : {}),
|
|
215948
|
+
...(appliedRego ? { rego: appliedRego } : {}),
|
|
214904
215949
|
verificationHint: resourceType === "noSqlDatabase" && permission === "CUSTOM"
|
|
214905
215950
|
? buildWriteVerificationHint(resourceId)
|
|
214906
215951
|
: undefined,
|
|
@@ -214908,7 +215953,9 @@ function registerPermissionTools(server) {
|
|
|
214908
215953
|
? buildPermissionPropagationHint(resourceId)
|
|
214909
215954
|
: undefined,
|
|
214910
215955
|
raw: result,
|
|
214911
|
-
},
|
|
215956
|
+
}, fallback
|
|
215957
|
+
? "资源权限更新成功(PostgreSQL 环境已回退到 modifyEnvAuthzConfig / tcb policy set)"
|
|
215958
|
+
: "资源权限更新成功");
|
|
214912
215959
|
}
|
|
214913
215960
|
case "createRole": {
|
|
214914
215961
|
if (!roleName || !roleIdentity) {
|
|
@@ -215645,7 +216692,7 @@ function registerSetupTools(server) {
|
|
|
215645
216692
|
server.registerTool("downloadTemplate", {
|
|
215646
216693
|
title: "下载项目模板",
|
|
215647
216694
|
description: `自动下载并部署CloudBase项目模板。
|
|
215648
|
-
**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.
|
|
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)`,
|
|
215649
216696
|
inputSchema: {
|
|
215650
216697
|
template: zod_1.z
|
|
215651
216698
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -232965,7 +234012,7 @@ function registerGatewayTools(server) {
|
|
|
232965
234012
|
{
|
|
232966
234013
|
tool: "queryGateway",
|
|
232967
234014
|
action: "getRoute",
|
|
232968
|
-
reason: "
|
|
234015
|
+
reason: "创建后通常数秒到约 30 秒内生效;请立刻轮询 getRoute 或探测 accessUrl,勿盲等 60 秒以上",
|
|
232969
234016
|
},
|
|
232970
234017
|
{
|
|
232971
234018
|
tool: "queryPermissions",
|
|
@@ -233113,7 +234160,7 @@ function registerGatewayTools(server) {
|
|
|
233113
234160
|
const privilege = await getGatewayPrivilege();
|
|
233114
234161
|
if (privilege.EnableService !== true) {
|
|
233115
234162
|
privilegeHint =
|
|
233116
|
-
"⚠️ HTTP 网关总开关未开启,路由创建成功后访问仍将返回 HTTPSERVICE_NONACTIVATED(403);请先调用 manageGateway(action=\"enableService\", enable=true)
|
|
234163
|
+
"⚠️ HTTP 网关总开关未开启,路由创建成功后访问仍将返回 HTTPSERVICE_NONACTIVATED(403);请先调用 manageGateway(action=\"enableService\", enable=true) 开启,再立刻探测 accessUrl(通常数秒到约 30 秒内生效,勿盲等 60 秒以上)。";
|
|
233117
234164
|
privilegeNextActions = [
|
|
233118
234165
|
{
|
|
233119
234166
|
tool: "manageGateway",
|
|
@@ -233149,7 +234196,7 @@ function registerGatewayTools(server) {
|
|
|
233149
234196
|
: payload.resolved.enablePathTransmission === false
|
|
233150
234197
|
? ";路径透传关闭(网关会剥掉触发路径前缀后再转发给后端)"
|
|
233151
234198
|
: ";路径透传未显式设置(平台默认 false,会剥掉触发路径前缀)") +
|
|
233152
|
-
|
|
234199
|
+
`。注意:路由配置传播通常数秒到约 30 秒;请立刻用 queryGateway(getRoute) 或探测 accessUrl 确认,勿盲等 60 秒以上。该操作只创建网关入口,不会自动放开上游权限;若上游是云函数且需要匿名或浏览器直接访问,请继续检查函数资源权限。` +
|
|
233153
234200
|
(privilegeHint ? ` ${privilegeHint}` : ""), [
|
|
233154
234201
|
...privilegeNextActions,
|
|
233155
234202
|
...(routeMutationNextActions(payload.resolved.upstreamResourceName) ??
|
|
@@ -265760,6 +266807,10 @@ class CloudBase {
|
|
|
265760
266807
|
get hosting() {
|
|
265761
266808
|
return this.currentEnvironment().getHostingService();
|
|
265762
266809
|
}
|
|
266810
|
+
/**
|
|
266811
|
+
* HTTP Service(CloudBaseGW)访问服务
|
|
266812
|
+
* @deprecated 自 v5.0.0 起 HTTP Service 模块已废弃,请改用 env 下的域名与路由相关 API。
|
|
266813
|
+
*/
|
|
265763
266814
|
get access() {
|
|
265764
266815
|
return this.currentEnvironment().getAccessService();
|
|
265765
266816
|
}
|
|
@@ -267872,48 +268923,27 @@ class FunctionService {
|
|
|
267872
268923
|
return data;
|
|
267873
268924
|
}
|
|
267874
268925
|
/**
|
|
267875
|
-
|
|
267876
|
-
|
|
267877
|
-
* @param {{
|
|
267878
|
-
* name: string
|
|
267879
|
-
* offset: number
|
|
267880
|
-
* limit: number
|
|
267881
|
-
* order: string
|
|
267882
|
-
* orderBy: string
|
|
267883
|
-
* startTime: string
|
|
267884
|
-
* endTime: string
|
|
267885
|
-
* requestId: string
|
|
267886
|
-
* }} options
|
|
267887
|
-
* @returns {Promise<IFunctionLogRes>}
|
|
267888
|
-
*/
|
|
267889
|
-
async getFunctionLogs(options) {
|
|
267890
|
-
let { name, offset = 0, limit = 10, order, orderBy, startTime, endTime, requestId } = options;
|
|
267891
|
-
const { namespace } = this.getFunctionConfig();
|
|
267892
|
-
if (startTime && !endTime) {
|
|
267893
|
-
// 自动补 endTime 为 startTime + 1d
|
|
267894
|
-
endTime = (0, utils_1.formatTimeByMs)(new Date(startTime).getTime() + 24 * 3600 * 1000);
|
|
267895
|
-
}
|
|
267896
|
-
if (endTime && !startTime) {
|
|
267897
|
-
// 自动补 startTime 为 endTime - 1d
|
|
267898
|
-
startTime = (0, utils_1.formatTimeByMs)(new Date(endTime).getTime() - 24 * 3600 * 1000);
|
|
267899
|
-
}
|
|
267900
|
-
const params = {
|
|
267901
|
-
Namespace: namespace,
|
|
267902
|
-
FunctionName: name,
|
|
267903
|
-
Offset: offset,
|
|
267904
|
-
Limit: limit,
|
|
267905
|
-
Order: order,
|
|
267906
|
-
OrderBy: orderBy,
|
|
267907
|
-
StartTime: startTime,
|
|
267908
|
-
EndTime: endTime,
|
|
267909
|
-
FunctionRequestId: requestId
|
|
267910
|
-
};
|
|
267911
|
-
const res = await this.scfService.request('GetFunctionLogs', params);
|
|
267912
|
-
return res;
|
|
267913
|
-
}
|
|
267914
|
-
/**
|
|
267915
|
-
* 获取函数日志 ID 列表
|
|
268926
|
+
* 获取函数日志
|
|
268927
|
+
* @deprecated 底层 GetFunctionLogs 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志
|
|
267916
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 {{
|
|
267917
268947
|
* name: string
|
|
267918
268948
|
* offset: number
|
|
267919
268949
|
* limit: number
|
|
@@ -267924,28 +268954,12 @@ class FunctionService {
|
|
|
267924
268954
|
* }} options
|
|
267925
268955
|
* @returns {Promise<IFunctionLogResV2>}
|
|
267926
268956
|
*/
|
|
267927
|
-
async getFunctionLogsV2(
|
|
267928
|
-
|
|
267929
|
-
const { env } = this.getFunctionConfig();
|
|
267930
|
-
({ startTime, endTime } = (0, utils_1.getCompleteTimeRange)({
|
|
267931
|
-
startTime,
|
|
267932
|
-
endTime
|
|
267933
|
-
}));
|
|
267934
|
-
const params = {
|
|
267935
|
-
EnvId: env,
|
|
267936
|
-
FunctionName: name,
|
|
267937
|
-
Offset: offset,
|
|
267938
|
-
Limit: limit,
|
|
267939
|
-
StartTime: startTime,
|
|
267940
|
-
EndTime: endTime,
|
|
267941
|
-
LogRequestId: requestId,
|
|
267942
|
-
Qualifier: qualifier || "$LATEST" // 云函数版本,不传则用 $LATEST
|
|
267943
|
-
};
|
|
267944
|
-
const res = await this.tcbService.request('GetFunctionLogs', params);
|
|
267945
|
-
return res;
|
|
268957
|
+
async getFunctionLogsV2(_options) {
|
|
268958
|
+
throw new error_1.CloudBaseError('getFunctionLogsV2 已废弃:底层 GetFunctionLogs 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志');
|
|
267946
268959
|
}
|
|
267947
268960
|
/**
|
|
267948
268961
|
* 根据函数日志 ID 查询日志详情
|
|
268962
|
+
* @deprecated 底层 GetFunctionLogDetail 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志
|
|
267949
268963
|
* @param {{
|
|
267950
268964
|
* startTime: string
|
|
267951
268965
|
* endTime: string
|
|
@@ -267953,46 +268967,18 @@ class FunctionService {
|
|
|
267953
268967
|
* }} options
|
|
267954
268968
|
* @returns {Promise<IFunctionLogDetailRes>}
|
|
267955
268969
|
*/
|
|
267956
|
-
async getFunctionLogDetail(
|
|
267957
|
-
|
|
267958
|
-
({ startTime, endTime } = (0, utils_1.getCompleteTimeRange)({
|
|
267959
|
-
startTime,
|
|
267960
|
-
endTime
|
|
267961
|
-
}));
|
|
267962
|
-
const params = {
|
|
267963
|
-
StartTime: startTime,
|
|
267964
|
-
LogRequestId: logRequestId,
|
|
267965
|
-
EndTime: endTime
|
|
267966
|
-
};
|
|
267967
|
-
const res = await this.tcbService.request('GetFunctionLogDetail', params);
|
|
267968
|
-
return res;
|
|
268970
|
+
async getFunctionLogDetail(_options) {
|
|
268971
|
+
throw new error_1.CloudBaseError('getFunctionLogDetail 已废弃:底层 GetFunctionLogDetail 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志');
|
|
267969
268972
|
}
|
|
267970
268973
|
/**
|
|
267971
268974
|
* 获取函数的完整调用日志
|
|
267972
268975
|
* 该方法会自动完成两步操作:1. 获取日志请求ID列表。 2. 根据ID列表获取每条日志的详细内容。
|
|
268976
|
+
* @deprecated 底层 GetFunctionLogs/GetFunctionLogDetail 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志
|
|
267973
268977
|
* @param {IFunctionLogOptionsV2} options - 查询选项
|
|
267974
268978
|
* @returns {Promise<IFunctionLogDetailRes[]>} 返回包含完整日志详情的数组
|
|
267975
268979
|
*/
|
|
267976
|
-
async getCompleteFunctionLogs(
|
|
267977
|
-
|
|
267978
|
-
const { name } = options;
|
|
267979
|
-
const logs = await this.getFunctionLogsV2(options);
|
|
267980
|
-
// 如果没有日志,直接返回空数组
|
|
267981
|
-
if (logs.LogList.length === 0) {
|
|
267982
|
-
return [];
|
|
267983
|
-
}
|
|
267984
|
-
const detailPromises = logs.LogList.map(async (log) => {
|
|
267985
|
-
// 对每一个日志ID,调用 getFunctionLogDetail
|
|
267986
|
-
const res = await this.getFunctionLogDetail({
|
|
267987
|
-
logRequestId: log.RequestId,
|
|
267988
|
-
startTime: options.startTime,
|
|
267989
|
-
endTime: options.endTime
|
|
267990
|
-
});
|
|
267991
|
-
return Object.assign(Object.assign({}, res), { RetCode: log.RetCode, FunctionName: name });
|
|
267992
|
-
});
|
|
267993
|
-
// 并发执行所有详情查询,等待它们全部完成
|
|
267994
|
-
const detailedLogs = await Promise.all(detailPromises);
|
|
267995
|
-
return detailedLogs;
|
|
268980
|
+
async getCompleteFunctionLogs(_options) {
|
|
268981
|
+
throw new error_1.CloudBaseError('getCompleteFunctionLogs 已废弃:底层 GetFunctionLogs/GetFunctionLogDetail 接口已下线,请使用 env.getLogService().searchClsLog() 查询云函数日志');
|
|
267996
268982
|
}
|
|
267997
268983
|
/**
|
|
267998
268984
|
* 更新云函数配置
|