@cloudbase/cloudbase-mcp 2.25.3 → 2.25.5
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 +476 -37
- package/dist/index.js +476 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2772,7 +2772,7 @@ ${envIdSection}
|
|
|
2772
2772
|
## 环境信息
|
|
2773
2773
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
2774
2774
|
- Node.js版本: ${process.version}
|
|
2775
|
-
- MCP 版本:${process.env.npm_package_version || "2.25.
|
|
2775
|
+
- MCP 版本:${process.env.npm_package_version || "2.25.5" || 0}
|
|
2776
2776
|
- 系统架构: ${os_1.default.arch()}
|
|
2777
2777
|
- 时间: ${new Date().toISOString()}
|
|
2778
2778
|
- 请求ID: ${requestId}
|
|
@@ -2853,7 +2853,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
2853
2853
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
2854
2854
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
2855
2855
|
});
|
|
2856
|
-
const mcpVersion = true ? "2.25.
|
|
2856
|
+
const mcpVersion = true ? "2.25.5" : 0;
|
|
2857
2857
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
2858
2858
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
2859
2859
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -7840,6 +7840,7 @@ const QUERY_GATEWAY_ACTIONS = [
|
|
|
7840
7840
|
"listRoutes",
|
|
7841
7841
|
"getRoute",
|
|
7842
7842
|
"listCustomDomains",
|
|
7843
|
+
"getPrivilege",
|
|
7843
7844
|
];
|
|
7844
7845
|
const MANAGE_GATEWAY_ACTIONS = [
|
|
7845
7846
|
"createRoute",
|
|
@@ -7847,6 +7848,8 @@ const MANAGE_GATEWAY_ACTIONS = [
|
|
|
7847
7848
|
"deleteRoute",
|
|
7848
7849
|
"bindCustomDomain",
|
|
7849
7850
|
"deleteCustomDomain",
|
|
7851
|
+
"enableService",
|
|
7852
|
+
"authSwitch",
|
|
7850
7853
|
];
|
|
7851
7854
|
/** DomainType of the environment default HTTP service (gateway) domain. */
|
|
7852
7855
|
const HTTP_SERVICE_DOMAIN_TYPE = "HTTPSERVICE";
|
|
@@ -7943,6 +7946,32 @@ function registerGatewayTools(server) {
|
|
|
7943
7946
|
candidates.find((item) => item.Enable !== false) ??
|
|
7944
7947
|
candidates[0]);
|
|
7945
7948
|
};
|
|
7949
|
+
const getGatewayPrivilege = async () => {
|
|
7950
|
+
const cloudbase = await getManager();
|
|
7951
|
+
const result = await cloudbase
|
|
7952
|
+
.commonService("tcb", "2018-06-08")
|
|
7953
|
+
.call({
|
|
7954
|
+
Action: "DescribeCloudBaseGWPrivilege",
|
|
7955
|
+
Param: {
|
|
7956
|
+
ServiceId: await resolveEnvId(),
|
|
7957
|
+
},
|
|
7958
|
+
});
|
|
7959
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7960
|
+
return result ?? {};
|
|
7961
|
+
};
|
|
7962
|
+
const buildPrivilegeDescription = (privilege) => {
|
|
7963
|
+
const serviceStatus = privilege.EnableService === undefined
|
|
7964
|
+
? "未知"
|
|
7965
|
+
: privilege.EnableService === true
|
|
7966
|
+
? "已开启"
|
|
7967
|
+
: "未开启";
|
|
7968
|
+
const authStatus = privilege.EnableAuth === undefined
|
|
7969
|
+
? "未知"
|
|
7970
|
+
: privilege.EnableAuth === true
|
|
7971
|
+
? "已开启"
|
|
7972
|
+
: "未开启";
|
|
7973
|
+
return `HTTP 网关${serviceStatus},访问鉴权${authStatus}`;
|
|
7974
|
+
};
|
|
7946
7975
|
const resolveDefaultHttpDomain = async () => {
|
|
7947
7976
|
// Match console DescribeHTTPServiceRoute usage: filter DomainType=HTTPSERVICE.
|
|
7948
7977
|
// Envs often also expose an IsDefault STATIC_STORE domain (*.tcloudbaseapp.com);
|
|
@@ -8115,6 +8144,28 @@ function registerGatewayTools(server) {
|
|
|
8115
8144
|
},
|
|
8116
8145
|
]);
|
|
8117
8146
|
}
|
|
8147
|
+
case "getPrivilege": {
|
|
8148
|
+
const privilege = await getGatewayPrivilege();
|
|
8149
|
+
const enableService = privilege.EnableService === true;
|
|
8150
|
+
const enableAuth = privilege.EnableAuth === true;
|
|
8151
|
+
return buildEnvelope({
|
|
8152
|
+
action: input.action,
|
|
8153
|
+
enableService,
|
|
8154
|
+
enableAuth,
|
|
8155
|
+
raw: privilege,
|
|
8156
|
+
}, buildPrivilegeDescription(privilege) +
|
|
8157
|
+
(enableService
|
|
8158
|
+
? ""
|
|
8159
|
+
: ";若路由创建成功但访问报 HTTPSERVICE_NONACTIVATED(403),请先开启 HTTP 网关"), enableService
|
|
8160
|
+
? undefined
|
|
8161
|
+
: [
|
|
8162
|
+
{
|
|
8163
|
+
tool: "manageGateway",
|
|
8164
|
+
action: "enableService",
|
|
8165
|
+
reason: "开启 HTTP 网关总开关(EnableService),开启后路由即可通过默认域访问",
|
|
8166
|
+
},
|
|
8167
|
+
]);
|
|
8168
|
+
}
|
|
8118
8169
|
default:
|
|
8119
8170
|
throw new Error(`不支持的操作类型: ${input.action}`);
|
|
8120
8171
|
}
|
|
@@ -8145,6 +8196,28 @@ function registerGatewayTools(server) {
|
|
|
8145
8196
|
throw err;
|
|
8146
8197
|
}
|
|
8147
8198
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
8199
|
+
// 探测 HTTP 网关总开关:未开启时提示 HTTPSERVICE_NONACTIVATED 风险并引导开启。
|
|
8200
|
+
// 探测失败不阻断路由创建结果,仅追加弱提示。
|
|
8201
|
+
let privilegeHint = "";
|
|
8202
|
+
let privilegeNextActions = [];
|
|
8203
|
+
try {
|
|
8204
|
+
const privilege = await getGatewayPrivilege();
|
|
8205
|
+
if (privilege.EnableService !== true) {
|
|
8206
|
+
privilegeHint =
|
|
8207
|
+
"⚠️ HTTP 网关总开关未开启,路由创建成功后访问仍将返回 HTTPSERVICE_NONACTIVATED(403);请先调用 manageGateway(action=\"enableService\", enable=true) 开启,再等待 30 秒到 3 分钟访问。";
|
|
8208
|
+
privilegeNextActions = [
|
|
8209
|
+
{
|
|
8210
|
+
tool: "manageGateway",
|
|
8211
|
+
action: "enableService",
|
|
8212
|
+
reason: "开启 HTTP 网关总开关(EnableService),否则访问路由会报 HTTPSERVICE_NONACTIVATED(403)",
|
|
8213
|
+
},
|
|
8214
|
+
];
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8217
|
+
catch {
|
|
8218
|
+
privilegeHint =
|
|
8219
|
+
"(无法确认 HTTP 网关开关状态;若访问报 HTTPSERVICE_NONACTIVATED,请用 queryGateway(action=\"getPrivilege\") 查询后用 manageGateway(action=\"enableService\") 开启)";
|
|
8220
|
+
}
|
|
8148
8221
|
return buildEnvelope({
|
|
8149
8222
|
action: input.action,
|
|
8150
8223
|
model: "httpServiceRoute",
|
|
@@ -8167,7 +8240,12 @@ function registerGatewayTools(server) {
|
|
|
8167
8240
|
: payload.resolved.enablePathTransmission === false
|
|
8168
8241
|
? ";路径透传关闭(网关会剥掉触发路径前缀后再转发给后端)"
|
|
8169
8242
|
: ";路径透传未显式设置(平台默认 false,会剥掉触发路径前缀)") +
|
|
8170
|
-
`。注意:路由配置传播通常需要等待 30 秒到 3
|
|
8243
|
+
`。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。该操作只创建网关入口,不会自动放开上游权限;若上游是云函数且需要匿名或浏览器直接访问,请继续检查函数资源权限。` +
|
|
8244
|
+
(privilegeHint ? ` ${privilegeHint}` : ""), [
|
|
8245
|
+
...privilegeNextActions,
|
|
8246
|
+
...(routeMutationNextActions(payload.resolved.upstreamResourceName) ??
|
|
8247
|
+
[]),
|
|
8248
|
+
]);
|
|
8171
8249
|
}
|
|
8172
8250
|
case "updateRoute": {
|
|
8173
8251
|
const cloudbase = await getManager();
|
|
@@ -8226,12 +8304,28 @@ function registerGatewayTools(server) {
|
|
|
8226
8304
|
if (!input.domain || !input.certificateId) {
|
|
8227
8305
|
throw new Error("action=bindCustomDomain 时必须提供 domain 和 certificateId");
|
|
8228
8306
|
}
|
|
8307
|
+
const accessType = input.accessType ?? "DIRECT";
|
|
8308
|
+
if (accessType === "CUSTOM" && !input.customCname) {
|
|
8309
|
+
throw new Error("action=bindCustomDomain 且 accessType=CUSTOM 时必须提供 customCname(自有 CDN/WAF 的回源/回填地址,不是 DNS 解析目标)。" +
|
|
8310
|
+
"说明:https://docs.cloudbase.net/service/custom-domain");
|
|
8311
|
+
}
|
|
8312
|
+
if (accessType !== "CUSTOM" && input.customCname) {
|
|
8313
|
+
throw new Error("customCname 仅在 accessType=CUSTOM 时可用;普通绑定用默认 DIRECT,不要传 customCname。" +
|
|
8314
|
+
"说明:https://docs.cloudbase.net/service/custom-domain");
|
|
8315
|
+
}
|
|
8229
8316
|
const cloudbase = await getManager();
|
|
8230
8317
|
const result = await cloudbase.env.bindCustomDomain({
|
|
8231
8318
|
EnvId: await resolveEnvId(),
|
|
8232
8319
|
Domain: {
|
|
8233
8320
|
Domain: input.domain,
|
|
8234
8321
|
CertId: input.certificateId,
|
|
8322
|
+
AccessType: accessType,
|
|
8323
|
+
...(input.enable !== undefined
|
|
8324
|
+
? { Enable: input.enable }
|
|
8325
|
+
: {}),
|
|
8326
|
+
...(input.customCname
|
|
8327
|
+
? { CustomCname: input.customCname }
|
|
8328
|
+
: {}),
|
|
8235
8329
|
},
|
|
8236
8330
|
});
|
|
8237
8331
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
@@ -8239,24 +8333,118 @@ function registerGatewayTools(server) {
|
|
|
8239
8333
|
action: input.action,
|
|
8240
8334
|
domain: input.domain,
|
|
8241
8335
|
certificateId: input.certificateId,
|
|
8336
|
+
accessType,
|
|
8337
|
+
...(input.customCname
|
|
8338
|
+
? { customCname: input.customCname }
|
|
8339
|
+
: {}),
|
|
8340
|
+
...(input.enable !== undefined
|
|
8341
|
+
? { enable: input.enable }
|
|
8342
|
+
: {}),
|
|
8242
8343
|
raw: result,
|
|
8243
|
-
},
|
|
8344
|
+
}, `自定义域名绑定成功(${accessType})`, [
|
|
8345
|
+
{
|
|
8346
|
+
tool: "manageGateway",
|
|
8347
|
+
action: "createRoute",
|
|
8348
|
+
reason: "绑定后需 createRoute 添加访问路径,并完成 DNS CNAME 解析后才可访问",
|
|
8349
|
+
},
|
|
8350
|
+
]);
|
|
8244
8351
|
}
|
|
8245
8352
|
case "deleteCustomDomain": {
|
|
8246
8353
|
if (!input.domain) {
|
|
8247
8354
|
throw new Error("action=deleteCustomDomain 时必须提供 domain");
|
|
8248
8355
|
}
|
|
8249
8356
|
const cloudbase = await getManager();
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8357
|
+
try {
|
|
8358
|
+
const result = await cloudbase.env.deleteCustomDomain({
|
|
8359
|
+
EnvId: await resolveEnvId(),
|
|
8360
|
+
Domain: input.domain,
|
|
8361
|
+
});
|
|
8362
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
8363
|
+
return buildEnvelope({
|
|
8364
|
+
action: input.action,
|
|
8365
|
+
domain: input.domain,
|
|
8366
|
+
raw: result,
|
|
8367
|
+
}, "自定义域名删除成功");
|
|
8368
|
+
}
|
|
8369
|
+
catch (error) {
|
|
8370
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8371
|
+
// SDK 在域名下仍有路由绑定时抛错,需先删路由再删域名
|
|
8372
|
+
if (/route binding/i.test(message)) {
|
|
8373
|
+
return {
|
|
8374
|
+
success: false,
|
|
8375
|
+
data: { action: input.action, domain: input.domain },
|
|
8376
|
+
message: `域名 ${input.domain} 下仍有路由绑定,需先删除路由再删除域名。` +
|
|
8377
|
+
`原始错误:${message}`,
|
|
8378
|
+
nextActions: [
|
|
8379
|
+
{
|
|
8380
|
+
tool: "queryGateway",
|
|
8381
|
+
action: "listRoutes",
|
|
8382
|
+
reason: "查看该域名下的路由,确认需要删除的路径",
|
|
8383
|
+
},
|
|
8384
|
+
{
|
|
8385
|
+
tool: "manageGateway",
|
|
8386
|
+
action: "deleteRoute",
|
|
8387
|
+
reason: "先删除该域名下的全部路由(逐个 deleteRoute),再重试 deleteCustomDomain",
|
|
8388
|
+
},
|
|
8389
|
+
],
|
|
8390
|
+
};
|
|
8391
|
+
}
|
|
8392
|
+
throw error;
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
8395
|
+
case "enableService": {
|
|
8396
|
+
if (typeof input.enable !== "boolean") {
|
|
8397
|
+
throw new Error('action=enableService 时必须提供 enable 参数(boolean),如 enable=true 开启 HTTP 网关总开关、enable=false 关闭;禁止省略或传非布尔值。');
|
|
8398
|
+
}
|
|
8399
|
+
const cloudbase = await getManager();
|
|
8400
|
+
const result = await cloudbase.access.switchAuth(input.enable);
|
|
8401
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
8402
|
+
const serviceText = input.enable ? "开启" : "关闭";
|
|
8403
|
+
return buildEnvelope({
|
|
8404
|
+
action: input.action,
|
|
8405
|
+
enable: input.enable,
|
|
8406
|
+
raw: result,
|
|
8407
|
+
}, `HTTP 网关总开关${serviceText}成功`, [
|
|
8408
|
+
{
|
|
8409
|
+
tool: "queryGateway",
|
|
8410
|
+
action: "getPrivilege",
|
|
8411
|
+
reason: "复核 HTTP 网关总开关与访问鉴权状态",
|
|
8412
|
+
},
|
|
8413
|
+
]);
|
|
8414
|
+
}
|
|
8415
|
+
case "authSwitch": {
|
|
8416
|
+
if (typeof input.enable !== "boolean") {
|
|
8417
|
+
throw new Error('action=authSwitch 时必须提供 enable 参数(boolean),如 enable=true 开启访问鉴权、enable=false 关闭;禁止省略或传非布尔值。');
|
|
8418
|
+
}
|
|
8419
|
+
const cloudbase = await getManager();
|
|
8420
|
+
const result = await cloudbase
|
|
8421
|
+
.commonService("tcb", "2018-06-08")
|
|
8422
|
+
.call({
|
|
8423
|
+
Action: "ModifyCloudBaseGWPrivilege",
|
|
8424
|
+
Param: {
|
|
8425
|
+
ServiceId: await resolveEnvId(),
|
|
8426
|
+
EnableService: input.enable,
|
|
8427
|
+
Options: [
|
|
8428
|
+
{
|
|
8429
|
+
Key: "authswitch",
|
|
8430
|
+
Value: input.enable ? "true" : "false",
|
|
8431
|
+
},
|
|
8432
|
+
],
|
|
8433
|
+
},
|
|
8253
8434
|
});
|
|
8254
8435
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
8436
|
+
const serviceText = input.enable ? "开启" : "关闭";
|
|
8255
8437
|
return buildEnvelope({
|
|
8256
8438
|
action: input.action,
|
|
8257
|
-
|
|
8439
|
+
enable: input.enable,
|
|
8258
8440
|
raw: result,
|
|
8259
|
-
},
|
|
8441
|
+
}, `HTTP 访问服务鉴权${serviceText}成功`, [
|
|
8442
|
+
{
|
|
8443
|
+
tool: "queryGateway",
|
|
8444
|
+
action: "getPrivilege",
|
|
8445
|
+
reason: "复核 HTTP 网关总开关与访问鉴权状态",
|
|
8446
|
+
},
|
|
8447
|
+
]);
|
|
8260
8448
|
}
|
|
8261
8449
|
default:
|
|
8262
8450
|
throw new Error(`不支持的操作类型: ${input.action}`);
|
|
@@ -8266,12 +8454,14 @@ function registerGatewayTools(server) {
|
|
|
8266
8454
|
title: "查询 CloudBase 网关",
|
|
8267
8455
|
description: "CloudBase HTTP 网关统一只读入口(Domain/Route)。查询域名下路径路由及其上游:" +
|
|
8268
8456
|
"WEB_SCF/SCF=云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
|
|
8269
|
-
"主键为 Domain + Path;listRoutes / getRoute / listCustomDomains。" +
|
|
8457
|
+
"主键为 Domain + Path;listRoutes / getRoute / listCustomDomains / getPrivilege。" +
|
|
8458
|
+
"getPrivilege 查询 HTTP 网关总开关(enableService)与访问鉴权(enableAuth)状态。" +
|
|
8270
8459
|
"实现自定义域名访问前,先 listCustomDomains:若已有自定义域名,优先 createRoute 挂路由(无需证书 ID);仅在没有可用自定义域名时才 bindCustomDomain。",
|
|
8271
8460
|
inputSchema: {
|
|
8272
8461
|
action: zod_1.z
|
|
8273
8462
|
.enum(QUERY_GATEWAY_ACTIONS)
|
|
8274
|
-
.describe("只读操作类型:listRoutes、getRoute、listCustomDomains。" +
|
|
8463
|
+
.describe("只读操作类型:listRoutes、getRoute、listCustomDomains、getPrivilege。" +
|
|
8464
|
+
"getPrivilege 无需其他参数,直接返回 HTTP 网关总开关与访问鉴权状态。" +
|
|
8275
8465
|
"自定义域名访问场景先 listCustomDomains 确认是否已有域名可复用。"),
|
|
8276
8466
|
targetName: zod_1.z
|
|
8277
8467
|
.string()
|
|
@@ -8304,14 +8494,17 @@ function registerGatewayTools(server) {
|
|
|
8304
8494
|
"createRoute 只建网关入口,不改上游权限。" +
|
|
8305
8495
|
"enablePathTransmission:默认 false 剥触发路径前缀;true 透传完整路径(CBR 多路由、WEB_SCF 自管子路径常需 true;STATIC_STORE 自定义触发路径映射站点根通常 false)。" +
|
|
8306
8496
|
"⚠️ 自定义域名访问:若环境已有自定义域名(先 queryGateway listCustomDomains),优先 createRoute 并显式传入该 domain,无需 certificateId;" +
|
|
8307
|
-
"仅首次绑定全新自定义域名时用 bindCustomDomain(需 certificateId)。CORS/安全域名用 envDomainManagement。"
|
|
8497
|
+
"仅首次绑定全新自定义域名时用 bindCustomDomain(需 certificateId)。CORS/安全域名用 envDomainManagement。" +
|
|
8498
|
+
"enableService/authSwitch:HTTP 网关总开关与访问鉴权开关;createRoute 后若访问报 HTTPSERVICE_NONACTIVATED,通常是总开关未开启(用 queryGateway getPrivilege 查询、enableService 开启)。",
|
|
8308
8499
|
inputSchema: {
|
|
8309
8500
|
action: zod_1.z
|
|
8310
8501
|
.enum(MANAGE_GATEWAY_ACTIONS)
|
|
8311
|
-
.describe("写操作:createRoute/updateRoute/deleteRoute 管理路由;bindCustomDomain/deleteCustomDomain
|
|
8502
|
+
.describe("写操作:createRoute/updateRoute/deleteRoute 管理路由;bindCustomDomain/deleteCustomDomain 管理自定义域名;" +
|
|
8503
|
+
"enableService/authSwitch 开关 HTTP 网关总开关与访问鉴权(需配合 enable 参数)。" +
|
|
8312
8504
|
"createRoute/updateRoute 必须提供 upstreamResourceType。" +
|
|
8313
8505
|
"已有自定义域名时优先 createRoute(domain=已有域名) 实现访问,不必再次 bindCustomDomain / 传入 certificateId;" +
|
|
8314
|
-
"bindCustomDomain
|
|
8506
|
+
"bindCustomDomain 仅用于首次绑定新域名(需 certificateId;可选 accessType=DIRECT|CDN|CUSTOM,CUSTOM 需 customCname;普通场景用默认 DIRECT)。" +
|
|
8507
|
+
"接入说明:https://docs.cloudbase.net/service/custom-domain"),
|
|
8315
8508
|
targetName: zod_1.z
|
|
8316
8509
|
.string()
|
|
8317
8510
|
.optional()
|
|
@@ -8374,6 +8567,24 @@ function registerGatewayTools(server) {
|
|
|
8374
8567
|
.optional()
|
|
8375
8568
|
.describe("证书 ID。仅首次 bindCustomDomain 必填。" +
|
|
8376
8569
|
"在已有自定义域名上 createRoute / updateRoute / deleteRoute 不需要 certificateId。"),
|
|
8570
|
+
accessType: zod_1.z
|
|
8571
|
+
.enum(["DIRECT", "CDN", "CUSTOM"])
|
|
8572
|
+
.optional()
|
|
8573
|
+
.describe("绑定类型(仅 bindCustomDomain,默认 DIRECT)。" +
|
|
8574
|
+
"DIRECT=直连(普通绑域名用这个);CDN=云开发 CDN;CUSTOM=自有 CDN/WAF(需 customCname)。" +
|
|
8575
|
+
"详见 https://docs.cloudbase.net/service/custom-domain"),
|
|
8576
|
+
customCname: zod_1.z
|
|
8577
|
+
.string()
|
|
8578
|
+
.optional()
|
|
8579
|
+
.describe("自有 CDN/WAF 的回源/回填地址(仅 bindCustomDomain 且 accessType=CUSTOM)。" +
|
|
8580
|
+
"不是 DNS 里用户域名要解析到的那个 CNAME;DIRECT/CDN 不要传。" +
|
|
8581
|
+
"详见 https://docs.cloudbase.net/service/custom-domain"),
|
|
8582
|
+
enable: zod_1.z
|
|
8583
|
+
.boolean()
|
|
8584
|
+
.optional()
|
|
8585
|
+
.describe("开关目标状态(enableService / authSwitch 必填):enable=true 开启,enable=false 关闭。" +
|
|
8586
|
+
"bindCustomDomain 可选:enable=false 表示绑定后禁用域名(默认启用)。" +
|
|
8587
|
+
"省略或非布尔值会返回参数错误。"),
|
|
8377
8588
|
},
|
|
8378
8589
|
annotations: {
|
|
8379
8590
|
readOnlyHint: false,
|
|
@@ -10410,6 +10621,22 @@ function buildLoginConfigNextStep(loginMethods) {
|
|
|
10410
10621
|
},
|
|
10411
10622
|
};
|
|
10412
10623
|
}
|
|
10624
|
+
/**
|
|
10625
|
+
* 短信验证码默认通道提示:开启 phone 登录后即可用云开发默认短信通道收发验证码,
|
|
10626
|
+
* 不需要配置短信签名/模板/自定义 Provider。仅当需要自定义模板、签名或更换短信服务商时,
|
|
10627
|
+
* 才需要配置 SmsVerificationConfig / 自定义短信通道。
|
|
10628
|
+
*/
|
|
10629
|
+
function buildSmsHint(loginMethods) {
|
|
10630
|
+
if (!loginMethods.phone) {
|
|
10631
|
+
return undefined;
|
|
10632
|
+
}
|
|
10633
|
+
return {
|
|
10634
|
+
defaultChannelReady: true,
|
|
10635
|
+
message: "短信验证码登录已开启,可直接使用云开发默认短信通道收发验证码,无需配置短信签名/模板/自定义 Provider。" +
|
|
10636
|
+
"前端调用 auth.getVerification({ phone_number }) 发送验证码、auth.signInWithSms({ verificationInfo, verificationCode, phoneNum }) 登录。" +
|
|
10637
|
+
"仅当需要自定义短信模板/签名或更换短信服务商时,才需要配置 SmsVerificationConfig 或接入自定义短信通道。",
|
|
10638
|
+
};
|
|
10639
|
+
}
|
|
10413
10640
|
function extractProviders(value) {
|
|
10414
10641
|
const payload = normalizePlainObject(value, "providersResult");
|
|
10415
10642
|
const providers = payload?.Providers ?? payload?.ProviderList ?? payload?.Data ?? [];
|
|
@@ -10590,12 +10817,14 @@ function registerAppAuthTools(server) {
|
|
|
10590
10817
|
const loginMethods = buildLoginMethods(result);
|
|
10591
10818
|
const nextStep = buildLoginConfigNextStep(loginMethods);
|
|
10592
10819
|
const webSdkHint = buildWebSdkHint(loginMethods);
|
|
10820
|
+
const smsHint = buildSmsHint(loginMethods);
|
|
10593
10821
|
return buildSupabaseLikeAuthResponse({
|
|
10594
10822
|
success: true,
|
|
10595
10823
|
envId,
|
|
10596
10824
|
loginMethods,
|
|
10597
10825
|
...(nextStep ? { next_step: nextStep } : {}),
|
|
10598
10826
|
...(webSdkHint ? { webSdkHint } : {}),
|
|
10827
|
+
...(smsHint ? { smsHint } : {}),
|
|
10599
10828
|
});
|
|
10600
10829
|
}
|
|
10601
10830
|
case "listProviders": {
|
|
@@ -10671,7 +10900,7 @@ function registerAppAuthTools(server) {
|
|
|
10671
10900
|
}));
|
|
10672
10901
|
server.registerTool?.("manageAppAuth", {
|
|
10673
10902
|
title: "管理 CloudBase 应用认证配置",
|
|
10674
|
-
description: "CloudBase 应用侧认证配置写入口。用于修改登录方式、provider、client 配置,确保 publishable key,以及创建或删除 API key、自定义登录密钥。⚠️ 本工具为管理端配置工具,不执行用户登录。当任务要求编写客户端登录代码时(例如「用 JS SDK 登录」),应先通过本工具完成配置(如启用 usernamePassword、获取 publishable key),再在项目代码中编写 @cloudbase/js-sdk 客户端登录代码(如 auth.signInWithPassword()),而非使用本工具完成登录。若前端要接受普通用户名样式标识符,应先执行 action=patchLoginStrategy 并传入 patch={ usernamePassword: true },再实现对应前端登录逻辑。⚠️ action=createApiKey 返回体中的 created 字段表示是否真正新建:created=false 说明复用了环境中已存在的 key,此时 keyName/expireIn 入参不会生效,返回的 keyName/expireAt 均为服务端真实值,并会附带 warnings,切勿把它当作临时凭证分发。",
|
|
10903
|
+
description: "CloudBase 应用侧认证配置写入口。用于修改登录方式、provider、client 配置,确保 publishable key,以及创建或删除 API key、自定义登录密钥。⚠️ 本工具为管理端配置工具,不执行用户登录。当任务要求编写客户端登录代码时(例如「用 JS SDK 登录」),应先通过本工具完成配置(如启用 usernamePassword、获取 publishable key),再在项目代码中编写 @cloudbase/js-sdk 客户端登录代码(如 auth.signInWithPassword()),而非使用本工具完成登录。若前端要接受普通用户名样式标识符,应先执行 action=patchLoginStrategy 并传入 patch={ usernamePassword: true },再实现对应前端登录逻辑。⚠️ 短信验证码登录(patch={ phone: true })使用云开发默认短信通道,开启后即可收发验证码,不需要配置短信签名/模板/自定义 Provider;仅当需要自定义模板/签名或更换短信服务商时才需配置 SmsVerificationConfig 或自定义短信通道。⚠️ action=createApiKey 返回体中的 created 字段表示是否真正新建:created=false 说明复用了环境中已存在的 key,此时 keyName/expireIn 入参不会生效,返回的 keyName/expireAt 均为服务端真实值,并会附带 warnings,切勿把它当作临时凭证分发。",
|
|
10675
10904
|
inputSchema: {
|
|
10676
10905
|
action: zod_1.z.enum(MANAGE_APP_AUTH_ACTIONS),
|
|
10677
10906
|
patch: zod_1.z
|
|
@@ -10737,12 +10966,14 @@ function registerAppAuthTools(server) {
|
|
|
10737
10966
|
const loginMethods = buildLoginMethods(confirmed);
|
|
10738
10967
|
const nextStep = buildLoginConfigNextStep(loginMethods);
|
|
10739
10968
|
const webSdkHint = buildWebSdkHint(loginMethods);
|
|
10969
|
+
const smsHint = buildSmsHint(loginMethods);
|
|
10740
10970
|
return buildSupabaseLikeAuthResponse({
|
|
10741
10971
|
success: true,
|
|
10742
10972
|
envId,
|
|
10743
10973
|
loginMethods,
|
|
10744
10974
|
...(nextStep ? { next_step: nextStep } : {}),
|
|
10745
10975
|
...(webSdkHint ? { webSdkHint } : {}),
|
|
10976
|
+
...(smsHint ? { smsHint } : {}),
|
|
10746
10977
|
});
|
|
10747
10978
|
}
|
|
10748
10979
|
case "addProvider": {
|
|
@@ -10977,7 +11208,7 @@ class TelemetryReporter {
|
|
|
10977
11208
|
const nodeVersion = process.version; // Node.js版本
|
|
10978
11209
|
const arch = os_1.default.arch(); // 系统架构
|
|
10979
11210
|
// 从构建时注入的版本号获取MCP版本信息
|
|
10980
|
-
const mcpVersion = process.env.npm_package_version || "2.25.
|
|
11211
|
+
const mcpVersion = process.env.npm_package_version || "2.25.5" || 0;
|
|
10981
11212
|
return {
|
|
10982
11213
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
10983
11214
|
deviceId: this.deviceId,
|
|
@@ -11489,7 +11720,7 @@ const TRIGGER_SCHEMA = zod_1.z.object({
|
|
|
11489
11720
|
const CREATE_FUNCTION_SCHEMA = zod_1.z.object({
|
|
11490
11721
|
name: zod_1.z.string().describe("函数名称"),
|
|
11491
11722
|
type: zod_1.z.enum(["Event", "HTTP"]).optional().describe("函数类型"),
|
|
11492
|
-
protocolType: zod_1.z.enum(["
|
|
11723
|
+
protocolType: zod_1.z.enum(["WS"]).optional().describe("HTTP 函数访问协议,当前仅支持 WebSockets,取值为 WS(配合 protocolParams.wsParams 使用)。普通 HTTP 函数不要传此字段;传其他值(如 HTTP)会报 InvalidParameterValue.ProtocolType。"),
|
|
11493
11724
|
protocolParams: zod_1.z
|
|
11494
11725
|
.object({
|
|
11495
11726
|
wsParams: zod_1.z
|
|
@@ -12987,6 +13218,14 @@ function collectMigrationVersions(value, found = new Set()) {
|
|
|
12987
13218
|
/** Brief retries before concluding a pushed migration never landed (Push can be async). */
|
|
12988
13219
|
const MIGRATION_VERIFY_ATTEMPTS = 4;
|
|
12989
13220
|
const MIGRATION_VERIFY_DELAY_MS = 1500;
|
|
13221
|
+
/** Page size when hydrating remote migration history into a Push/Preview payload. */
|
|
13222
|
+
const MIGRATION_LIST_PAGE_SIZE = 100;
|
|
13223
|
+
/**
|
|
13224
|
+
* PushPGUserMigrations is async: poll DescribeTaskResult until Succeed/Failed.
|
|
13225
|
+
* Aligns with CLI `tcb db pg migration up` and Manager SDK docs.
|
|
13226
|
+
*/
|
|
13227
|
+
const MIGRATION_TASK_POLL_INTERVAL_MS = 1500;
|
|
13228
|
+
const MIGRATION_TASK_MAX_WAIT_MS = 90_000;
|
|
12990
13229
|
/**
|
|
12991
13230
|
* Confirm a pushed migration version is present in the remote migration history.
|
|
12992
13231
|
*
|
|
@@ -13020,6 +13259,99 @@ async function verifyMigrationApplied(context, version, cloudBaseOptions) {
|
|
|
13020
13259
|
}
|
|
13021
13260
|
return { applied: null, error: lastError };
|
|
13022
13261
|
}
|
|
13262
|
+
function extractMigrationSummaries(history) {
|
|
13263
|
+
const raw = history.Migrations;
|
|
13264
|
+
if (!Array.isArray(raw)) {
|
|
13265
|
+
return [];
|
|
13266
|
+
}
|
|
13267
|
+
const summaries = [];
|
|
13268
|
+
for (const item of raw) {
|
|
13269
|
+
if (!item || typeof item !== "object") {
|
|
13270
|
+
continue;
|
|
13271
|
+
}
|
|
13272
|
+
const row = item;
|
|
13273
|
+
const version = typeof row.Version === "string" ? row.Version.trim() : "";
|
|
13274
|
+
const name = typeof row.Name === "string" ? row.Name.trim() : "";
|
|
13275
|
+
if (version) {
|
|
13276
|
+
summaries.push({ Version: version, Name: name || version });
|
|
13277
|
+
}
|
|
13278
|
+
}
|
|
13279
|
+
return summaries;
|
|
13280
|
+
}
|
|
13281
|
+
/**
|
|
13282
|
+
* List every remote migration summary (paginated). Required before Push/Preview:
|
|
13283
|
+
* submitting only the pending migration while remote history exists yields
|
|
13284
|
+
* Executable=false with Conflicts reason remote_history_not_found_locally, and the
|
|
13285
|
+
* async task fails with "migration plan is not executable".
|
|
13286
|
+
*/
|
|
13287
|
+
async function listAllRemoteMigrationSummaries(context, cloudBaseOptions) {
|
|
13288
|
+
const summaries = [];
|
|
13289
|
+
let offset = 0;
|
|
13290
|
+
let latestVersion = "";
|
|
13291
|
+
let total = 0;
|
|
13292
|
+
while (true) {
|
|
13293
|
+
const page = await callPgMigrationApi(context, "ListPGUserMigrations", { Limit: MIGRATION_LIST_PAGE_SIZE, Offset: offset }, cloudBaseOptions);
|
|
13294
|
+
const pageItems = extractMigrationSummaries(page);
|
|
13295
|
+
summaries.push(...pageItems);
|
|
13296
|
+
if (typeof page.LatestVersion === "string" && page.LatestVersion.trim()) {
|
|
13297
|
+
latestVersion = page.LatestVersion.trim();
|
|
13298
|
+
}
|
|
13299
|
+
if (typeof page.Total === "number" && Number.isFinite(page.Total)) {
|
|
13300
|
+
total = page.Total;
|
|
13301
|
+
}
|
|
13302
|
+
else {
|
|
13303
|
+
total = summaries.length;
|
|
13304
|
+
}
|
|
13305
|
+
if (pageItems.length < MIGRATION_LIST_PAGE_SIZE) {
|
|
13306
|
+
break;
|
|
13307
|
+
}
|
|
13308
|
+
offset += MIGRATION_LIST_PAGE_SIZE;
|
|
13309
|
+
}
|
|
13310
|
+
return { summaries, latestVersion, total };
|
|
13311
|
+
}
|
|
13312
|
+
/**
|
|
13313
|
+
* Build Push/Preview Migrations = remote history Queries + the pending migration.
|
|
13314
|
+
* Matches CLI behavior (submit full set; server skips already-applied versions).
|
|
13315
|
+
*/
|
|
13316
|
+
async function buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions) {
|
|
13317
|
+
const { summaries, latestVersion } = await listAllRemoteMigrationSummaries(context, cloudBaseOptions);
|
|
13318
|
+
const migrations = [];
|
|
13319
|
+
for (const summary of summaries) {
|
|
13320
|
+
if (summary.Version === pending.Version) {
|
|
13321
|
+
// Caller supplies the authoritative SQL for this version.
|
|
13322
|
+
continue;
|
|
13323
|
+
}
|
|
13324
|
+
const detail = await callPgMigrationApi(context, "DescribePGUserMigration", { MigrationVersion: summary.Version }, cloudBaseOptions);
|
|
13325
|
+
const query = typeof detail.Query === "string" ? detail.Query : "";
|
|
13326
|
+
if (!query.trim()) {
|
|
13327
|
+
throw new Error(`DescribePGUserMigration returned empty Query for version=${summary.Version} (${summary.Name}). ` +
|
|
13328
|
+
"Cannot hydrate remote history for PushPGUserMigrations.");
|
|
13329
|
+
}
|
|
13330
|
+
migrations.push({
|
|
13331
|
+
Version: typeof detail.Version === "string" && detail.Version.trim() ? detail.Version.trim() : summary.Version,
|
|
13332
|
+
Name: typeof detail.Name === "string" && detail.Name.trim() ? detail.Name.trim() : summary.Name,
|
|
13333
|
+
Query: query,
|
|
13334
|
+
});
|
|
13335
|
+
}
|
|
13336
|
+
migrations.push(pending);
|
|
13337
|
+
return { migrations, remoteCount: summaries.length, latestVersion };
|
|
13338
|
+
}
|
|
13339
|
+
async function waitPgMigrationTask(context, taskId, cloudBaseOptions) {
|
|
13340
|
+
const deadline = Date.now() + MIGRATION_TASK_MAX_WAIT_MS;
|
|
13341
|
+
let last = { TaskId: taskId };
|
|
13342
|
+
while (Date.now() <= deadline) {
|
|
13343
|
+
last = (await callPgMigrationApi(context, "DescribeTaskResult", { TaskId: taskId }, cloudBaseOptions));
|
|
13344
|
+
const status = String(last.Status || "").toLowerCase();
|
|
13345
|
+
if (status === "succeed" || status === "failed") {
|
|
13346
|
+
return last;
|
|
13347
|
+
}
|
|
13348
|
+
await sleep(MIGRATION_TASK_POLL_INTERVAL_MS);
|
|
13349
|
+
}
|
|
13350
|
+
const error = new Error(`DescribeTaskResult timed out after ${Math.floor(MIGRATION_TASK_MAX_WAIT_MS / 1000)}s ` +
|
|
13351
|
+
`for TaskId=${taskId} (last Status=${last.Status || "-"}, Phase=${last.Phase || "-"}).`);
|
|
13352
|
+
error.lastTask = last;
|
|
13353
|
+
throw error;
|
|
13354
|
+
}
|
|
13023
13355
|
const MIGRATION_VERSION_REQUIRED_MESSAGE = "migrationVersion is required (14-digit UTC timestamp YYYYMMDDHHMMSS). " +
|
|
13024
13356
|
"Decide the version first, write local file migrations/<version>_<migrationName>.sql, " +
|
|
13025
13357
|
"then call planMigration/applyMigration with the same migrationVersion and migrationName.";
|
|
@@ -13806,29 +14138,38 @@ async function handlePlanMigration(args, context, deps, cloudBaseOptions) {
|
|
|
13806
14138
|
}
|
|
13807
14139
|
const version = versionOrError;
|
|
13808
14140
|
const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
|
|
13809
|
-
const
|
|
14141
|
+
const pending = {
|
|
13810
14142
|
Version: version,
|
|
13811
14143
|
Name: args.migrationName,
|
|
13812
14144
|
Query: args.sql,
|
|
13813
14145
|
};
|
|
13814
14146
|
if (args.rollbackSql?.trim()) {
|
|
13815
|
-
|
|
14147
|
+
pending.Rollback = args.rollbackSql;
|
|
13816
14148
|
}
|
|
13817
14149
|
try {
|
|
14150
|
+
const hydrated = await buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions);
|
|
13818
14151
|
const result = await callPgMigrationApi(context, "PreviewPGUserMigrations", {
|
|
13819
|
-
Migrations:
|
|
14152
|
+
Migrations: hydrated.migrations,
|
|
13820
14153
|
}, cloudBaseOptions);
|
|
14154
|
+
const executable = result.Executable === true;
|
|
13821
14155
|
return buildPgToolResult({
|
|
13822
14156
|
success: true,
|
|
13823
14157
|
data: {
|
|
13824
14158
|
migrationVersion: version,
|
|
13825
14159
|
migrationName: args.migrationName,
|
|
13826
14160
|
localFileHint,
|
|
14161
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14162
|
+
latestRemoteVersion: hydrated.latestVersion || null,
|
|
14163
|
+
executable,
|
|
13827
14164
|
apiResult: result,
|
|
13828
14165
|
},
|
|
13829
|
-
message:
|
|
14166
|
+
message: executable
|
|
14167
|
+
? `Migration plan generated via PreviewPGUserMigrations (hydrated ${hydrated.remoteCount} remote migration(s)). Reuse migrationVersion=${version} on applyMigration. Ensure local file ${localFileHint} exists and matches.`
|
|
14168
|
+
: `Migration plan is NOT executable (PreviewPGUserMigrations.Executable=false after hydrating ${hydrated.remoteCount} remote migration(s)). Inspect apiResult.Conflicts before applyMigration. Common causes: version older than latest remote (${hydrated.latestVersion || "unknown"}), or checksum mismatch.`,
|
|
13830
14169
|
nextActions: [
|
|
13831
|
-
buildNextAction(MANAGE_PG_DATABASE, "applyMigration",
|
|
14170
|
+
buildNextAction(MANAGE_PG_DATABASE, "applyMigration", executable
|
|
14171
|
+
? "Review the plan above. If it looks correct, call applyMigration with the same migrationVersion."
|
|
14172
|
+
: "Resolve Conflicts (often pick a migrationVersion newer than LatestVersion), then retry planMigration/applyMigration.", {
|
|
13832
14173
|
action: "applyMigration",
|
|
13833
14174
|
migrationName: args.migrationName,
|
|
13834
14175
|
migrationVersion: version,
|
|
@@ -13875,26 +14216,118 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
13875
14216
|
}
|
|
13876
14217
|
const version = versionOrError;
|
|
13877
14218
|
const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
|
|
13878
|
-
const
|
|
14219
|
+
const pending = {
|
|
13879
14220
|
Version: version,
|
|
13880
14221
|
Name: args.migrationName,
|
|
13881
14222
|
Query: args.sql,
|
|
13882
14223
|
};
|
|
13883
14224
|
if (args.rollbackSql?.trim()) {
|
|
13884
|
-
|
|
13885
|
-
}
|
|
13886
|
-
const params = { Migrations: [migration] };
|
|
13887
|
-
if (args.lockTimeoutMs !== undefined) {
|
|
13888
|
-
params.LockTimeoutMs = args.lockTimeoutMs;
|
|
13889
|
-
}
|
|
13890
|
-
if (args.statementTimeoutMs !== undefined) {
|
|
13891
|
-
params.StatementTimeoutMs = args.statementTimeoutMs;
|
|
14225
|
+
pending.Rollback = args.rollbackSql;
|
|
13892
14226
|
}
|
|
13893
14227
|
try {
|
|
14228
|
+
// Pushing only the pending migration while remote history exists makes the plan
|
|
14229
|
+
// non-executable (remote_history_not_found_locally). Hydrate remote Queries first,
|
|
14230
|
+
// matching CLI `tcb db pg migration up` (full list; server skips applied).
|
|
14231
|
+
const hydrated = await buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions);
|
|
14232
|
+
const preview = await callPgMigrationApi(context, "PreviewPGUserMigrations", { Migrations: hydrated.migrations }, cloudBaseOptions);
|
|
14233
|
+
if (preview.Executable !== true) {
|
|
14234
|
+
return buildPgToolResult({
|
|
14235
|
+
success: false,
|
|
14236
|
+
errorCode: "MIGRATION_NOT_EXECUTABLE",
|
|
14237
|
+
data: {
|
|
14238
|
+
migrationVersion: version,
|
|
14239
|
+
migrationName: args.migrationName,
|
|
14240
|
+
localFileHint,
|
|
14241
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14242
|
+
latestRemoteVersion: hydrated.latestVersion || null,
|
|
14243
|
+
previewResult: preview,
|
|
14244
|
+
verified: false,
|
|
14245
|
+
},
|
|
14246
|
+
message: `PreviewPGUserMigrations reported Executable=false for migrationVersion=${version} ` +
|
|
14247
|
+
`(latest remote=${hydrated.latestVersion || "unknown"}, hydratedRemoteCount=${hydrated.remoteCount}). ` +
|
|
14248
|
+
"Push was NOT submitted. Inspect previewResult.Conflicts — common reasons: " +
|
|
14249
|
+
"local_migration_before_latest_remote (pick a newer 14-digit version) or checksum_mismatch. " +
|
|
14250
|
+
"If the schema change is urgent you may fall back to action=execute with allowDdlViaExecute=true, but that bypasses migration history.",
|
|
14251
|
+
nextActions: [
|
|
14252
|
+
buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Check LatestVersion and pick a migrationVersion strictly newer than it.", { action: "listMigrations", limit: 20 }),
|
|
14253
|
+
buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Re-run planMigration after adjusting migrationVersion/SQL.", {
|
|
14254
|
+
action: "planMigration",
|
|
14255
|
+
migrationName: args.migrationName,
|
|
14256
|
+
migrationVersion: version,
|
|
14257
|
+
sql: args.sql,
|
|
14258
|
+
...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
|
|
14259
|
+
}),
|
|
14260
|
+
],
|
|
14261
|
+
});
|
|
14262
|
+
}
|
|
14263
|
+
const params = { Migrations: hydrated.migrations };
|
|
14264
|
+
if (args.lockTimeoutMs !== undefined) {
|
|
14265
|
+
params.LockTimeoutMs = args.lockTimeoutMs;
|
|
14266
|
+
}
|
|
14267
|
+
if (args.statementTimeoutMs !== undefined) {
|
|
14268
|
+
params.StatementTimeoutMs = args.statementTimeoutMs;
|
|
14269
|
+
}
|
|
13894
14270
|
const result = await callPgMigrationApi(context, "PushPGUserMigrations", params, cloudBaseOptions);
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
14271
|
+
const taskId = typeof result.TaskId === "string" ? result.TaskId.trim() : "";
|
|
14272
|
+
let taskResult = null;
|
|
14273
|
+
if (taskId) {
|
|
14274
|
+
try {
|
|
14275
|
+
taskResult = await waitPgMigrationTask(context, taskId, cloudBaseOptions);
|
|
14276
|
+
}
|
|
14277
|
+
catch (error) {
|
|
14278
|
+
const lastTask = error.lastTask;
|
|
14279
|
+
return buildPgToolResult({
|
|
14280
|
+
success: false,
|
|
14281
|
+
errorCode: "MIGRATION_TASK_TIMEOUT",
|
|
14282
|
+
data: {
|
|
14283
|
+
migrationVersion: version,
|
|
14284
|
+
migrationName: args.migrationName,
|
|
14285
|
+
localFileHint,
|
|
14286
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14287
|
+
apiResult: result,
|
|
14288
|
+
taskResult: lastTask ?? { TaskId: taskId },
|
|
14289
|
+
verified: null,
|
|
14290
|
+
},
|
|
14291
|
+
message: `PushPGUserMigrations returned TaskId=${taskId}, but waiting for DescribeTaskResult timed out: ` +
|
|
14292
|
+
`${error instanceof Error ? error.message : String(error)}. ` +
|
|
14293
|
+
"The migration may still be running — check action=listMigrations before retrying or falling back to execute.",
|
|
14294
|
+
nextActions: [
|
|
14295
|
+
buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Check whether the migrationVersion eventually landed.", { action: "listMigrations", limit: 20 }),
|
|
14296
|
+
],
|
|
14297
|
+
});
|
|
14298
|
+
}
|
|
14299
|
+
const status = String(taskResult.Status || "").toLowerCase();
|
|
14300
|
+
if (status === "failed") {
|
|
14301
|
+
return buildPgToolResult({
|
|
14302
|
+
success: false,
|
|
14303
|
+
errorCode: "MIGRATION_TASK_FAILED",
|
|
14304
|
+
data: {
|
|
14305
|
+
migrationVersion: version,
|
|
14306
|
+
migrationName: args.migrationName,
|
|
14307
|
+
localFileHint,
|
|
14308
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14309
|
+
apiResult: result,
|
|
14310
|
+
taskResult: taskResult,
|
|
14311
|
+
verified: false,
|
|
14312
|
+
},
|
|
14313
|
+
message: `PushPGUserMigrations TaskId=${taskId} failed at phase=${taskResult.Phase || "-"}: ` +
|
|
14314
|
+
`${taskResult.Reason || "unknown reason"}. ` +
|
|
14315
|
+
"The migration did NOT take effect. Fix Conflicts/SQL (or use a newer migrationVersion), then retry applyMigration. " +
|
|
14316
|
+
"Urgent bypass: action=execute with allowDdlViaExecute=true (skips migration history).",
|
|
14317
|
+
nextActions: [
|
|
14318
|
+
buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Preview again to inspect Conflicts before retrying apply.", {
|
|
14319
|
+
action: "planMigration",
|
|
14320
|
+
migrationName: args.migrationName,
|
|
14321
|
+
migrationVersion: version,
|
|
14322
|
+
sql: args.sql,
|
|
14323
|
+
...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
|
|
14324
|
+
}),
|
|
14325
|
+
buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Confirm the version is still absent from remote history.", { action: "listMigrations", limit: 20 }),
|
|
14326
|
+
],
|
|
14327
|
+
});
|
|
14328
|
+
}
|
|
14329
|
+
}
|
|
14330
|
+
// Task Succeed (or sync response without TaskId): confirm history records the version.
|
|
13898
14331
|
const verification = await verifyMigrationApplied(context, version, cloudBaseOptions);
|
|
13899
14332
|
if (verification.applied === false) {
|
|
13900
14333
|
return buildPgToolResult({
|
|
@@ -13904,10 +14337,12 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
13904
14337
|
migrationVersion: version,
|
|
13905
14338
|
migrationName: args.migrationName,
|
|
13906
14339
|
localFileHint,
|
|
14340
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
13907
14341
|
apiResult: result,
|
|
14342
|
+
taskResult: taskResult,
|
|
13908
14343
|
verified: false,
|
|
13909
14344
|
},
|
|
13910
|
-
message: `PushPGUserMigrations
|
|
14345
|
+
message: `PushPGUserMigrations completed (TaskId=${taskId || "none"}), but migrationVersion=${version} is not present in the remote migration history, ` +
|
|
13911
14346
|
"so the migration did NOT take effect. Do not assume the schema change is applied. " +
|
|
13912
14347
|
"Re-check the SQL and migrationVersion, inspect the migration with action=migrationDetail, and retry action=applyMigration. " +
|
|
13913
14348
|
"If the schema change is urgent you may fall back to action=execute with allowDdlViaExecute=true, but that bypasses migration history.",
|
|
@@ -13925,7 +14360,9 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
13925
14360
|
migrationVersion: version,
|
|
13926
14361
|
migrationName: args.migrationName,
|
|
13927
14362
|
localFileHint,
|
|
14363
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
13928
14364
|
apiResult: result,
|
|
14365
|
+
taskResult: taskResult,
|
|
13929
14366
|
verified: null,
|
|
13930
14367
|
},
|
|
13931
14368
|
message: `PushPGUserMigrations was submitted, but verifying migrationVersion=${version} against the remote history failed: ${verification.error}. ` +
|
|
@@ -13941,10 +14378,12 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
13941
14378
|
migrationVersion: version,
|
|
13942
14379
|
migrationName: args.migrationName,
|
|
13943
14380
|
localFileHint,
|
|
14381
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
13944
14382
|
apiResult: result,
|
|
14383
|
+
taskResult: taskResult,
|
|
13945
14384
|
verified: true,
|
|
13946
14385
|
},
|
|
13947
|
-
message: `Migrations applied via PushPGUserMigrations
|
|
14386
|
+
message: `Migrations applied via PushPGUserMigrations (hydrated ${hydrated.remoteCount} remote migration(s)), task ${taskId ? `TaskId=${taskId} ` : ""}verified present in the remote migration history. Ensure local file ${localFileHint} exists and matches.`,
|
|
13948
14387
|
});
|
|
13949
14388
|
}
|
|
13950
14389
|
catch (error) {
|
|
@@ -15459,7 +15898,7 @@ function registerSetupTools(server) {
|
|
|
15459
15898
|
server.registerTool("downloadTemplate", {
|
|
15460
15899
|
title: "下载项目模板",
|
|
15461
15900
|
description: `自动下载并部署CloudBase项目模板。
|
|
15462
|
-
**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.
|
|
15901
|
+
**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.5" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
15463
15902
|
inputSchema: {
|
|
15464
15903
|
template: zod_1.z
|
|
15465
15904
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|