@cloudbase/cloudbase-mcp 2.31.0 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +11 -11
- package/dist/index.cjs +14547 -184
- package/dist/index.d.ts +42 -0
- package/dist/index.js +953 -12
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3474,7 +3474,7 @@ ${envIdSection}
|
|
|
3474
3474
|
## 环境信息
|
|
3475
3475
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
3476
3476
|
- Node.js版本: ${process.version}
|
|
3477
|
-
- MCP 版本:${process.env.npm_package_version || ( true ? "2.
|
|
3477
|
+
- MCP 版本:${process.env.npm_package_version || ( true ? "2.32.0" : 0)}
|
|
3478
3478
|
- 系统架构: ${os_1.default.arch()}
|
|
3479
3479
|
- 时间: ${new Date().toISOString()}
|
|
3480
3480
|
- 请求ID: ${requestId}
|
|
@@ -3555,7 +3555,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
3555
3555
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
3556
3556
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
3557
3557
|
});
|
|
3558
|
-
const mcpVersion = true ? "2.
|
|
3558
|
+
const mcpVersion = true ? "2.32.0" : 0;
|
|
3559
3559
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
3560
3560
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
3561
3561
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -3728,6 +3728,7 @@ const app_auth_js_1 = __webpack_require__(5313);
|
|
|
3728
3728
|
const apps_js_1 = __webpack_require__(6817);
|
|
3729
3729
|
const logs_js_1 = __webpack_require__(2322);
|
|
3730
3730
|
const permissions_js_1 = __webpack_require__(6485);
|
|
3731
|
+
const msg_push_js_1 = __webpack_require__(7561);
|
|
3731
3732
|
const cloud_mode_js_1 = __webpack_require__(9684);
|
|
3732
3733
|
const logger_js_1 = __webpack_require__(3039);
|
|
3733
3734
|
const site_map_js_1 = __webpack_require__(8107);
|
|
@@ -3800,6 +3801,7 @@ const AVAILABLE_PLUGINS = {
|
|
|
3800
3801
|
apps: { name: "apps", register: apps_js_1.registerAppTools },
|
|
3801
3802
|
cloudrun: { name: "cloudrun", register: cloudrun_js_1.registerCloudRunTools },
|
|
3802
3803
|
capi: { name: "capi", register: capi_js_1.registerCapiTools },
|
|
3804
|
+
"msg-push": { name: "msg-push", register: msg_push_js_1.registerMsgPushTools },
|
|
3803
3805
|
};
|
|
3804
3806
|
const PLUGIN_ALIASES = {
|
|
3805
3807
|
"access-control": "permissions",
|
|
@@ -9001,6 +9003,42 @@ function normalizeAccessPath(path) {
|
|
|
9001
9003
|
}
|
|
9002
9004
|
return path.startsWith("/") ? path : `/${path}`;
|
|
9003
9005
|
}
|
|
9006
|
+
/**
|
|
9007
|
+
* Collect FAIL items from VerifyHTTPServiceRoute, mapping DNS records to
|
|
9008
|
+
* lowercase field names for MCP structured output (aligned with CLI 3.8.1).
|
|
9009
|
+
*/
|
|
9010
|
+
function collectFailedVerifyChecks(result) {
|
|
9011
|
+
const checkItems = {
|
|
9012
|
+
ownership: result.Ownership,
|
|
9013
|
+
cert: result.Cert,
|
|
9014
|
+
quota: result.Quota,
|
|
9015
|
+
routeConflict: result.RouteConflict,
|
|
9016
|
+
domainConflict: result.DomainConflict,
|
|
9017
|
+
internalAccount: result.InternalAccount,
|
|
9018
|
+
blacklist: result.Blacklist,
|
|
9019
|
+
cdnResource: result.CDNResource,
|
|
9020
|
+
eo: result.EO,
|
|
9021
|
+
};
|
|
9022
|
+
return Object.entries(checkItems)
|
|
9023
|
+
.filter(([, item]) => item?.Status === "FAIL")
|
|
9024
|
+
.map(([name, item]) => {
|
|
9025
|
+
const dnsRecords = item?.OwnershipVerification?.DnsVerification?.map(({ Subdomain, RecordType, RecordValue }) => ({
|
|
9026
|
+
subdomain: Subdomain,
|
|
9027
|
+
recordType: RecordType,
|
|
9028
|
+
recordValue: RecordValue,
|
|
9029
|
+
}));
|
|
9030
|
+
return {
|
|
9031
|
+
name,
|
|
9032
|
+
status: item.Status,
|
|
9033
|
+
...(item?.Code ? { code: item.Code } : {}),
|
|
9034
|
+
...(item?.Message ? { message: item.Message } : {}),
|
|
9035
|
+
...(dnsRecords?.length ? { dnsRecords } : {}),
|
|
9036
|
+
};
|
|
9037
|
+
});
|
|
9038
|
+
}
|
|
9039
|
+
function isDefaultHttpServiceDomain(domain) {
|
|
9040
|
+
return /(^|\.)app\.tcloudbase\.com$/i.test(domain);
|
|
9041
|
+
}
|
|
9004
9042
|
function registerGatewayTools(server) {
|
|
9005
9043
|
const cloudBaseOptions = server.cloudBaseOptions;
|
|
9006
9044
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
@@ -9016,6 +9054,129 @@ function registerGatewayTools(server) {
|
|
|
9016
9054
|
data: {},
|
|
9017
9055
|
message: error instanceof Error ? error.message : String(error),
|
|
9018
9056
|
});
|
|
9057
|
+
const verifyHttpServiceRouteOrFail = async (params) => {
|
|
9058
|
+
const cloudbase = await getManager();
|
|
9059
|
+
let result;
|
|
9060
|
+
try {
|
|
9061
|
+
result = await cloudbase.env.verifyHttpServiceRoute({
|
|
9062
|
+
EnvId: params.envId,
|
|
9063
|
+
Domain: params.domainParam,
|
|
9064
|
+
});
|
|
9065
|
+
}
|
|
9066
|
+
catch (err) {
|
|
9067
|
+
const domain = params.domainParam.Domain;
|
|
9068
|
+
if (params.allowDefaultDomainApiFallback &&
|
|
9069
|
+
domain &&
|
|
9070
|
+
isDefaultHttpServiceDomain(domain)) {
|
|
9071
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9072
|
+
server.logger?.({
|
|
9073
|
+
type: "errorToolCall",
|
|
9074
|
+
toolName: "manageGateway",
|
|
9075
|
+
message: `verifyHttpServiceRoute skipped for default domain ${domain}: ${message}`,
|
|
9076
|
+
});
|
|
9077
|
+
return null;
|
|
9078
|
+
}
|
|
9079
|
+
throw err;
|
|
9080
|
+
}
|
|
9081
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
9082
|
+
if (result.Passed !== false) {
|
|
9083
|
+
return null;
|
|
9084
|
+
}
|
|
9085
|
+
const checks = collectFailedVerifyChecks(result);
|
|
9086
|
+
const ownershipDns = checks.find((c) => c.name === "ownership")?.dnsRecords;
|
|
9087
|
+
const hasDns = Boolean(ownershipDns?.length);
|
|
9088
|
+
return {
|
|
9089
|
+
success: false,
|
|
9090
|
+
data: {
|
|
9091
|
+
action: params.action,
|
|
9092
|
+
domain: params.domainParam.Domain,
|
|
9093
|
+
checks,
|
|
9094
|
+
...(result.RequestId ? { requestId: result.RequestId } : {}),
|
|
9095
|
+
},
|
|
9096
|
+
message: hasDns
|
|
9097
|
+
? "HTTP 服务域名配置预检未通过:域名归属权校验失败。请按 data.checks 中 ownership.dnsRecords 配置 DNS TXT 记录后重试。"
|
|
9098
|
+
: "HTTP 服务域名配置预检未通过。请根据 data.checks 修复问题后重试。",
|
|
9099
|
+
nextActions: [
|
|
9100
|
+
{
|
|
9101
|
+
tool: "manageGateway",
|
|
9102
|
+
action: params.action,
|
|
9103
|
+
reason: hasDns
|
|
9104
|
+
? "配置 DNS TXT(主机记录/记录类型/记录值见 data.checks[].dnsRecords)后重新调用本 action"
|
|
9105
|
+
: "根据 data.checks 修复预检失败项后重新调用本 action",
|
|
9106
|
+
},
|
|
9107
|
+
],
|
|
9108
|
+
};
|
|
9109
|
+
};
|
|
9110
|
+
/**
|
|
9111
|
+
* Resolve SSL certificate ID for bindCustomDomain.
|
|
9112
|
+
* Explicit certificateId wins; otherwise search by domain (single → auto, multi → guidance).
|
|
9113
|
+
*/
|
|
9114
|
+
const resolveCertificateIdForBind = async (domain, certificateId) => {
|
|
9115
|
+
if (certificateId) {
|
|
9116
|
+
return { ok: true, certificateId };
|
|
9117
|
+
}
|
|
9118
|
+
const cloudbase = await getManager();
|
|
9119
|
+
let certificates = [];
|
|
9120
|
+
try {
|
|
9121
|
+
const res = await cloudbase.env.describeCertificates({
|
|
9122
|
+
SearchKey: domain,
|
|
9123
|
+
});
|
|
9124
|
+
certificates = res?.Certificates ?? [];
|
|
9125
|
+
}
|
|
9126
|
+
catch (err) {
|
|
9127
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9128
|
+
throw new Error(`获取 SSL 证书列表失败:${message}。请传入 certificateId,或确认账号有 SSL 证书服务权限。`);
|
|
9129
|
+
}
|
|
9130
|
+
if (certificates.length === 0) {
|
|
9131
|
+
return {
|
|
9132
|
+
ok: false,
|
|
9133
|
+
envelope: {
|
|
9134
|
+
success: false,
|
|
9135
|
+
data: { action: "bindCustomDomain", domain, certificates: [] },
|
|
9136
|
+
message: `未找到域名 [${domain}] 相关的可用 SSL 证书。请在腾讯云 SSL 证书控制台上传/申请后传入 certificateId 重试。`,
|
|
9137
|
+
nextActions: [
|
|
9138
|
+
{
|
|
9139
|
+
tool: "manageGateway",
|
|
9140
|
+
action: "bindCustomDomain",
|
|
9141
|
+
reason: "上传或申请匹配该域名的证书后,传入 certificateId 再调用 bindCustomDomain",
|
|
9142
|
+
},
|
|
9143
|
+
],
|
|
9144
|
+
},
|
|
9145
|
+
};
|
|
9146
|
+
}
|
|
9147
|
+
if (certificates.length === 1) {
|
|
9148
|
+
const only = certificates[0]?.CertificateId;
|
|
9149
|
+
if (!only) {
|
|
9150
|
+
throw new Error(`域名 [${domain}] 匹配到证书但缺少 CertificateId,请显式传入 certificateId。`);
|
|
9151
|
+
}
|
|
9152
|
+
return { ok: true, certificateId: only };
|
|
9153
|
+
}
|
|
9154
|
+
const options = certificates.map((cert) => ({
|
|
9155
|
+
certificateId: cert.CertificateId,
|
|
9156
|
+
domain: cert.Domain,
|
|
9157
|
+
alias: cert.Alias,
|
|
9158
|
+
certEndTime: cert.CertEndTime,
|
|
9159
|
+
}));
|
|
9160
|
+
return {
|
|
9161
|
+
ok: false,
|
|
9162
|
+
envelope: {
|
|
9163
|
+
success: false,
|
|
9164
|
+
data: {
|
|
9165
|
+
action: "bindCustomDomain",
|
|
9166
|
+
domain,
|
|
9167
|
+
certificates: options,
|
|
9168
|
+
},
|
|
9169
|
+
message: `找到 ${certificates.length} 个匹配证书,请选择其中一个 certificateId 后重试 bindCustomDomain(MCP 非交互,不会自动选择)。`,
|
|
9170
|
+
nextActions: options
|
|
9171
|
+
.filter((c) => c.certificateId)
|
|
9172
|
+
.map((c) => ({
|
|
9173
|
+
tool: "manageGateway",
|
|
9174
|
+
action: "bindCustomDomain",
|
|
9175
|
+
reason: `使用 certificateId=${c.certificateId}(domain=${c.domain ?? "-"}, 到期=${c.certEndTime ?? "-"})重试`,
|
|
9176
|
+
})),
|
|
9177
|
+
},
|
|
9178
|
+
};
|
|
9179
|
+
};
|
|
9019
9180
|
const withEnvelope = async (handler) => {
|
|
9020
9181
|
try {
|
|
9021
9182
|
return (0, json_content_js_1.jsonContent)(await handler());
|
|
@@ -9373,6 +9534,16 @@ function registerGatewayTools(server) {
|
|
|
9373
9534
|
case "createRoute": {
|
|
9374
9535
|
const cloudbase = await getManager();
|
|
9375
9536
|
const payload = await normalizeRoutePayload(input);
|
|
9537
|
+
// Probe → create: ownership / cert / quota / conflict checks before create.
|
|
9538
|
+
const verifyFailure = await verifyHttpServiceRouteOrFail({
|
|
9539
|
+
envId: payload.EnvId,
|
|
9540
|
+
domainParam: payload.Domain,
|
|
9541
|
+
action: "createRoute",
|
|
9542
|
+
allowDefaultDomainApiFallback: true,
|
|
9543
|
+
});
|
|
9544
|
+
if (verifyFailure) {
|
|
9545
|
+
return verifyFailure;
|
|
9546
|
+
}
|
|
9376
9547
|
let result;
|
|
9377
9548
|
try {
|
|
9378
9549
|
result = await cloudbase.env.createHttpServiceRoute({
|
|
@@ -9617,8 +9788,8 @@ function registerGatewayTools(server) {
|
|
|
9617
9788
|
}, "HTTP 路由删除成功");
|
|
9618
9789
|
}
|
|
9619
9790
|
case "bindCustomDomain": {
|
|
9620
|
-
if (!input.domain
|
|
9621
|
-
throw new Error("action=bindCustomDomain 时必须提供 domain
|
|
9791
|
+
if (!input.domain) {
|
|
9792
|
+
throw new Error("action=bindCustomDomain 时必须提供 domain");
|
|
9622
9793
|
}
|
|
9623
9794
|
const accessType = input.accessType ?? "DIRECT";
|
|
9624
9795
|
if (accessType === "CUSTOM" && !input.customCname) {
|
|
@@ -9629,12 +9800,34 @@ function registerGatewayTools(server) {
|
|
|
9629
9800
|
throw new Error("customCname 仅在 accessType=CUSTOM 时可用;普通绑定用默认 DIRECT,不要传 customCname。" +
|
|
9630
9801
|
"说明:https://docs.cloudbase.net/service/custom-domain");
|
|
9631
9802
|
}
|
|
9803
|
+
const envId = await resolveEnvId();
|
|
9632
9804
|
const cloudbase = await getManager();
|
|
9805
|
+
// Align CLI 3.8.1: verify before certificate resolution / bind.
|
|
9806
|
+
const verifyDomainParam = {
|
|
9807
|
+
Domain: input.domain,
|
|
9808
|
+
CertId: input.certificateId || "",
|
|
9809
|
+
AccessType: accessType,
|
|
9810
|
+
Enable: input.enable !== undefined ? input.enable : true,
|
|
9811
|
+
...(input.customCname ? { CustomCname: input.customCname } : {}),
|
|
9812
|
+
};
|
|
9813
|
+
const verifyFailure = await verifyHttpServiceRouteOrFail({
|
|
9814
|
+
envId,
|
|
9815
|
+
domainParam: verifyDomainParam,
|
|
9816
|
+
action: "bindCustomDomain",
|
|
9817
|
+
});
|
|
9818
|
+
if (verifyFailure) {
|
|
9819
|
+
return verifyFailure;
|
|
9820
|
+
}
|
|
9821
|
+
const certResolved = await resolveCertificateIdForBind(input.domain, input.certificateId);
|
|
9822
|
+
if (!certResolved.ok) {
|
|
9823
|
+
return certResolved.envelope;
|
|
9824
|
+
}
|
|
9825
|
+
const certificateId = certResolved.certificateId;
|
|
9633
9826
|
const result = await cloudbase.env.bindCustomDomain({
|
|
9634
|
-
EnvId:
|
|
9827
|
+
EnvId: envId,
|
|
9635
9828
|
Domain: {
|
|
9636
9829
|
Domain: input.domain,
|
|
9637
|
-
CertId:
|
|
9830
|
+
CertId: certificateId,
|
|
9638
9831
|
AccessType: accessType,
|
|
9639
9832
|
...(input.enable !== undefined
|
|
9640
9833
|
? { Enable: input.enable }
|
|
@@ -9648,7 +9841,7 @@ function registerGatewayTools(server) {
|
|
|
9648
9841
|
return buildEnvelope({
|
|
9649
9842
|
action: input.action,
|
|
9650
9843
|
domain: input.domain,
|
|
9651
|
-
certificateId
|
|
9844
|
+
certificateId,
|
|
9652
9845
|
accessType,
|
|
9653
9846
|
...(input.customCname
|
|
9654
9847
|
? { customCname: input.customCname }
|
|
@@ -9813,7 +10006,8 @@ function registerGatewayTools(server) {
|
|
|
9813
10006
|
"createRoute 只建网关入口,不改上游权限。" +
|
|
9814
10007
|
"enablePathTransmission:默认 false 剥触发路径前缀;true 透传完整路径(CBR 多路由、WEB_SCF 自管子路径常需 true;STATIC_STORE 自定义触发路径映射站点根通常 false)。" +
|
|
9815
10008
|
"⚠️ 自定义域名访问:若环境已有自定义域名(先 queryGateway listCustomDomains),优先 createRoute 并显式传入该 domain,无需 certificateId;" +
|
|
9816
|
-
"仅首次绑定全新自定义域名时用 bindCustomDomain
|
|
10009
|
+
"仅首次绑定全新自定义域名时用 bindCustomDomain(certificateId 可选:未传时按域名自动检索证书,单证书自动选用、多证书返回选择指引)。" +
|
|
10010
|
+
"createRoute / bindCustomDomain 创建前会调用 VerifyHTTPServiceRoute 做归属权等预检(探测→创建);失败时返回 data.checks 与 DNS TXT 指引,配置后重试。CORS/安全域名用 envDomainManagement。" +
|
|
9817
10011
|
"enableService/authSwitch:HTTP 网关总开关与访问鉴权开关;createRoute 后若访问报 HTTPSERVICE_NONACTIVATED,通常是总开关未开启(用 queryGateway getPrivilege 查询、enableService 开启)。",
|
|
9818
10012
|
inputSchema: {
|
|
9819
10013
|
action: zod_1.z
|
|
@@ -9825,7 +10019,8 @@ function registerGatewayTools(server) {
|
|
|
9825
10019
|
"updateRoute 也可传 enable/route.enable 直接改 Routes[].Enable。" +
|
|
9826
10020
|
"关闭 *.tcloudbaseapp.com 默认静态托管域:disableRoute + domain=该 STATIC_STORE IsDefault 域名 + path=\"/\"。" +
|
|
9827
10021
|
"已有自定义域名时优先 createRoute(domain=已有域名) 实现访问,不必再次 bindCustomDomain / 传入 certificateId;" +
|
|
9828
|
-
"bindCustomDomain
|
|
10022
|
+
"bindCustomDomain 仅用于首次绑定新域名(certificateId 可选,未传则按域名自动检索;可选 accessType=DIRECT|CDN|CUSTOM,CUSTOM 需 customCname;普通场景用默认 DIRECT)。" +
|
|
10023
|
+
"createRoute/bindCustomDomain 创建前会 VerifyHTTPServiceRoute 预检;失败时按返回的 DNS TXT 指引配置后重试。" +
|
|
9829
10024
|
"接入说明:https://docs.cloudbase.net/service/custom-domain"),
|
|
9830
10025
|
targetName: zod_1.z
|
|
9831
10026
|
.string()
|
|
@@ -9896,7 +10091,9 @@ function registerGatewayTools(server) {
|
|
|
9896
10091
|
certificateId: zod_1.z
|
|
9897
10092
|
.string()
|
|
9898
10093
|
.optional()
|
|
9899
|
-
.describe("证书 ID
|
|
10094
|
+
.describe("证书 ID。仅 bindCustomDomain 使用:显式传入时跳过自动检索;" +
|
|
10095
|
+
"省略时按 domain 调用 describeCertificates(SearchKey=domain)——" +
|
|
10096
|
+
"单证书自动选用,无证书报错,多证书返回结构化选择指引(MCP 非交互)。" +
|
|
9900
10097
|
"在已有自定义域名上 createRoute / updateRoute / deleteRoute 不需要 certificateId。"),
|
|
9901
10098
|
accessType: zod_1.z
|
|
9902
10099
|
.enum(["DIRECT", "CDN", "CUSTOM"])
|
|
@@ -13555,7 +13752,7 @@ class TelemetryReporter {
|
|
|
13555
13752
|
const arch = os_1.default.arch(); // 系统架构
|
|
13556
13753
|
// Build-time injected version; guard for vitest / non-webpack runtimes.
|
|
13557
13754
|
const mcpVersion = process.env.npm_package_version ||
|
|
13558
|
-
( true ? "2.
|
|
13755
|
+
( true ? "2.32.0" : 0);
|
|
13559
13756
|
return {
|
|
13560
13757
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
13561
13758
|
deviceId: this.deviceId,
|
|
@@ -19462,7 +19659,7 @@ function registerSetupTools(server) {
|
|
|
19462
19659
|
server.registerTool("downloadTemplate", {
|
|
19463
19660
|
title: "下载项目模板",
|
|
19464
19661
|
description: `自动下载并部署CloudBase项目模板。
|
|
19465
|
-
**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.
|
|
19662
|
+
**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.32.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
19466
19663
|
inputSchema: {
|
|
19467
19664
|
template: zod_1.z
|
|
19468
19665
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -22071,6 +22268,750 @@ async function logout(options) {
|
|
|
22071
22268
|
}
|
|
22072
22269
|
|
|
22073
22270
|
|
|
22271
|
+
/***/ }),
|
|
22272
|
+
|
|
22273
|
+
/***/ 7561:
|
|
22274
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
22275
|
+
|
|
22276
|
+
|
|
22277
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
22278
|
+
exports.QbaseError = exports.MSG_TYPES = exports.XPAY_EVENT_TYPES = void 0;
|
|
22279
|
+
exports.mergeSubscribeList = mergeSubscribeList;
|
|
22280
|
+
exports.mergeUnsubscribeList = mergeUnsubscribeList;
|
|
22281
|
+
exports.mergeSetEnableList = mergeSetEnableList;
|
|
22282
|
+
exports.registerMsgPushTools = registerMsgPushTools;
|
|
22283
|
+
const zod_1 = __webpack_require__(2971);
|
|
22284
|
+
const tool_result_js_1 = __webpack_require__(9835);
|
|
22285
|
+
const CATEGORY = "消息推送";
|
|
22286
|
+
/** 消息推送 qbase CGI 对应的 Cloud API service 名(宿主按后端契约确认) */
|
|
22287
|
+
const MSG_PUSH_SERVICE = "qbase";
|
|
22288
|
+
/** 消息推送 Cloud API version(宿主按后端契约确认) */
|
|
22289
|
+
const MSG_PUSH_VERSION = "2018-06-08";
|
|
22290
|
+
/**
|
|
22291
|
+
* 虚拟支付默认事件集合。
|
|
22292
|
+
* manageMessagePush(action=subscribe) 未传 event_types 时默认订阅这 7 个事件。
|
|
22293
|
+
*/
|
|
22294
|
+
exports.XPAY_EVENT_TYPES = [
|
|
22295
|
+
"xpay_goods_deliver_notify",
|
|
22296
|
+
"xpay_coin_pay_notify",
|
|
22297
|
+
"xpay_complaint_notify",
|
|
22298
|
+
"xpay_subscribe_signing_result_notify",
|
|
22299
|
+
"xpay_subscribe_pay_fail_notify",
|
|
22300
|
+
"xpay_subscribe_ios_refund_query_notify",
|
|
22301
|
+
"xpay_refund_notify",
|
|
22302
|
+
];
|
|
22303
|
+
/** 配置不存在的业务码(getappconfig 返回该码时视为空配置) */
|
|
22304
|
+
const RET_CONFIG_NOT_EXISTS = 80209;
|
|
22305
|
+
/**
|
|
22306
|
+
* uploadappconfig version 乐观锁冲突业务码(实测:用旧 version 覆盖写返回 ret=80208)。
|
|
22307
|
+
* Prefer structured ret over errmsg regex; regex remains a fallback.
|
|
22308
|
+
*/
|
|
22309
|
+
const RET_VERSION_CONFLICT = 80208;
|
|
22310
|
+
/** 消息推送条目类型(虚拟支付回调等事件均为 event) */
|
|
22311
|
+
const MSG_TYPE_EVENT = "event";
|
|
22312
|
+
/**
|
|
22313
|
+
* manageMessagePush 可操作的消息类型。
|
|
22314
|
+
* - event:事件类(需 event_types;event 字段为具体事件名)
|
|
22315
|
+
* - text/image/voice/video/miniprogrampage:消息类型条目(event 固定空串 "")
|
|
22316
|
+
*/
|
|
22317
|
+
exports.MSG_TYPES = [
|
|
22318
|
+
"event",
|
|
22319
|
+
"text",
|
|
22320
|
+
"image",
|
|
22321
|
+
"voice",
|
|
22322
|
+
"video",
|
|
22323
|
+
"miniprogrampage",
|
|
22324
|
+
];
|
|
22325
|
+
/** Empty event field for non-event message-type callback entries */
|
|
22326
|
+
const EMPTY_EVENT = "";
|
|
22327
|
+
/** qbase CGI 业务错误(code 为 ret 或内部错误码) */
|
|
22328
|
+
class QbaseError extends Error {
|
|
22329
|
+
code;
|
|
22330
|
+
constructor(code, message) {
|
|
22331
|
+
super(message);
|
|
22332
|
+
this.name = "QbaseError";
|
|
22333
|
+
this.code = code;
|
|
22334
|
+
}
|
|
22335
|
+
}
|
|
22336
|
+
exports.QbaseError = QbaseError;
|
|
22337
|
+
function canonicalEntry(entry) {
|
|
22338
|
+
return JSON.stringify([
|
|
22339
|
+
entry.msgType,
|
|
22340
|
+
entry.event,
|
|
22341
|
+
entry.env,
|
|
22342
|
+
entry.functionName,
|
|
22343
|
+
entry.enable ?? false,
|
|
22344
|
+
]);
|
|
22345
|
+
}
|
|
22346
|
+
function sameCallbackList(a, b) {
|
|
22347
|
+
if (a.length !== b.length)
|
|
22348
|
+
return false;
|
|
22349
|
+
const sortedA = a.map(canonicalEntry).sort();
|
|
22350
|
+
const sortedB = b.map(canonicalEntry).sort();
|
|
22351
|
+
return sortedA.every((v, i) => v === sortedB[i]);
|
|
22352
|
+
}
|
|
22353
|
+
/**
|
|
22354
|
+
* subscribe 幂等 merge:对每个目标事件,
|
|
22355
|
+
* - 已存在完全相同 (msgType, event, env, functionName) 的条目 → 保留原条目(含 enable,幂等)
|
|
22356
|
+
* - 该事件已绑定到其他云函数 → 移除旧条目并重绑到目标函数(一事一函数);重绑保留原 enable
|
|
22357
|
+
* - 全新事件条目 → 默认 enable=true
|
|
22358
|
+
* 未变化时 changed=false,调用方跳过 POST。
|
|
22359
|
+
*/
|
|
22360
|
+
function mergeSubscribeList(current, targets, envId, functionName, msgType = MSG_TYPE_EVENT) {
|
|
22361
|
+
const uniqueTargets = [...new Set(targets)];
|
|
22362
|
+
const kept = [];
|
|
22363
|
+
const keptEvents = new Set();
|
|
22364
|
+
const rebound = [];
|
|
22365
|
+
/** enable to preserve when rebinding an existing event onto a different function */
|
|
22366
|
+
const reboundEnableByEvent = new Map();
|
|
22367
|
+
for (const entry of current) {
|
|
22368
|
+
if (entry.msgType === msgType && uniqueTargets.includes(entry.event)) {
|
|
22369
|
+
if (entry.env === envId && entry.functionName === functionName) {
|
|
22370
|
+
if (keptEvents.has(entry.event)) {
|
|
22371
|
+
continue; // 防御:同事件重复条目只保留一条
|
|
22372
|
+
}
|
|
22373
|
+
keptEvents.add(entry.event);
|
|
22374
|
+
kept.push(entry);
|
|
22375
|
+
}
|
|
22376
|
+
else {
|
|
22377
|
+
rebound.push(entry.event);
|
|
22378
|
+
if (!reboundEnableByEvent.has(entry.event)) {
|
|
22379
|
+
reboundEnableByEvent.set(entry.event, entry.enable ?? false);
|
|
22380
|
+
}
|
|
22381
|
+
}
|
|
22382
|
+
}
|
|
22383
|
+
else {
|
|
22384
|
+
kept.push(entry);
|
|
22385
|
+
}
|
|
22386
|
+
}
|
|
22387
|
+
const added = [];
|
|
22388
|
+
for (const target of uniqueTargets) {
|
|
22389
|
+
const exists = kept.some((e) => e.msgType === msgType &&
|
|
22390
|
+
e.event === target &&
|
|
22391
|
+
e.env === envId &&
|
|
22392
|
+
e.functionName === functionName);
|
|
22393
|
+
if (!exists) {
|
|
22394
|
+
// Rebound keeps prior enable; brand-new events default to enabled
|
|
22395
|
+
const enable = reboundEnableByEvent.has(target)
|
|
22396
|
+
? reboundEnableByEvent.get(target)
|
|
22397
|
+
: true;
|
|
22398
|
+
kept.push({ msgType, event: target, env: envId, functionName, enable });
|
|
22399
|
+
added.push(target);
|
|
22400
|
+
}
|
|
22401
|
+
}
|
|
22402
|
+
return {
|
|
22403
|
+
list: kept,
|
|
22404
|
+
changed: !sameCallbackList(current, kept),
|
|
22405
|
+
added,
|
|
22406
|
+
rebound: [...new Set(rebound)],
|
|
22407
|
+
removed: [],
|
|
22408
|
+
matched: [],
|
|
22409
|
+
};
|
|
22410
|
+
}
|
|
22411
|
+
/**
|
|
22412
|
+
* unsubscribe merge:仅移除匹配 (msgType, event, env, functionName) 的条目,
|
|
22413
|
+
* 其他配置全部保留;无匹配条目时 changed=false(幂等 no-op)。
|
|
22414
|
+
*/
|
|
22415
|
+
function mergeUnsubscribeList(current, targets, envId, functionName, msgType = MSG_TYPE_EVENT) {
|
|
22416
|
+
const uniqueTargets = new Set(targets);
|
|
22417
|
+
const removed = [];
|
|
22418
|
+
const list = current.filter((entry) => {
|
|
22419
|
+
if (entry.msgType === msgType &&
|
|
22420
|
+
uniqueTargets.has(entry.event) &&
|
|
22421
|
+
entry.env === envId &&
|
|
22422
|
+
entry.functionName === functionName) {
|
|
22423
|
+
removed.push(entry.event);
|
|
22424
|
+
return false;
|
|
22425
|
+
}
|
|
22426
|
+
return true;
|
|
22427
|
+
});
|
|
22428
|
+
return {
|
|
22429
|
+
list,
|
|
22430
|
+
changed: !sameCallbackList(current, list),
|
|
22431
|
+
added: [],
|
|
22432
|
+
rebound: [],
|
|
22433
|
+
removed: [...new Set(removed)],
|
|
22434
|
+
matched: [],
|
|
22435
|
+
};
|
|
22436
|
+
}
|
|
22437
|
+
/**
|
|
22438
|
+
* setEnable merge:翻转匹配条目的 enable 字段;无匹配条目或值未变化时 changed=false。
|
|
22439
|
+
*/
|
|
22440
|
+
function mergeSetEnableList(current, targets, envId, functionName, enable, msgType = MSG_TYPE_EVENT) {
|
|
22441
|
+
const uniqueTargets = new Set(targets);
|
|
22442
|
+
const matched = [];
|
|
22443
|
+
const list = current.map((entry) => {
|
|
22444
|
+
if (entry.msgType === msgType &&
|
|
22445
|
+
uniqueTargets.has(entry.event) &&
|
|
22446
|
+
entry.env === envId &&
|
|
22447
|
+
entry.functionName === functionName) {
|
|
22448
|
+
matched.push(entry.event);
|
|
22449
|
+
if ((entry.enable ?? false) === enable)
|
|
22450
|
+
return entry;
|
|
22451
|
+
return { ...entry, enable };
|
|
22452
|
+
}
|
|
22453
|
+
return entry;
|
|
22454
|
+
});
|
|
22455
|
+
return {
|
|
22456
|
+
list,
|
|
22457
|
+
changed: !sameCallbackList(current, list),
|
|
22458
|
+
added: [],
|
|
22459
|
+
rebound: [],
|
|
22460
|
+
removed: [],
|
|
22461
|
+
matched: [...new Set(matched)],
|
|
22462
|
+
};
|
|
22463
|
+
}
|
|
22464
|
+
function getTransport(server) {
|
|
22465
|
+
return server.cloudBaseOptions?.requestFn;
|
|
22466
|
+
}
|
|
22467
|
+
function parseConfigString(config) {
|
|
22468
|
+
if (typeof config !== "string" || !config) {
|
|
22469
|
+
throw new QbaseError("PARSE_ERROR", "getappconfig 返回的 config 为空或非 JSON 字符串");
|
|
22470
|
+
}
|
|
22471
|
+
try {
|
|
22472
|
+
const parsed = JSON.parse(config);
|
|
22473
|
+
return {
|
|
22474
|
+
enable: parsed?.enable !== false,
|
|
22475
|
+
callbacks: Array.isArray(parsed?.callbacks) ? parsed.callbacks : [],
|
|
22476
|
+
};
|
|
22477
|
+
}
|
|
22478
|
+
catch (e) {
|
|
22479
|
+
throw new QbaseError("PARSE_ERROR", `getappconfig 返回的 config 解析失败: ${e instanceof Error ? e.message : String(e)}`);
|
|
22480
|
+
}
|
|
22481
|
+
}
|
|
22482
|
+
/**
|
|
22483
|
+
* 经现有 CloudApiRequestFn(cloudBaseOptions.requestFn)领域语义调用 qbase CGI。
|
|
22484
|
+
* 与 databaseNoSQL/functions 完全一致:包内只表达 service/action/payload,不感知 URL;
|
|
22485
|
+
* 由宿主(微信 IDE)注入的 requestFn 负责路由到 wxa-dev-qbase/apihttpagent 并附加登录态。
|
|
22486
|
+
*/
|
|
22487
|
+
async function callQbase(server, appid, action, payload) {
|
|
22488
|
+
const requestFn = getTransport(server);
|
|
22489
|
+
if (!requestFn) {
|
|
22490
|
+
throw new QbaseError("TRANSPORT_UNAVAILABLE", "未注入 Cloud API 请求通道(cloudBaseOptions.requestFn)");
|
|
22491
|
+
}
|
|
22492
|
+
const service = server.pluginOptions?.msgPush?.service ?? MSG_PUSH_SERVICE;
|
|
22493
|
+
try {
|
|
22494
|
+
// Pass appid as a top-level optional field so hosts can select the correct
|
|
22495
|
+
// WeChat login session in multi-appid scenarios (additive, non-breaking).
|
|
22496
|
+
const result = await requestFn({
|
|
22497
|
+
service,
|
|
22498
|
+
action,
|
|
22499
|
+
version: MSG_PUSH_VERSION,
|
|
22500
|
+
region: "",
|
|
22501
|
+
payload,
|
|
22502
|
+
appid,
|
|
22503
|
+
});
|
|
22504
|
+
return (result ?? {});
|
|
22505
|
+
}
|
|
22506
|
+
catch (e) {
|
|
22507
|
+
throw new QbaseError("TRANSPORT_ERROR", `qbase 请求失败(${service}/${action}): ${e instanceof Error ? e.message : String(e)}`);
|
|
22508
|
+
}
|
|
22509
|
+
}
|
|
22510
|
+
/** 读取当前消息推送配置(乐观锁:version + 全量列表) */
|
|
22511
|
+
async function readCallbackConfig(server, appid) {
|
|
22512
|
+
const resp = await callQbase(server, appid, "getAppConfig", { type: 1 });
|
|
22513
|
+
const ret = resp.base_resp?.ret;
|
|
22514
|
+
if (ret !== undefined && ret !== 0) {
|
|
22515
|
+
if (ret === RET_CONFIG_NOT_EXISTS) {
|
|
22516
|
+
return { version: 0, enable: false, list: [] };
|
|
22517
|
+
}
|
|
22518
|
+
throw new QbaseError(ret, `getappconfig 失败(ret=${ret}): ${resp.base_resp?.errmsg ?? "未知错误"}`);
|
|
22519
|
+
}
|
|
22520
|
+
const config = parseConfigString(resp.config);
|
|
22521
|
+
return {
|
|
22522
|
+
version: Number(resp.version ?? 0),
|
|
22523
|
+
enable: config.enable,
|
|
22524
|
+
list: config.callbacks,
|
|
22525
|
+
};
|
|
22526
|
+
}
|
|
22527
|
+
/** 读取合法事件约束列表 */
|
|
22528
|
+
async function fetchSupportedEvents(server, appid) {
|
|
22529
|
+
const resp = await callQbase(server, appid, "getCallbackSupportList", {});
|
|
22530
|
+
const ret = resp.base_resp?.ret;
|
|
22531
|
+
if (ret !== undefined && ret !== 0) {
|
|
22532
|
+
throw new QbaseError(ret, `getcallbacksupportlist 失败(ret=${ret}): ${resp.base_resp?.errmsg ?? "未知错误"}`);
|
|
22533
|
+
}
|
|
22534
|
+
const raw = resp.data;
|
|
22535
|
+
if (typeof raw === "string") {
|
|
22536
|
+
try {
|
|
22537
|
+
const parsed = JSON.parse(raw);
|
|
22538
|
+
return Array.isArray(parsed?.list) ? parsed.list : [];
|
|
22539
|
+
}
|
|
22540
|
+
catch {
|
|
22541
|
+
return [];
|
|
22542
|
+
}
|
|
22543
|
+
}
|
|
22544
|
+
if (typeof raw === "object" && raw !== null && Array.isArray(raw.list)) {
|
|
22545
|
+
return raw.list;
|
|
22546
|
+
}
|
|
22547
|
+
return [];
|
|
22548
|
+
}
|
|
22549
|
+
/** 全量覆盖写入配置(带 version 乐观锁;冲突时抛 VERSION_CONFLICT) */
|
|
22550
|
+
async function uploadCallbackConfig(server, appid, state) {
|
|
22551
|
+
const resp = await callQbase(server, appid, "uploadAppConfig", {
|
|
22552
|
+
type: 1,
|
|
22553
|
+
version: state.version,
|
|
22554
|
+
config: JSON.stringify({ enable: state.enable, callbacks: state.list }),
|
|
22555
|
+
});
|
|
22556
|
+
const ret = resp.base_resp?.ret;
|
|
22557
|
+
if (ret !== undefined && ret !== 0) {
|
|
22558
|
+
const errmsg = resp.base_resp?.errmsg ?? "";
|
|
22559
|
+
// Prefer structured ret (80208); fall back to errmsg regex for older/unknown backends
|
|
22560
|
+
const isVersionConflict = ret === RET_VERSION_CONFLICT || /version|版本|conflict|冲突/i.test(errmsg);
|
|
22561
|
+
if (isVersionConflict) {
|
|
22562
|
+
throw new QbaseError("VERSION_CONFLICT", `uploadappconfig version 冲突(ret=${ret},本地 version=${state.version},服务端已被其他操作修改): ${errmsg || "system error"}。` +
|
|
22563
|
+
`请重新调用 queryMessagePush(action=list) 获取最新配置后重试(RFC 7232 If-Match 语义:重读 → merge → 重试)。`);
|
|
22564
|
+
}
|
|
22565
|
+
throw new QbaseError(ret, `uploadappconfig 失败(ret=${ret}): ${errmsg || "未知错误"}。请重新查询最新配置后重试。`);
|
|
22566
|
+
}
|
|
22567
|
+
}
|
|
22568
|
+
/** 读取云托管消息推送配置;无配置(云函数模式)返回 null */
|
|
22569
|
+
async function readContainerConfig(server, appid) {
|
|
22570
|
+
const resp = await callQbase(server, appid, "getContainerCallbackConfig", {});
|
|
22571
|
+
const ret = resp.base_resp?.ret;
|
|
22572
|
+
if (ret !== undefined && ret !== 0) {
|
|
22573
|
+
if (ret === RET_CONFIG_NOT_EXISTS) {
|
|
22574
|
+
return null;
|
|
22575
|
+
}
|
|
22576
|
+
throw new QbaseError(ret, `getcontainercallbackconfig 失败(ret=${ret}): ${resp.base_resp?.errmsg ?? "未知错误"}`);
|
|
22577
|
+
}
|
|
22578
|
+
const { base_resp: _base, ...config } = resp;
|
|
22579
|
+
return config;
|
|
22580
|
+
}
|
|
22581
|
+
async function setContainerConfig(server, appid, cfg) {
|
|
22582
|
+
const resp = await callQbase(server, appid, "setContainerCallbackConfig", cfg);
|
|
22583
|
+
const ret = resp.base_resp?.ret;
|
|
22584
|
+
if (ret !== undefined && ret !== 0) {
|
|
22585
|
+
throw new QbaseError(ret, `setcontainercallbackconfig 失败(ret=${ret}): ${resp.base_resp?.errmsg ?? "未知错误"}`);
|
|
22586
|
+
}
|
|
22587
|
+
}
|
|
22588
|
+
function buildTransportUnavailablePayload(toolName, action) {
|
|
22589
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22590
|
+
ok: false,
|
|
22591
|
+
code: "MSG_PUSH_TRANSPORT_UNAVAILABLE",
|
|
22592
|
+
message: "消息推送配置依赖微信小程序云开发 qbase 管理接口(需要微信 IDE 登录态)," +
|
|
22593
|
+
"当前 CloudBase MCP 进程未注入 Cloud API 请求通道(cloudBaseOptions.requestFn),无法直连 qbase" +
|
|
22594
|
+
"(安全边界:腾讯云身份不可调用微信登录态 CGI)。\n" +
|
|
22595
|
+
"可用方案:\n" +
|
|
22596
|
+
"- 在微信开发者工具 MCP 中使用 cloud_msg_push_query / cloud_msg_push_manage(微信 IDE 已注入 qbase 通道)\n" +
|
|
22597
|
+
"- 或由宿主(微信 IDE)在 createCloudBaseMcpServer 时注入 cloudBaseOptions.requestFn,并在 pluginsEnabled 中启用 msg-push 插件",
|
|
22598
|
+
next_step: {
|
|
22599
|
+
tool: toolName,
|
|
22600
|
+
action,
|
|
22601
|
+
hint: "需在微信 IDE 登录态通道中调用",
|
|
22602
|
+
},
|
|
22603
|
+
});
|
|
22604
|
+
}
|
|
22605
|
+
function buildConfirmPayload({ toolName, action, message, requiredParams, }) {
|
|
22606
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22607
|
+
ok: false,
|
|
22608
|
+
code: "CONFIRM_REQUIRED",
|
|
22609
|
+
message: `${message}\n\n请核对后传入 confirm="yes" 确认执行;如需取消或修改,请勿传 confirm="yes",改传其他参数重试。`,
|
|
22610
|
+
confirmation_acknowledgement: {
|
|
22611
|
+
text: "我已知晓并确认执行上述消息推送配置变更",
|
|
22612
|
+
required: true,
|
|
22613
|
+
},
|
|
22614
|
+
next_step: { tool: toolName, action, requiredParams },
|
|
22615
|
+
});
|
|
22616
|
+
}
|
|
22617
|
+
function buildInvalidEventPayload(action, invalid) {
|
|
22618
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22619
|
+
ok: false,
|
|
22620
|
+
code: "INVALID_EVENT_TYPE",
|
|
22621
|
+
message: `${action} 包含不在合法约束内的事件:${invalid.join(", ")}。` +
|
|
22622
|
+
`请先调用 queryMessagePush(action=listSupportedEvents) 查询该小程序全部合法事件(含虚拟支付 7 个 xpay_* 事件),` +
|
|
22623
|
+
`确认事件名称后重试。`,
|
|
22624
|
+
invalid_events: invalid,
|
|
22625
|
+
next_step: {
|
|
22626
|
+
tool: "queryMessagePush",
|
|
22627
|
+
action: "listSupportedEvents",
|
|
22628
|
+
required_params: ["appid"],
|
|
22629
|
+
},
|
|
22630
|
+
});
|
|
22631
|
+
}
|
|
22632
|
+
function buildMergeDiffText(action, result, envId, functionName) {
|
|
22633
|
+
const lines = [];
|
|
22634
|
+
if (action === "subscribe") {
|
|
22635
|
+
if (result.added.length) {
|
|
22636
|
+
lines.push(`- 新增订阅(${envId}/${functionName}): ${result.added.join(", ")}`);
|
|
22637
|
+
}
|
|
22638
|
+
if (result.rebound.length) {
|
|
22639
|
+
lines.push(`- 重绑事件(原绑定其他云函数,按"一事一函数"约束改绑到 ${envId}/${functionName}): ${result.rebound.join(", ")}`);
|
|
22640
|
+
}
|
|
22641
|
+
}
|
|
22642
|
+
else if (action === "unsubscribe") {
|
|
22643
|
+
lines.push(`- 移除订阅(${envId}/${functionName}): ${result.removed.join(", ")}`);
|
|
22644
|
+
}
|
|
22645
|
+
else if (action === "setEnable") {
|
|
22646
|
+
lines.push(`- ${result.matched.length ? "" : "未匹配到条目"}订阅状态变更(${envId}/${functionName}): ${result.matched.join(", ")}`);
|
|
22647
|
+
}
|
|
22648
|
+
return lines.join("\n");
|
|
22649
|
+
}
|
|
22650
|
+
function registerMsgPushTools(server) {
|
|
22651
|
+
const logger = server.logger;
|
|
22652
|
+
// ─── queryMessagePush(只读)───────────────────────────────────────────────
|
|
22653
|
+
server.registerTool?.("queryMessagePush", {
|
|
22654
|
+
title: "查询小程序消息推送配置",
|
|
22655
|
+
description: "查询小程序云开发消息推送配置(qbase getappconfig)或全部合法消息推送事件约束(getcallbacksupportlist)。" +
|
|
22656
|
+
"消息推送把小程序事件/消息(含虚拟支付回调、text/image 等消息类型)送到云函数,无需自建服务器。" +
|
|
22657
|
+
"action=list 返回当前配置列表(msgType/event/env/functionName/enable)与 version(乐观锁版本号);" +
|
|
22658
|
+
"action=listSupportedEvents 返回全部合法约束(按消息类型分组:event 组含事件名列表;text/image/voice/video/miniprogrampage 组 events 为空数组)," +
|
|
22659
|
+
"事件类用 manageMessagePush(event_types=...),消息类型用 manageMessagePush(msg_type=...)。" +
|
|
22660
|
+
"需要微信 IDE 登录态通道(宿主注入 cloudBaseOptions.requestFn),独立 CloudBase MCP 运行会返回指引错误。",
|
|
22661
|
+
inputSchema: {
|
|
22662
|
+
appid: zod_1.z
|
|
22663
|
+
.string()
|
|
22664
|
+
.describe("小程序 AppID(必填,与微信开发者工具一致;用于选择微信登录态会话)"),
|
|
22665
|
+
env: zod_1.z.string().optional().describe("可选:环境 ID;传入时 list 仅返回该环境的订阅条目"),
|
|
22666
|
+
action: zod_1.z
|
|
22667
|
+
.enum(["list", "listSupportedEvents"])
|
|
22668
|
+
.describe("list: 查询当前消息推送配置列表\nlistSupportedEvents: 查询全部合法消息推送事件约束(按 msgType 分组)"),
|
|
22669
|
+
},
|
|
22670
|
+
annotations: {
|
|
22671
|
+
readOnlyHint: true,
|
|
22672
|
+
openWorldHint: true,
|
|
22673
|
+
category: CATEGORY,
|
|
22674
|
+
},
|
|
22675
|
+
}, async ({ appid, env, action }) => {
|
|
22676
|
+
if (!getTransport(server)) {
|
|
22677
|
+
return buildTransportUnavailablePayload("queryMessagePush", action);
|
|
22678
|
+
}
|
|
22679
|
+
if (action === "list") {
|
|
22680
|
+
const state = await readCallbackConfig(server, appid);
|
|
22681
|
+
const callbacks = env
|
|
22682
|
+
? state.list.filter((c) => c.env === env)
|
|
22683
|
+
: state.list;
|
|
22684
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22685
|
+
ok: true,
|
|
22686
|
+
success: true,
|
|
22687
|
+
message: "查询消息推送配置成功",
|
|
22688
|
+
version: state.version,
|
|
22689
|
+
enable: state.enable,
|
|
22690
|
+
callbacks,
|
|
22691
|
+
filteredByEnv: env ?? undefined,
|
|
22692
|
+
next_steps: [
|
|
22693
|
+
"manageMessagePush(action=subscribe) 订阅事件(缺省 event_types 时默认订阅虚拟支付 7 事件)",
|
|
22694
|
+
"queryMessagePush(action=listSupportedEvents) 查看全部合法事件",
|
|
22695
|
+
],
|
|
22696
|
+
});
|
|
22697
|
+
}
|
|
22698
|
+
if (action === "listSupportedEvents") {
|
|
22699
|
+
const constraints = await fetchSupportedEvents(server, appid);
|
|
22700
|
+
const grouped = [];
|
|
22701
|
+
const byMsgType = new Map();
|
|
22702
|
+
for (const c of constraints) {
|
|
22703
|
+
// Ensure every msgType appears (message types like text have empty event)
|
|
22704
|
+
if (!byMsgType.has(c.msgType)) {
|
|
22705
|
+
byMsgType.set(c.msgType, []);
|
|
22706
|
+
}
|
|
22707
|
+
if (c.event) {
|
|
22708
|
+
byMsgType.get(c.msgType).push(c.event);
|
|
22709
|
+
}
|
|
22710
|
+
}
|
|
22711
|
+
for (const [msgType, events] of byMsgType) {
|
|
22712
|
+
grouped.push({ msgType, events });
|
|
22713
|
+
}
|
|
22714
|
+
const supportedSet = new Set(constraints.filter((c) => c.event).map((c) => c.event));
|
|
22715
|
+
const xpaySupported = exports.XPAY_EVENT_TYPES.filter((e) => supportedSet.has(e));
|
|
22716
|
+
const xpayMissing = exports.XPAY_EVENT_TYPES.filter((e) => !supportedSet.has(e));
|
|
22717
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22718
|
+
ok: true,
|
|
22719
|
+
success: true,
|
|
22720
|
+
message: "获取合法消息推送事件约束成功",
|
|
22721
|
+
msgTypes: grouped,
|
|
22722
|
+
totalEvents: supportedSet.size,
|
|
22723
|
+
xpay_default_events: {
|
|
22724
|
+
supported: xpaySupported,
|
|
22725
|
+
missing: xpayMissing,
|
|
22726
|
+
hint: xpayMissing.length
|
|
22727
|
+
? "以下默认事件不在当前小程序合法约束中(可能未开通虚拟支付),订阅时服务端可能拒绝"
|
|
22728
|
+
: undefined,
|
|
22729
|
+
},
|
|
22730
|
+
hint: "manageMessagePush 的 event_types 仅接受上述合法事件;subscribe 缺省 event_types 时默认订阅虚拟支付 7 事件。" +
|
|
22731
|
+
"消息类型条目(text/image/voice/video/miniprogrampage,events 为空数组)请用 manageMessagePush(msg_type=...) 管理,无需 event_types。",
|
|
22732
|
+
});
|
|
22733
|
+
}
|
|
22734
|
+
throw new Error(`不支持的操作类型: ${action}`);
|
|
22735
|
+
});
|
|
22736
|
+
// ─── manageMessagePush(写,需确认)────────────────────────────────────────
|
|
22737
|
+
server.registerTool?.("manageMessagePush", {
|
|
22738
|
+
title: "管理小程序消息推送配置",
|
|
22739
|
+
description: "管理小程序云开发消息推送配置(写操作,需 confirm=\"yes\" 确认)。" +
|
|
22740
|
+
"基于「读全量 → merge → 全量覆盖(带 version 乐观锁)」实现声明式幂等(对齐 kubectl apply:event_types 是期望集合,重复执行收敛到同一状态;" +
|
|
22741
|
+
"version 冲突即 RFC 7232 If-Match 412,返回可重试错误:重读 → merge → 重试)。" +
|
|
22742
|
+
"msg_type 区分两类条目(缺省 \"event\",向后兼容):" +
|
|
22743
|
+
"msg_type=\"event\" 操作事件类(需 event_types;subscribe 缺省时默认虚拟支付 7 个 xpay_* 事件);" +
|
|
22744
|
+
"msg_type=\"text\"|\"image\"|\"voice\"|\"video\"|\"miniprogrampage\" 操作消息类型条目(event 固定空串,勿传 event_types)。" +
|
|
22745
|
+
"action=subscribe 订阅到指定云函数;同一 (msgType,event) 只能推到一个云函数(一事一函数),已绑定其他函数会自动重绑并说明。" +
|
|
22746
|
+
"action=unsubscribe 移除匹配订阅(msg_type=event 时 event_types 必填;消息类型时按 msg_type 移除)。" +
|
|
22747
|
+
"action=setEnable 启用/停用匹配订阅(msg_type=event 时 event_types + enable 必填;消息类型时 msg_type + enable)。" +
|
|
22748
|
+
"action=ensureCloudFunctionMode 确保推送模式为云函数(若为云托管整包接收则切换 qbase_open=false,需确认)。" +
|
|
22749
|
+
"集合无变化时不发起写请求(幂等 no-op,无需确认)。" +
|
|
22750
|
+
"需要微信 IDE 登录态通道(宿主注入 cloudBaseOptions.requestFn)。",
|
|
22751
|
+
inputSchema: {
|
|
22752
|
+
appid: zod_1.z
|
|
22753
|
+
.string()
|
|
22754
|
+
.describe("小程序 AppID(必填,与微信开发者工具一致;用于选择微信登录态会话)"),
|
|
22755
|
+
env_id: zod_1.z.string().describe("环境 ID(订阅条目绑定的云开发环境)"),
|
|
22756
|
+
function_name: zod_1.z.string().describe("接收消息推送的云函数名"),
|
|
22757
|
+
action: zod_1.z
|
|
22758
|
+
.enum(["subscribe", "unsubscribe", "setEnable", "ensureCloudFunctionMode"])
|
|
22759
|
+
.describe("subscribe: 订阅到指定云函数(msg_type=event 时 event_types 缺省=虚拟支付 7 事件;消息类型时按 msg_type 订阅)\n" +
|
|
22760
|
+
"unsubscribe: 移除匹配订阅(msg_type=event 时 event_types 必填)\n" +
|
|
22761
|
+
"setEnable: 启用/停用匹配订阅(msg_type=event 时 event_types + enable 必填)\n" +
|
|
22762
|
+
"ensureCloudFunctionMode: 确保为云函数推送模式(云托管整包接收时切换,需确认)"),
|
|
22763
|
+
msg_type: zod_1.z
|
|
22764
|
+
.enum(exports.MSG_TYPES)
|
|
22765
|
+
.optional()
|
|
22766
|
+
.describe('消息类型(缺省 "event")。' +
|
|
22767
|
+
'"event":事件类条目,需配合 event_types(subscribe 可缺省=虚拟支付 7 事件)。' +
|
|
22768
|
+
'"text"|"image"|"voice"|"video"|"miniprogrampage":消息类型条目(event 固定空串),勿传 event_types。'),
|
|
22769
|
+
event_types: zod_1.z
|
|
22770
|
+
.array(zod_1.z.string())
|
|
22771
|
+
.optional()
|
|
22772
|
+
.describe("要操作的事件列表(仅 msg_type=\"event\" 时使用;可先 queryMessagePush(action=listSupportedEvents) 查询全量约束)。" +
|
|
22773
|
+
"subscribe 缺省时默认订阅虚拟支付 7 个事件:xpay_goods_deliver_notify、xpay_coin_pay_notify、xpay_complaint_notify、" +
|
|
22774
|
+
"xpay_subscribe_signing_result_notify、xpay_subscribe_pay_fail_notify、xpay_subscribe_ios_refund_query_notify、xpay_refund_notify;" +
|
|
22775
|
+
"unsubscribe / setEnable 且 msg_type=event 时必填。消息类型操作请勿传本参数。"),
|
|
22776
|
+
enable: zod_1.z.boolean().optional().describe("setEnable 时必填:true 启用订阅 / false 停用订阅"),
|
|
22777
|
+
confirm: zod_1.z
|
|
22778
|
+
.string()
|
|
22779
|
+
.optional()
|
|
22780
|
+
.describe('写操作确认:确认执行请传 confirm="yes";不传或传其他值将返回待确认的配置摘要(CONFIRM_REQUIRED),核对后再重试。集合无变化时无需确认。'),
|
|
22781
|
+
},
|
|
22782
|
+
annotations: {
|
|
22783
|
+
readOnlyHint: false,
|
|
22784
|
+
destructiveHint: true,
|
|
22785
|
+
idempotentHint: true,
|
|
22786
|
+
openWorldHint: true,
|
|
22787
|
+
category: CATEGORY,
|
|
22788
|
+
},
|
|
22789
|
+
}, async ({ appid, env_id, function_name, action, msg_type, event_types, enable, confirm, }) => {
|
|
22790
|
+
if (!getTransport(server)) {
|
|
22791
|
+
return buildTransportUnavailablePayload("manageMessagePush", action);
|
|
22792
|
+
}
|
|
22793
|
+
const confirmed = confirm === "yes";
|
|
22794
|
+
const resolvedMsgType = msg_type ?? MSG_TYPE_EVENT;
|
|
22795
|
+
const isEventMsgType = resolvedMsgType === MSG_TYPE_EVENT;
|
|
22796
|
+
if (action === "ensureCloudFunctionMode") {
|
|
22797
|
+
const current = await readContainerConfig(server, appid);
|
|
22798
|
+
if (!current || current.qbase_open === false) {
|
|
22799
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22800
|
+
ok: true,
|
|
22801
|
+
success: true,
|
|
22802
|
+
code: "NO_CHANGE",
|
|
22803
|
+
message: "当前已是云函数推送模式(云托管整包接收未开启),无需变更",
|
|
22804
|
+
containerConfig: current ?? undefined,
|
|
22805
|
+
});
|
|
22806
|
+
}
|
|
22807
|
+
if (!confirmed) {
|
|
22808
|
+
return buildConfirmPayload({
|
|
22809
|
+
toolName: "manageMessagePush",
|
|
22810
|
+
action,
|
|
22811
|
+
message: `当前推送模式为云托管整包接收(qbase_open=true,环境 ${current.qbase_env ?? "-"},路径 ${current.qbase_container_path ?? "-"}),` +
|
|
22812
|
+
`事件会整包进入云托管而非云函数。\n即将执行 setcontainercallbackconfig 关闭云托管整包接收(qbase_open=false),` +
|
|
22813
|
+
`使消息推送按事件进入云函数 ${env_id}/${function_name}。`,
|
|
22814
|
+
requiredParams: ["appid", "env_id", "function_name", "confirm"],
|
|
22815
|
+
});
|
|
22816
|
+
}
|
|
22817
|
+
await setContainerConfig(server, appid, {
|
|
22818
|
+
...current,
|
|
22819
|
+
qbase_open: false,
|
|
22820
|
+
});
|
|
22821
|
+
logger?.({
|
|
22822
|
+
type: "toolInfo",
|
|
22823
|
+
toolName: "manageMessagePush",
|
|
22824
|
+
message: "已切换到云函数推送模式",
|
|
22825
|
+
appid,
|
|
22826
|
+
envId: env_id,
|
|
22827
|
+
});
|
|
22828
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22829
|
+
ok: true,
|
|
22830
|
+
success: true,
|
|
22831
|
+
message: "已切换到云函数推送模式(setcontainercallbackconfig qbase_open=false 成功)",
|
|
22832
|
+
action,
|
|
22833
|
+
});
|
|
22834
|
+
}
|
|
22835
|
+
// Message-type entries use a fixed empty event; event_types must not be supplied
|
|
22836
|
+
if (!isEventMsgType && event_types && event_types.length > 0) {
|
|
22837
|
+
throw new Error(`msg_type="${resolvedMsgType}" 时不应传 event_types(消息类型条目的 event 固定为空串 "");` +
|
|
22838
|
+
`请去掉 event_types,仅传 msg_type / function_name / enable(setEnable 时)`);
|
|
22839
|
+
}
|
|
22840
|
+
// subscribe / unsubscribe / setEnable 共用流程:读 → 校验 → merge → 确认 → 覆盖写
|
|
22841
|
+
let eventList;
|
|
22842
|
+
if (!isEventMsgType) {
|
|
22843
|
+
// Non-event message types: single entry keyed by (msgType, event="")
|
|
22844
|
+
eventList = [EMPTY_EVENT];
|
|
22845
|
+
}
|
|
22846
|
+
else {
|
|
22847
|
+
const targets = event_types ? [...new Set(event_types)] : undefined;
|
|
22848
|
+
if (action === "subscribe" && !targets) {
|
|
22849
|
+
// 缺省 = 虚拟支付默认事件集合
|
|
22850
|
+
}
|
|
22851
|
+
else if (!targets || targets.length === 0) {
|
|
22852
|
+
throw new Error(`${action} 必须提供 event_types(要操作的事件列表,可先 queryMessagePush(action=listSupportedEvents) 查询);` +
|
|
22853
|
+
`若操作消息类型条目请传 msg_type(如 "text"),勿传 event_types`);
|
|
22854
|
+
}
|
|
22855
|
+
eventList = targets ?? [...exports.XPAY_EVENT_TYPES];
|
|
22856
|
+
}
|
|
22857
|
+
// 1. 读当前配置(乐观锁基础:必须读全量再 merge,禁止用本地列表直接覆盖)
|
|
22858
|
+
const state = await readCallbackConfig(server, appid);
|
|
22859
|
+
// 2. 合法性校验
|
|
22860
|
+
let warnings = [];
|
|
22861
|
+
if (!isEventMsgType) {
|
|
22862
|
+
// Validate msg_type exists in support list (message types have empty/missing event)
|
|
22863
|
+
try {
|
|
22864
|
+
const constraints = await fetchSupportedEvents(server, appid);
|
|
22865
|
+
const supportedMsgTypes = new Set(constraints.map((c) => c.msgType));
|
|
22866
|
+
if (!supportedMsgTypes.has(resolvedMsgType)) {
|
|
22867
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22868
|
+
ok: false,
|
|
22869
|
+
code: "INVALID_MSG_TYPE",
|
|
22870
|
+
message: `msg_type="${resolvedMsgType}" 不在 getcallbacksupportlist 合法消息类型内。` +
|
|
22871
|
+
`请先 queryMessagePush(action=listSupportedEvents) 查看可用 msgTypes。`,
|
|
22872
|
+
next_step: { tool: "queryMessagePush", action: "listSupportedEvents" },
|
|
22873
|
+
});
|
|
22874
|
+
}
|
|
22875
|
+
}
|
|
22876
|
+
catch {
|
|
22877
|
+
// Constraint fetch failure does not block; server still validates on write
|
|
22878
|
+
}
|
|
22879
|
+
}
|
|
22880
|
+
else if (action === "subscribe" && event_types && event_types.length > 0) {
|
|
22881
|
+
// 显式 event_types 时严格校验合法性(默认集合仅警告,服务端为准)
|
|
22882
|
+
const constraints = await fetchSupportedEvents(server, appid);
|
|
22883
|
+
const supported = new Set(constraints.filter((c) => c.event).map((c) => c.event));
|
|
22884
|
+
const invalid = eventList.filter((e) => !supported.has(e));
|
|
22885
|
+
if (invalid.length > 0) {
|
|
22886
|
+
return buildInvalidEventPayload(action, invalid);
|
|
22887
|
+
}
|
|
22888
|
+
}
|
|
22889
|
+
else if (action === "subscribe") {
|
|
22890
|
+
try {
|
|
22891
|
+
const constraints = await fetchSupportedEvents(server, appid);
|
|
22892
|
+
const supported = new Set(constraints.filter((c) => c.event).map((c) => c.event));
|
|
22893
|
+
warnings = eventList.filter((e) => !supported.has(e));
|
|
22894
|
+
}
|
|
22895
|
+
catch {
|
|
22896
|
+
// 约束获取失败不阻塞默认订阅(服务端仍会校验)
|
|
22897
|
+
}
|
|
22898
|
+
}
|
|
22899
|
+
// 3. merge(纯函数,幂等;透传 resolvedMsgType)
|
|
22900
|
+
let result;
|
|
22901
|
+
if (action === "subscribe") {
|
|
22902
|
+
result = mergeSubscribeList(state.list, eventList, env_id, function_name, resolvedMsgType);
|
|
22903
|
+
}
|
|
22904
|
+
else if (action === "unsubscribe") {
|
|
22905
|
+
result = mergeUnsubscribeList(state.list, eventList, env_id, function_name, resolvedMsgType);
|
|
22906
|
+
}
|
|
22907
|
+
else if (action === "setEnable") {
|
|
22908
|
+
if (enable === undefined) {
|
|
22909
|
+
throw new Error("setEnable 必须提供 enable(true=启用 / false=停用)");
|
|
22910
|
+
}
|
|
22911
|
+
result = mergeSetEnableList(state.list, eventList, env_id, function_name, enable, resolvedMsgType);
|
|
22912
|
+
}
|
|
22913
|
+
else {
|
|
22914
|
+
throw new Error(`不支持的操作类型: ${action}`);
|
|
22915
|
+
}
|
|
22916
|
+
const targetLabel = isEventMsgType
|
|
22917
|
+
? eventList.join(", ")
|
|
22918
|
+
: `msg_type=${resolvedMsgType}`;
|
|
22919
|
+
// 4. 集合无变化 → 幂等成功,不 POST(无需确认)
|
|
22920
|
+
if (!result.changed) {
|
|
22921
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22922
|
+
ok: true,
|
|
22923
|
+
success: true,
|
|
22924
|
+
code: "NO_CHANGE",
|
|
22925
|
+
message: action === "subscribe"
|
|
22926
|
+
? `订阅已处于期望状态,无变更(幂等,未发起写请求):${targetLabel} 均已绑定 ${env_id}/${function_name}`
|
|
22927
|
+
: action === "unsubscribe"
|
|
22928
|
+
? `未找到可移除的匹配订阅,无变更(幂等,未发起写请求)`
|
|
22929
|
+
: `目标订阅状态已一致,无变更(幂等,未发起写请求)`,
|
|
22930
|
+
action,
|
|
22931
|
+
msg_type: resolvedMsgType,
|
|
22932
|
+
warnings: warnings.length ? warnings : undefined,
|
|
22933
|
+
});
|
|
22934
|
+
}
|
|
22935
|
+
// 5. 有变更 → 需确认
|
|
22936
|
+
const diffText = buildMergeDiffText(action, result, env_id, function_name);
|
|
22937
|
+
if (!confirmed) {
|
|
22938
|
+
return buildConfirmPayload({
|
|
22939
|
+
toolName: "manageMessagePush",
|
|
22940
|
+
action,
|
|
22941
|
+
message: `即将执行消息推送配置变更(${env_id}/${function_name},msg_type=${resolvedMsgType},version=${state.version}):\n${diffText}`,
|
|
22942
|
+
requiredParams: ["appid", "env_id", "function_name", "confirm"],
|
|
22943
|
+
});
|
|
22944
|
+
}
|
|
22945
|
+
// 6. 全量覆盖写(带 version 乐观锁)
|
|
22946
|
+
try {
|
|
22947
|
+
await uploadCallbackConfig(server, appid, {
|
|
22948
|
+
version: state.version,
|
|
22949
|
+
enable: action === "subscribe" ? true : state.enable,
|
|
22950
|
+
list: result.list,
|
|
22951
|
+
});
|
|
22952
|
+
}
|
|
22953
|
+
catch (e) {
|
|
22954
|
+
if (e instanceof QbaseError && e.code === "VERSION_CONFLICT") {
|
|
22955
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22956
|
+
ok: false,
|
|
22957
|
+
code: "VERSION_CONFLICT",
|
|
22958
|
+
retryable: true,
|
|
22959
|
+
message: e.message,
|
|
22960
|
+
next_step: { tool: "queryMessagePush", action: "list" },
|
|
22961
|
+
});
|
|
22962
|
+
}
|
|
22963
|
+
if (e instanceof QbaseError) {
|
|
22964
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22965
|
+
ok: false,
|
|
22966
|
+
code: "QBASE_ERROR",
|
|
22967
|
+
retryable: true,
|
|
22968
|
+
message: e.message,
|
|
22969
|
+
next_step: {
|
|
22970
|
+
tool: "queryMessagePush",
|
|
22971
|
+
action: "list",
|
|
22972
|
+
hint: "重新读取最新配置后重试",
|
|
22973
|
+
},
|
|
22974
|
+
});
|
|
22975
|
+
}
|
|
22976
|
+
throw e;
|
|
22977
|
+
}
|
|
22978
|
+
logger?.({
|
|
22979
|
+
type: "toolInfo",
|
|
22980
|
+
toolName: "manageMessagePush",
|
|
22981
|
+
message: `${action} 成功`,
|
|
22982
|
+
appid,
|
|
22983
|
+
envId: env_id,
|
|
22984
|
+
functionName: function_name,
|
|
22985
|
+
msgType: resolvedMsgType,
|
|
22986
|
+
events: eventList,
|
|
22987
|
+
});
|
|
22988
|
+
const changedText = action === "subscribe"
|
|
22989
|
+
? [
|
|
22990
|
+
result.added.length ? `新增 ${result.added.length} 个` : null,
|
|
22991
|
+
result.rebound.length ? `重绑 ${result.rebound.length} 个` : null,
|
|
22992
|
+
]
|
|
22993
|
+
.filter(Boolean)
|
|
22994
|
+
.join("、")
|
|
22995
|
+
: action === "unsubscribe"
|
|
22996
|
+
? `移除 ${result.removed.length} 个`
|
|
22997
|
+
: `更新 ${result.matched.length} 个`;
|
|
22998
|
+
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
22999
|
+
ok: true,
|
|
23000
|
+
success: true,
|
|
23001
|
+
message: `${action} 成功(${changedText}),version=${state.version} → 服务端已更新`,
|
|
23002
|
+
action,
|
|
23003
|
+
msg_type: resolvedMsgType,
|
|
23004
|
+
added: result.added,
|
|
23005
|
+
rebound: result.rebound,
|
|
23006
|
+
removed: result.removed,
|
|
23007
|
+
matched: result.matched,
|
|
23008
|
+
warnings: warnings.length ? warnings : undefined,
|
|
23009
|
+
callbacks: result.list,
|
|
23010
|
+
});
|
|
23011
|
+
});
|
|
23012
|
+
}
|
|
23013
|
+
|
|
23014
|
+
|
|
22074
23015
|
/***/ }),
|
|
22075
23016
|
|
|
22076
23017
|
/***/ 7793:
|