@cloudbase/cloudbase-mcp 2.24.1 → 2.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -515
- package/README.zh-CN.md +175 -0
- package/dist/cli.cjs +3 -3
- package/dist/index.cjs +1364 -455
- package/dist/index.js +382 -361
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -684,8 +684,8 @@ async function enrichEnvInfoWithRuntimeMode(result, manager) {
|
|
|
684
684
|
: "No legacy NoSQL Database/Storage observed in this env.",
|
|
685
685
|
MysqlNotAvailable: hasMysql
|
|
686
686
|
? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
|
|
687
|
-
: "No MySQL instance is provisioned for this env. Do NOT use `manageMysqlDatabase` / `queryMysqlDatabase` (those are MySQL-specific) and do NOT read the `relational-database-
|
|
688
|
-
RecommendedSkills: "Read `postgresql-development` first for new PG code. `
|
|
687
|
+
: "No MySQL instance is provisioned for this env. Do NOT use `manageMysqlDatabase` / `queryMysqlDatabase` (those are MySQL-specific) and do NOT read the `relational-database-mcp-cloudbase` skill — that family targets MySQL, not CloudBase PG.",
|
|
688
|
+
RecommendedSkills: "Read `postgresql-development-cloudbase` first for new PG code. `cloudbase-document-database-web-sdk` is still applicable for existing NoSQL collections in this env. Skip `relational-database-mcp-cloudbase` (MySQL-only) entirely.",
|
|
689
689
|
}
|
|
690
690
|
: {
|
|
691
691
|
PrimaryBackend: "PostgreSQL is NOT provisioned in this env — this is a legacy NoSQL CloudBase backend.",
|
|
@@ -694,8 +694,8 @@ async function enrichEnvInfoWithRuntimeMode(result, manager) {
|
|
|
694
694
|
Storage: "Browser uploads use `app.uploadFile()` against the bucket exposed in `EnvInfo.Storages[].Bucket`.",
|
|
695
695
|
MysqlNotAvailable: hasMysql
|
|
696
696
|
? "MySQL instance(s) detected — see EnvInfo.MysqlInstances."
|
|
697
|
-
: "No MySQL instance in this env. `manageMysqlDatabase` / `queryMysqlDatabase` and the `relational-database-
|
|
698
|
-
RecommendedSkills: "Read `
|
|
697
|
+
: "No MySQL instance in this env. `manageMysqlDatabase` / `queryMysqlDatabase` and the `relational-database-mcp-cloudbase` skill are not applicable.",
|
|
698
|
+
RecommendedSkills: "Read `cloudbase-document-database-web-sdk` (and `cloud-storage-web` for uploads). `postgresql-development-cloudbase` and `relational-database-mcp-cloudbase` are not applicable to this env.",
|
|
699
699
|
};
|
|
700
700
|
return {
|
|
701
701
|
...result,
|
|
@@ -1616,7 +1616,7 @@ function registerEnvTools(server) {
|
|
|
1616
1616
|
// Register primary tool name (queryEnv)
|
|
1617
1617
|
const queryEnvToolSchema = {
|
|
1618
1618
|
title: "CloudBase 环境查询",
|
|
1619
|
-
description: "查询 CloudBase 环境相关信息,支持查询环境列表、指定环境详情和安全域名。(曾用名:envQuery、listEnvs、getEnvInfo、getEnvAuthDomains)当 action=list 时,会按 DescribeEnvs 语义做列表/筛选,标准返回字段为 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,并支持通过 fields 白名单裁剪这些字段;aliasExact=true 时会按别名精确筛选,避免把前缀相近的环境误当作候选;即使传入 envId,action=list 也只返回摘要,不会返回完整资源明细或 expiry。如需查询某个已知 EnvId 对应环境的详细信息(包括资源字段和计费信息),必须使用 action=info 并传入目标环境的 envId 参数。action=info 会在可用时补充 BillingInfo(如 ExpireTime、PayMode、IsAutoRenew 等计费字段)。\n\n🔍 action=info 还会派生三个用于后端选型的字段:\n- `EnvInfo.RuntimeMode`:'postgresql' 或 'nosql',表示新业务建议默认使用的后端(PG 已开通时为 postgresql,否则为 nosql)。\n- `EnvInfo.RuntimeBackends`:`{postgresql, nosql, mysql}` 三个布尔值,描述当前环境实际并存的后端。\n- `EnvInfo.RuntimeModeHints`:每个后端对应的 API/工具/skill 提示。\n\nAI 在写业务/权限/存储代码前必须先看这三项:PG 模式下新业务推荐 `app.rdb()` + RLS(`managePgDatabase action=execute` 跑 `CREATE POLICY`)+ pgstore;已存在的 NoSQL 集合 / 旧 storage / `managePermissions(resourceType=\"noSqlDatabase\")` 在 PG 环境下仍然有效。真正不适用的是 MySQL:当 `RuntimeBackends.mysql === false` 时,`manageMysqlDatabase` / `queryMysqlDatabase` / `relational-database-
|
|
1619
|
+
description: "查询 CloudBase 环境相关信息,支持查询环境列表、指定环境详情和安全域名。(曾用名:envQuery、listEnvs、getEnvInfo、getEnvAuthDomains)当 action=list 时,会按 DescribeEnvs 语义做列表/筛选,标准返回字段为 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,并支持通过 fields 白名单裁剪这些字段;aliasExact=true 时会按别名精确筛选,避免把前缀相近的环境误当作候选;即使传入 envId,action=list 也只返回摘要,不会返回完整资源明细或 expiry。如需查询某个已知 EnvId 对应环境的详细信息(包括资源字段和计费信息),必须使用 action=info 并传入目标环境的 envId 参数。action=info 会在可用时补充 BillingInfo(如 ExpireTime、PayMode、IsAutoRenew 等计费字段)。\n\n🔍 action=info 还会派生三个用于后端选型的字段:\n- `EnvInfo.RuntimeMode`:'postgresql' 或 'nosql',表示新业务建议默认使用的后端(PG 已开通时为 postgresql,否则为 nosql)。\n- `EnvInfo.RuntimeBackends`:`{postgresql, nosql, mysql}` 三个布尔值,描述当前环境实际并存的后端。\n- `EnvInfo.RuntimeModeHints`:每个后端对应的 API/工具/skill 提示。\n\nAI 在写业务/权限/存储代码前必须先看这三项:PG 模式下新业务推荐 `app.rdb()` + RLS(`managePgDatabase action=execute` 跑 `CREATE POLICY`)+ pgstore;已存在的 NoSQL 集合 / 旧 storage / `managePermissions(resourceType=\"noSqlDatabase\")` 在 PG 环境下仍然有效。真正不适用的是 MySQL:当 `RuntimeBackends.mysql === false` 时,`manageMysqlDatabase` / `queryMysqlDatabase` / `relational-database-mcp-cloudbase` skill 都不该使用。",
|
|
1620
1620
|
inputSchema: {
|
|
1621
1621
|
action: zod_1.z
|
|
1622
1622
|
.enum(["list", "info", "domains"])
|
|
@@ -2233,7 +2233,7 @@ ${envIdSection}
|
|
|
2233
2233
|
## 环境信息
|
|
2234
2234
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
2235
2235
|
- Node.js版本: ${process.version}
|
|
2236
|
-
- MCP 版本:${process.env.npm_package_version || "2.
|
|
2236
|
+
- MCP 版本:${process.env.npm_package_version || "2.25.1" || 0}
|
|
2237
2237
|
- 系统架构: ${os_1.default.arch()}
|
|
2238
2238
|
- 时间: ${new Date().toISOString()}
|
|
2239
2239
|
- 请求ID: ${requestId}
|
|
@@ -2314,7 +2314,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
2314
2314
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
2315
2315
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
2316
2316
|
});
|
|
2317
|
-
const mcpVersion = true ? "2.
|
|
2317
|
+
const mcpVersion = true ? "2.25.1" : 0;
|
|
2318
2318
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
2319
2319
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
2320
2320
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -2483,7 +2483,6 @@ const capi_js_1 = __webpack_require__(738);
|
|
|
2483
2483
|
const cloudrun_js_1 = __webpack_require__(5023);
|
|
2484
2484
|
const dataModel_js_1 = __webpack_require__(4052);
|
|
2485
2485
|
const gateway_js_1 = __webpack_require__(4319);
|
|
2486
|
-
const invite_code_js_1 = __webpack_require__(4760);
|
|
2487
2486
|
const agents_js_1 = __webpack_require__(9527);
|
|
2488
2487
|
const app_auth_js_1 = __webpack_require__(5313);
|
|
2489
2488
|
const apps_js_1 = __webpack_require__(6817);
|
|
@@ -2514,7 +2513,6 @@ const DEFAULT_PLUGINS = [
|
|
|
2514
2513
|
"logs",
|
|
2515
2514
|
"agents",
|
|
2516
2515
|
"download",
|
|
2517
|
-
"invite-code",
|
|
2518
2516
|
"capi",
|
|
2519
2517
|
];
|
|
2520
2518
|
function registerDatabase(server) {
|
|
@@ -2556,7 +2554,6 @@ const AVAILABLE_PLUGINS = {
|
|
|
2556
2554
|
logs: { name: "logs", register: logs_js_1.registerLogTools },
|
|
2557
2555
|
agents: { name: "agents", register: agents_js_1.registerAgentTools },
|
|
2558
2556
|
apps: { name: "apps", register: apps_js_1.registerAppTools },
|
|
2559
|
-
"invite-code": { name: "invite-code", register: invite_code_js_1.registerInviteCodeTools },
|
|
2560
2557
|
cloudrun: { name: "cloudrun", register: cloudrun_js_1.registerCloudRunTools },
|
|
2561
2558
|
capi: { name: "capi", register: capi_js_1.registerCapiTools },
|
|
2562
2559
|
};
|
|
@@ -7153,21 +7150,23 @@ const zod_1 = __webpack_require__(2971);
|
|
|
7153
7150
|
const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
7154
7151
|
const json_content_js_1 = __webpack_require__(4365);
|
|
7155
7152
|
const QUERY_GATEWAY_ACTIONS = [
|
|
7156
|
-
"getAccess",
|
|
7157
|
-
"listDomains",
|
|
7158
7153
|
"listRoutes",
|
|
7159
7154
|
"getRoute",
|
|
7160
7155
|
"listCustomDomains",
|
|
7161
7156
|
];
|
|
7162
7157
|
const MANAGE_GATEWAY_ACTIONS = [
|
|
7163
|
-
"createAccess",
|
|
7164
7158
|
"createRoute",
|
|
7165
7159
|
"updateRoute",
|
|
7166
7160
|
"deleteRoute",
|
|
7167
7161
|
"bindCustomDomain",
|
|
7168
7162
|
"deleteCustomDomain",
|
|
7169
|
-
|
|
7170
|
-
|
|
7163
|
+
];
|
|
7164
|
+
const UPSTREAM_RESOURCE_TYPES = [
|
|
7165
|
+
"SCF",
|
|
7166
|
+
"WEB_SCF",
|
|
7167
|
+
"CBR",
|
|
7168
|
+
"STATIC_STORE",
|
|
7169
|
+
"LH",
|
|
7171
7170
|
];
|
|
7172
7171
|
function normalizeAccessPath(path) {
|
|
7173
7172
|
if (!path) {
|
|
@@ -7198,24 +7197,11 @@ function registerGatewayTools(server) {
|
|
|
7198
7197
|
return (0, json_content_js_1.jsonContent)(buildErrorEnvelope(error));
|
|
7199
7198
|
}
|
|
7200
7199
|
};
|
|
7201
|
-
const listGatewayDomains = async () => {
|
|
7202
|
-
const cloudbase = await getManager();
|
|
7203
|
-
const result = await cloudbase.access.getDomainList();
|
|
7204
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7205
|
-
const domains = [
|
|
7206
|
-
result.DefaultDomain,
|
|
7207
|
-
...(result.ServiceSet || []).map((item) => item.Domain),
|
|
7208
|
-
].filter(Boolean);
|
|
7209
|
-
return {
|
|
7210
|
-
domains,
|
|
7211
|
-
enableService: result.EnableService,
|
|
7212
|
-
raw: result,
|
|
7213
|
-
};
|
|
7214
|
-
};
|
|
7215
7200
|
const listHttpServiceRoutes = async (domain) => {
|
|
7216
7201
|
const cloudbase = await getManager();
|
|
7217
7202
|
const result = await cloudbase.env.describeHttpServiceRoute({
|
|
7218
7203
|
EnvId: await resolveEnvId(),
|
|
7204
|
+
Limit: 1000,
|
|
7219
7205
|
...(domain
|
|
7220
7206
|
? {
|
|
7221
7207
|
Filters: [
|
|
@@ -7231,123 +7217,176 @@ function registerGatewayTools(server) {
|
|
|
7231
7217
|
return result;
|
|
7232
7218
|
};
|
|
7233
7219
|
const flattenRoutes = (result) => (result.Domains ?? []).flatMap((domainItem) => (domainItem.Routes ?? []).map((route) => ({
|
|
7234
|
-
Domain: domainItem.Domain,
|
|
7220
|
+
Domain: domainItem.Domain ?? "",
|
|
7235
7221
|
DomainType: domainItem.DomainType,
|
|
7236
7222
|
AccessType: domainItem.AccessType,
|
|
7223
|
+
IsDefault: domainItem.IsDefault,
|
|
7237
7224
|
...route,
|
|
7238
7225
|
})));
|
|
7226
|
+
const buildRouteUrls = (routes) => Array.from(new Set(routes
|
|
7227
|
+
.filter((route) => route.Domain && route.Path)
|
|
7228
|
+
.map((route) => `https://${route.Domain}${normalizeAccessPath(String(route.Path))}`)));
|
|
7229
|
+
const resolveDefaultHttpDomain = async () => {
|
|
7230
|
+
const routeInfo = await listHttpServiceRoutes();
|
|
7231
|
+
const domains = routeInfo.Domains ?? [];
|
|
7232
|
+
const defaultCandidates = domains.filter((item) => item.IsDefault === true && item.Domain);
|
|
7233
|
+
const preferred = defaultCandidates.find((item) => item.Enable !== false && item.Status === "SUCCESS") ??
|
|
7234
|
+
defaultCandidates.find((item) => item.Enable !== false) ??
|
|
7235
|
+
defaultCandidates[0];
|
|
7236
|
+
if (!preferred?.Domain) {
|
|
7237
|
+
throw new Error("环境默认 HTTP 访问域名未就绪或未开通。请先在控制台开通 HTTP 访问服务,或用 queryGateway(action=\"listRoutes\") 确认 Domains 中是否存在 IsDefault=true 的域名;也可以显式传入 domain 后重试 createRoute/updateRoute/deleteRoute。");
|
|
7238
|
+
}
|
|
7239
|
+
return preferred.Domain;
|
|
7240
|
+
};
|
|
7239
7241
|
const resolveRouteDomain = async (preferredDomain) => {
|
|
7240
7242
|
if (preferredDomain) {
|
|
7241
7243
|
return preferredDomain;
|
|
7242
7244
|
}
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
+
return resolveDefaultHttpDomain();
|
|
7246
|
+
};
|
|
7247
|
+
const resolveUpstreamResourceType = (input) => {
|
|
7248
|
+
const resolved = input.routeUpstreamResourceType ?? input.upstreamResourceType;
|
|
7249
|
+
if (resolved) {
|
|
7250
|
+
return resolved;
|
|
7251
|
+
}
|
|
7252
|
+
throw new Error("必须提供 upstreamResourceType(或 route.upstreamResourceType):" +
|
|
7253
|
+
"WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
|
|
7254
|
+
"禁止仅凭 targetName/serviceName 推断上游类型。");
|
|
7245
7255
|
};
|
|
7246
|
-
const normalizeRoutePayload = async (
|
|
7247
|
-
const
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7256
|
+
const normalizeRoutePayload = async (input) => {
|
|
7257
|
+
const upstreamResourceName = input.route?.serviceName ?? input.targetName;
|
|
7258
|
+
if (!upstreamResourceName) {
|
|
7259
|
+
throw new Error("必须提供 targetName 或 route.serviceName 作为上游资源名称:" +
|
|
7260
|
+
"云函数名、云托管服务名,或静态托管实例名(常见为 staticstore)。");
|
|
7261
|
+
}
|
|
7262
|
+
const upstreamResourceType = resolveUpstreamResourceType({
|
|
7263
|
+
upstreamResourceType: input.upstreamResourceType,
|
|
7264
|
+
routeUpstreamResourceType: input.route?.upstreamResourceType,
|
|
7265
|
+
});
|
|
7266
|
+
const path = normalizeAccessPath(input.route?.path ?? input.path ?? `/${upstreamResourceName}`);
|
|
7267
|
+
const domain = await resolveRouteDomain(input.domain);
|
|
7268
|
+
const enableAuth = input.route?.auth !== undefined
|
|
7269
|
+
? input.route.auth
|
|
7270
|
+
: input.auth !== undefined
|
|
7271
|
+
? input.auth
|
|
7272
|
+
: undefined;
|
|
7273
|
+
const enablePathTransmission = input.route?.enablePathTransmission !== undefined
|
|
7274
|
+
? input.route.enablePathTransmission
|
|
7275
|
+
: input.enablePathTransmission !== undefined
|
|
7276
|
+
? input.enablePathTransmission
|
|
7277
|
+
: undefined;
|
|
7278
|
+
const route = {
|
|
7279
|
+
Path: path,
|
|
7280
|
+
UpstreamResourceType: upstreamResourceType,
|
|
7281
|
+
UpstreamResourceName: upstreamResourceName,
|
|
7282
|
+
EnableAuth: enableAuth,
|
|
7283
|
+
};
|
|
7284
|
+
if (enablePathTransmission !== undefined) {
|
|
7285
|
+
route.EnablePathTransmission = enablePathTransmission;
|
|
7252
7286
|
}
|
|
7253
7287
|
return {
|
|
7254
7288
|
EnvId: await resolveEnvId(),
|
|
7255
7289
|
Domain: {
|
|
7256
|
-
Domain:
|
|
7257
|
-
Routes: [
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
: undefined,
|
|
7267
|
-
},
|
|
7268
|
-
],
|
|
7290
|
+
Domain: domain,
|
|
7291
|
+
Routes: [route],
|
|
7292
|
+
},
|
|
7293
|
+
resolved: {
|
|
7294
|
+
domain,
|
|
7295
|
+
path,
|
|
7296
|
+
upstreamResourceType,
|
|
7297
|
+
upstreamResourceName,
|
|
7298
|
+
enableAuth,
|
|
7299
|
+
enablePathTransmission,
|
|
7269
7300
|
},
|
|
7270
7301
|
};
|
|
7271
7302
|
};
|
|
7303
|
+
const routeMutationNextActions = (targetName) => [
|
|
7304
|
+
{
|
|
7305
|
+
tool: "queryGateway",
|
|
7306
|
+
action: "getRoute",
|
|
7307
|
+
reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
|
|
7308
|
+
},
|
|
7309
|
+
{
|
|
7310
|
+
tool: "queryPermissions",
|
|
7311
|
+
action: "getResourcePermission",
|
|
7312
|
+
reason: "确认函数安全规则是否允许预期访问方;网关 EnableAuth/auth=false 不等于函数已允许匿名访问",
|
|
7313
|
+
},
|
|
7314
|
+
{
|
|
7315
|
+
tool: "managePermissions",
|
|
7316
|
+
action: "updateResourcePermission",
|
|
7317
|
+
reason: "只有在确认需要匿名或浏览器直连访问时,才按实际安全要求更新函数权限",
|
|
7318
|
+
},
|
|
7319
|
+
];
|
|
7272
7320
|
const handleQueryGateway = async (input) => {
|
|
7273
7321
|
switch (input.action) {
|
|
7274
|
-
case "listDomains": {
|
|
7275
|
-
const result = await listGatewayDomains();
|
|
7276
|
-
return buildEnvelope({
|
|
7277
|
-
action: input.action,
|
|
7278
|
-
domains: result.domains,
|
|
7279
|
-
enableService: result.enableService,
|
|
7280
|
-
raw: result.raw,
|
|
7281
|
-
}, `已获取 ${result.domains.length} 个网关域名`, [
|
|
7282
|
-
{
|
|
7283
|
-
tool: "queryGateway",
|
|
7284
|
-
action: "getAccess",
|
|
7285
|
-
reason: "查看某个目标当前的访问入口",
|
|
7286
|
-
},
|
|
7287
|
-
]);
|
|
7288
|
-
}
|
|
7289
7322
|
case "listCustomDomains": {
|
|
7290
|
-
const result = await
|
|
7323
|
+
const result = await listHttpServiceRoutes();
|
|
7324
|
+
const customDomains = (result.Domains ?? []).filter((item) => item.IsDefault !== true);
|
|
7291
7325
|
return buildEnvelope({
|
|
7292
7326
|
action: input.action,
|
|
7293
|
-
domains:
|
|
7294
|
-
total:
|
|
7295
|
-
raw: result
|
|
7296
|
-
}, `已获取 ${
|
|
7327
|
+
domains: customDomains,
|
|
7328
|
+
total: customDomains.length,
|
|
7329
|
+
raw: result,
|
|
7330
|
+
}, `已获取 ${customDomains.length} 个自定义域名`);
|
|
7297
7331
|
}
|
|
7298
7332
|
case "listRoutes": {
|
|
7299
|
-
const result = await listHttpServiceRoutes();
|
|
7333
|
+
const result = await listHttpServiceRoutes(input.domain);
|
|
7300
7334
|
const routes = flattenRoutes(result);
|
|
7301
7335
|
return buildEnvelope({
|
|
7302
7336
|
action: input.action,
|
|
7303
7337
|
routes,
|
|
7338
|
+
urls: buildRouteUrls(routes),
|
|
7304
7339
|
total: result.TotalCount ?? routes.length,
|
|
7305
7340
|
raw: result,
|
|
7306
7341
|
}, `已获取 ${result.TotalCount ?? routes.length} 条 HTTP 路由`);
|
|
7307
7342
|
}
|
|
7308
7343
|
case "getRoute": {
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7344
|
+
if (!input.routeId && !input.targetName && !input.path) {
|
|
7345
|
+
throw new Error("action=getRoute 时至少需要提供 routeId、targetName 或 path");
|
|
7346
|
+
}
|
|
7347
|
+
const result = await listHttpServiceRoutes(input.domain);
|
|
7348
|
+
const normalizedPath = input.path
|
|
7349
|
+
? normalizeAccessPath(input.path)
|
|
7350
|
+
: undefined;
|
|
7351
|
+
const matches = flattenRoutes(result).filter((item) => {
|
|
7352
|
+
if (input.routeId && item.RouteId !== input.routeId) {
|
|
7353
|
+
return false;
|
|
7354
|
+
}
|
|
7355
|
+
if (input.domain && item.Domain !== input.domain) {
|
|
7356
|
+
return false;
|
|
7357
|
+
}
|
|
7358
|
+
if (normalizedPath &&
|
|
7359
|
+
normalizeAccessPath(String(item.Path ?? "")) !== normalizedPath) {
|
|
7360
|
+
return false;
|
|
7361
|
+
}
|
|
7362
|
+
if (input.targetName &&
|
|
7363
|
+
item.UpstreamResourceName !== input.targetName) {
|
|
7364
|
+
return false;
|
|
7365
|
+
}
|
|
7366
|
+
return Boolean(input.routeId || input.targetName || normalizedPath);
|
|
7367
|
+
});
|
|
7368
|
+
const route = matches.length === 1 ? matches[0] : null;
|
|
7369
|
+
const urls = buildRouteUrls(matches);
|
|
7312
7370
|
return buildEnvelope({
|
|
7313
7371
|
action: input.action,
|
|
7314
7372
|
routeId: input.routeId ?? null,
|
|
7373
|
+
targetName: input.targetName ?? null,
|
|
7374
|
+
path: normalizedPath ?? null,
|
|
7375
|
+
domain: input.domain ?? null,
|
|
7315
7376
|
route,
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
}
|
|
7319
|
-
case "getAccess": {
|
|
7320
|
-
if (!input.targetName) {
|
|
7321
|
-
throw new Error("getAccess 操作时,targetName 参数是必需的");
|
|
7322
|
-
}
|
|
7323
|
-
const cloudbase = await getManager();
|
|
7324
|
-
const [accessList, domainInfo] = await Promise.all([
|
|
7325
|
-
cloudbase.access.getAccessList({ name: input.targetName }),
|
|
7326
|
-
listGatewayDomains(),
|
|
7327
|
-
]);
|
|
7328
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, accessList);
|
|
7329
|
-
const urls = Array.from(new Set((accessList.APISet || []).flatMap((api) => domainInfo.domains.map((domain) => {
|
|
7330
|
-
const normalizedPath = normalizeAccessPath(api.Path);
|
|
7331
|
-
return `https://${domain}${normalizedPath}`;
|
|
7332
|
-
}))));
|
|
7333
|
-
return buildEnvelope({
|
|
7334
|
-
action: input.action,
|
|
7335
|
-
targetType: input.targetType ?? "function",
|
|
7336
|
-
targetName: input.targetName,
|
|
7337
|
-
apis: accessList.APISet || [],
|
|
7338
|
-
total: accessList.Total || 0,
|
|
7339
|
-
domains: domainInfo.domains,
|
|
7377
|
+
routes: matches,
|
|
7378
|
+
total: matches.length,
|
|
7340
7379
|
urls,
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7380
|
+
raw: result,
|
|
7381
|
+
}, matches.length === 0
|
|
7382
|
+
? "未找到对应路由"
|
|
7383
|
+
: matches.length === 1
|
|
7384
|
+
? "已获取路由详情"
|
|
7385
|
+
: `匹配到 ${matches.length} 条路由,请补充 path 或 domain 精确定位`, [
|
|
7347
7386
|
{
|
|
7348
7387
|
tool: "manageGateway",
|
|
7349
|
-
action: "
|
|
7350
|
-
reason: "
|
|
7388
|
+
action: "createRoute",
|
|
7389
|
+
reason: "为该目标新增 Domain/Route 访问路径",
|
|
7351
7390
|
},
|
|
7352
7391
|
]);
|
|
7353
7392
|
}
|
|
@@ -7357,78 +7396,74 @@ function registerGatewayTools(server) {
|
|
|
7357
7396
|
};
|
|
7358
7397
|
const handleManageGateway = async (input) => {
|
|
7359
7398
|
switch (input.action) {
|
|
7360
|
-
case "
|
|
7361
|
-
if (!input.targetName || !input.targetType) {
|
|
7362
|
-
throw new Error("action=createAccess 时必须提供 targetType 和 targetName");
|
|
7363
|
-
}
|
|
7364
|
-
if (!input.type) {
|
|
7365
|
-
throw new Error("action=createAccess 且 targetType=function 时必须显式提供 type。HTTP 云函数传 HTTP;Event 函数传 Event。省略会默认按 Event 路由处理,可能让 HTTP 云函数访问后返回 FUNCTION_PARAM_INVALID。");
|
|
7366
|
-
}
|
|
7399
|
+
case "createRoute": {
|
|
7367
7400
|
const cloudbase = await getManager();
|
|
7368
|
-
const
|
|
7401
|
+
const payload = await normalizeRoutePayload(input);
|
|
7369
7402
|
let result;
|
|
7370
7403
|
try {
|
|
7371
|
-
result = await cloudbase.
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
type: (input.type === "HTTP" ? 6 : 1),
|
|
7375
|
-
auth: input.auth,
|
|
7404
|
+
result = await cloudbase.env.createHttpServiceRoute({
|
|
7405
|
+
EnvId: payload.EnvId,
|
|
7406
|
+
Domain: payload.Domain,
|
|
7376
7407
|
});
|
|
7377
7408
|
}
|
|
7378
7409
|
catch (err) {
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7410
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7411
|
+
if (message.includes("An error has occurred")) {
|
|
7412
|
+
let hint = "为目标资源配置访问路由失败(后端内部错误)。请确保:1) 目标云函数已成功创建并处于 Active 状态;2) 环境默认 HTTP 域名已完成初始化(IsDefault);3) 该访问路径未被占用。";
|
|
7413
|
+
if (payload.resolved.upstreamResourceType === "WEB_SCF" ||
|
|
7414
|
+
payload.resolved.upstreamResourceType === "SCF") {
|
|
7415
|
+
hint +=
|
|
7416
|
+
"此外注意:HTTP 云函数必须用 upstreamResourceType=WEB_SCF,Event 云函数必须用 SCF;互标会导致此错误。";
|
|
7383
7417
|
}
|
|
7384
|
-
throw new Error(`${hint} 原始错误:${
|
|
7418
|
+
throw new Error(`${hint} 原始错误:${message}`);
|
|
7385
7419
|
}
|
|
7386
7420
|
throw err;
|
|
7387
7421
|
}
|
|
7388
7422
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7389
7423
|
return buildEnvelope({
|
|
7390
7424
|
action: input.action,
|
|
7391
|
-
|
|
7392
|
-
targetName:
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
|
|
7400
|
-
},
|
|
7401
|
-
{
|
|
7402
|
-
tool: "queryPermissions",
|
|
7403
|
-
action: "getResourcePermission",
|
|
7404
|
-
reason: "确认函数安全规则是否允许预期访问方;网关 auth=false 不等于函数已允许匿名访问",
|
|
7405
|
-
},
|
|
7406
|
-
{
|
|
7407
|
-
tool: "managePermissions",
|
|
7408
|
-
action: "updateResourcePermission",
|
|
7409
|
-
reason: "只有在确认需要匿名或浏览器直连访问时,才按实际安全要求更新函数权限",
|
|
7410
|
-
},
|
|
7411
|
-
]);
|
|
7412
|
-
}
|
|
7413
|
-
case "createRoute": {
|
|
7414
|
-
const cloudbase = await getManager();
|
|
7415
|
-
const result = await cloudbase.env.createHttpServiceRoute((await normalizeRoutePayload(input.route, input, input.domain)));
|
|
7416
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7417
|
-
return buildEnvelope({
|
|
7418
|
-
action: input.action,
|
|
7419
|
-
route: input.route ?? null,
|
|
7425
|
+
model: "httpServiceRoute",
|
|
7426
|
+
targetName: payload.resolved.upstreamResourceName,
|
|
7427
|
+
domain: payload.resolved.domain,
|
|
7428
|
+
path: payload.resolved.path,
|
|
7429
|
+
upstreamResourceType: payload.resolved.upstreamResourceType,
|
|
7430
|
+
upstreamResourceName: payload.resolved.upstreamResourceName,
|
|
7431
|
+
auth: payload.resolved.enableAuth ?? null,
|
|
7432
|
+
enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
|
|
7420
7433
|
raw: result,
|
|
7421
|
-
},
|
|
7434
|
+
}, `已为目标 ${payload.resolved.upstreamResourceName} 在域名 ${payload.resolved.domain} 创建路由 ${payload.resolved.path}(${payload.resolved.upstreamResourceType})` +
|
|
7435
|
+
(payload.resolved.enablePathTransmission === true
|
|
7436
|
+
? ";已开启路径透传(后端收到完整请求路径)"
|
|
7437
|
+
: payload.resolved.enablePathTransmission === false
|
|
7438
|
+
? ";路径透传关闭(网关会剥掉触发路径前缀后再转发给后端)"
|
|
7439
|
+
: ";路径透传未显式设置(平台默认 false,会剥掉触发路径前缀)") +
|
|
7440
|
+
`。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。该操作只创建网关入口,不会自动放开上游权限;若上游是云函数且需要匿名或浏览器直接访问,请继续检查函数资源权限。`, routeMutationNextActions(payload.resolved.upstreamResourceName));
|
|
7422
7441
|
}
|
|
7423
7442
|
case "updateRoute": {
|
|
7424
7443
|
const cloudbase = await getManager();
|
|
7425
|
-
const
|
|
7444
|
+
const payload = await normalizeRoutePayload(input);
|
|
7445
|
+
const result = await cloudbase.env.modifyHttpServiceRoute({
|
|
7446
|
+
EnvId: payload.EnvId,
|
|
7447
|
+
Domain: payload.Domain,
|
|
7448
|
+
});
|
|
7426
7449
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7427
7450
|
return buildEnvelope({
|
|
7428
7451
|
action: input.action,
|
|
7429
|
-
|
|
7452
|
+
model: "httpServiceRoute",
|
|
7453
|
+
domain: payload.resolved.domain,
|
|
7454
|
+
path: payload.resolved.path,
|
|
7455
|
+
upstreamResourceType: payload.resolved.upstreamResourceType,
|
|
7456
|
+
upstreamResourceName: payload.resolved.upstreamResourceName,
|
|
7457
|
+
auth: payload.resolved.enableAuth ?? null,
|
|
7458
|
+
enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
|
|
7430
7459
|
raw: result,
|
|
7431
|
-
},
|
|
7460
|
+
}, `HTTP 路由更新成功(${payload.resolved.domain}${payload.resolved.path}` +
|
|
7461
|
+
(payload.resolved.enablePathTransmission === true
|
|
7462
|
+
? ",路径透传=开启"
|
|
7463
|
+
: payload.resolved.enablePathTransmission === false
|
|
7464
|
+
? ",路径透传=关闭"
|
|
7465
|
+
: "") +
|
|
7466
|
+
`)`, routeMutationNextActions(payload.resolved.upstreamResourceName));
|
|
7432
7467
|
}
|
|
7433
7468
|
case "deleteRoute": {
|
|
7434
7469
|
const routePath = input.route?.path ?? input.path;
|
|
@@ -7437,16 +7472,18 @@ function registerGatewayTools(server) {
|
|
|
7437
7472
|
}
|
|
7438
7473
|
const cloudbase = await getManager();
|
|
7439
7474
|
const domain = await resolveRouteDomain(input.domain);
|
|
7475
|
+
const normalizedPath = normalizeAccessPath(routePath);
|
|
7440
7476
|
const result = await cloudbase.env.deleteHttpServiceRoute({
|
|
7441
7477
|
EnvId: await resolveEnvId(),
|
|
7442
7478
|
Domain: domain,
|
|
7443
|
-
Paths: [
|
|
7479
|
+
Paths: [normalizedPath],
|
|
7444
7480
|
});
|
|
7445
7481
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7446
7482
|
return buildEnvelope({
|
|
7447
7483
|
action: input.action,
|
|
7484
|
+
model: "httpServiceRoute",
|
|
7448
7485
|
domain,
|
|
7449
|
-
path:
|
|
7486
|
+
path: normalizedPath,
|
|
7450
7487
|
raw: result,
|
|
7451
7488
|
}, "HTTP 路由删除成功");
|
|
7452
7489
|
}
|
|
@@ -7486,114 +7523,32 @@ function registerGatewayTools(server) {
|
|
|
7486
7523
|
raw: result,
|
|
7487
7524
|
}, "自定义域名删除成功");
|
|
7488
7525
|
}
|
|
7489
|
-
case "deleteAccess": {
|
|
7490
|
-
const cloudbase = await getManager();
|
|
7491
|
-
const accessPath = input.path ? normalizeAccessPath(input.path) : undefined;
|
|
7492
|
-
let resolvedAccessId = input.accessId;
|
|
7493
|
-
let resolvedName;
|
|
7494
|
-
if (!resolvedAccessId) {
|
|
7495
|
-
if (!input.targetName && !accessPath) {
|
|
7496
|
-
throw new Error("action=deleteAccess 时至少需要提供 accessId,或提供 targetName/path 用于定位入口");
|
|
7497
|
-
}
|
|
7498
|
-
const accessList = await cloudbase.access.getAccessList({
|
|
7499
|
-
...(input.targetName ? { name: input.targetName } : {}),
|
|
7500
|
-
...(accessPath ? { path: accessPath } : {}),
|
|
7501
|
-
});
|
|
7502
|
-
const candidates = (accessList.APISet ?? []).filter((item) => {
|
|
7503
|
-
if (!accessPath) {
|
|
7504
|
-
return true;
|
|
7505
|
-
}
|
|
7506
|
-
return normalizeAccessPath(item.Path) === accessPath;
|
|
7507
|
-
});
|
|
7508
|
-
if (candidates.length === 0) {
|
|
7509
|
-
throw new Error("未找到可删除的访问入口,请确认 targetName/path 或直接传 accessId");
|
|
7510
|
-
}
|
|
7511
|
-
if (candidates.length > 1 && !accessPath) {
|
|
7512
|
-
throw new Error("匹配到多个访问入口,请补充 path 或直接传 accessId");
|
|
7513
|
-
}
|
|
7514
|
-
resolvedAccessId = candidates[0]?.APIId;
|
|
7515
|
-
resolvedName = candidates[0]?.Name;
|
|
7516
|
-
}
|
|
7517
|
-
else {
|
|
7518
|
-
// 仅传入 accessId 时,先查询获取 name 等信息,确保删除参数完整
|
|
7519
|
-
try {
|
|
7520
|
-
const accessList = await cloudbase.access.getAccessList({});
|
|
7521
|
-
const match = (accessList.APISet ?? []).find((item) => item.APIId === resolvedAccessId);
|
|
7522
|
-
if (match) {
|
|
7523
|
-
resolvedName = match.Name;
|
|
7524
|
-
}
|
|
7525
|
-
}
|
|
7526
|
-
catch {
|
|
7527
|
-
// 查询失败不影响主流程,继续用 accessId 删除
|
|
7528
|
-
}
|
|
7529
|
-
}
|
|
7530
|
-
if (!resolvedAccessId) {
|
|
7531
|
-
throw new Error("无法解析访问入口 accessId,请改为显式传入 accessId");
|
|
7532
|
-
}
|
|
7533
|
-
const result = await cloudbase.access.deleteAccess({
|
|
7534
|
-
...(resolvedName ? { name: resolvedName } : {}),
|
|
7535
|
-
apiId: resolvedAccessId,
|
|
7536
|
-
});
|
|
7537
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7538
|
-
return buildEnvelope({
|
|
7539
|
-
action: input.action,
|
|
7540
|
-
targetName: resolvedName ?? input.targetName ?? null,
|
|
7541
|
-
path: accessPath ?? null,
|
|
7542
|
-
accessId: resolvedAccessId,
|
|
7543
|
-
raw: result,
|
|
7544
|
-
}, "网关访问入口删除成功");
|
|
7545
|
-
}
|
|
7546
|
-
case "updatePathAuth": {
|
|
7547
|
-
if (!input.targetName && !input.path) {
|
|
7548
|
-
throw new Error("action=updatePathAuth 时至少需要提供 targetName 或 path");
|
|
7549
|
-
}
|
|
7550
|
-
if (input.auth === undefined) {
|
|
7551
|
-
throw new Error("action=updatePathAuth 时必须提供 auth");
|
|
7552
|
-
}
|
|
7553
|
-
const cloudbase = await getManager();
|
|
7554
|
-
const accessPath = input.path ? normalizeAccessPath(input.path) : undefined;
|
|
7555
|
-
const accessList = await cloudbase.access.getAccessList({
|
|
7556
|
-
...(input.targetName ? { name: input.targetName } : {}),
|
|
7557
|
-
...(accessPath ? { path: accessPath } : {}),
|
|
7558
|
-
});
|
|
7559
|
-
const apiIds = (accessList.APISet ?? []).map((item) => item.APIId).filter(Boolean);
|
|
7560
|
-
if (apiIds.length === 0) {
|
|
7561
|
-
throw new Error("未找到可更新鉴权状态的访问入口");
|
|
7562
|
-
}
|
|
7563
|
-
const result = await cloudbase.access.switchPathAuth({
|
|
7564
|
-
apiIds,
|
|
7565
|
-
auth: input.auth,
|
|
7566
|
-
});
|
|
7567
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7568
|
-
return buildEnvelope({
|
|
7569
|
-
action: input.action,
|
|
7570
|
-
targetName: input.targetName ?? null,
|
|
7571
|
-
path: accessPath ?? null,
|
|
7572
|
-
auth: input.auth,
|
|
7573
|
-
apiIds,
|
|
7574
|
-
raw: result,
|
|
7575
|
-
}, "网关路径鉴权更新成功");
|
|
7576
|
-
}
|
|
7577
7526
|
default:
|
|
7578
7527
|
throw new Error(`不支持的操作类型: ${input.action}`);
|
|
7579
7528
|
}
|
|
7580
7529
|
};
|
|
7581
7530
|
server.registerTool?.("queryGateway", {
|
|
7582
7531
|
title: "查询 CloudBase 网关",
|
|
7583
|
-
description: "CloudBase
|
|
7532
|
+
description: "CloudBase HTTP 网关统一只读入口(Domain/Route)。查询域名下路径路由及其上游:" +
|
|
7533
|
+
"WEB_SCF/SCF=云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
|
|
7534
|
+
"主键为 Domain + Path;listRoutes / getRoute / listCustomDomains。",
|
|
7584
7535
|
inputSchema: {
|
|
7585
7536
|
action: zod_1.z
|
|
7586
7537
|
.enum(QUERY_GATEWAY_ACTIONS)
|
|
7587
|
-
.describe("
|
|
7588
|
-
targetType: zod_1.z
|
|
7589
|
-
.enum(["function"])
|
|
7590
|
-
.optional()
|
|
7591
|
-
.describe("目标资源类型。当前支持 function,后续可扩展"),
|
|
7538
|
+
.describe("只读操作类型:listRoutes、getRoute、listCustomDomains"),
|
|
7592
7539
|
targetName: zod_1.z
|
|
7593
7540
|
.string()
|
|
7594
7541
|
.optional()
|
|
7595
|
-
.describe("
|
|
7542
|
+
.describe("上游资源名过滤(UpstreamResourceName):云函数名、云托管服务名,或静态托管实例名(常见 staticstore)。"),
|
|
7596
7543
|
routeId: zod_1.z.string().optional().describe("路由 ID。getRoute 时可选"),
|
|
7544
|
+
path: zod_1.z
|
|
7545
|
+
.string()
|
|
7546
|
+
.optional()
|
|
7547
|
+
.describe("路由路径。getRoute / listRoutes 过滤时可选"),
|
|
7548
|
+
domain: zod_1.z
|
|
7549
|
+
.string()
|
|
7550
|
+
.optional()
|
|
7551
|
+
.describe("域名。getRoute / listRoutes 过滤时可选"),
|
|
7597
7552
|
},
|
|
7598
7553
|
annotations: {
|
|
7599
7554
|
readOnlyHint: true,
|
|
@@ -7603,45 +7558,76 @@ function registerGatewayTools(server) {
|
|
|
7603
7558
|
}, async (input) => withEnvelope(() => handleQueryGateway(input)));
|
|
7604
7559
|
server.registerTool?.("manageGateway", {
|
|
7605
7560
|
title: "管理 CloudBase 网关",
|
|
7606
|
-
description: "CloudBase
|
|
7561
|
+
description: "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path 转到上游;未传 domain 时用 IsDefault 默认域名。" +
|
|
7562
|
+
"上游类型只用一个参数 upstreamResourceType(也可写在 route.upstreamResourceType,route 优先):" +
|
|
7563
|
+
"WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器;" +
|
|
7564
|
+
"配合 targetName 或 route.serviceName(云函数名/云托管服务名/静态托管实例名,常见 staticstore)。" +
|
|
7565
|
+
"createRoute 只建网关入口,不改上游权限。" +
|
|
7566
|
+
"enablePathTransmission:默认 false 剥触发路径前缀;true 透传完整路径(CBR 多路由、WEB_SCF 自管子路径常需 true;STATIC_STORE 自定义触发路径映射站点根通常 false)。" +
|
|
7567
|
+
"⚠️ SSL 自定义域名用 bindCustomDomain;CORS/安全域名用 envDomainManagement。",
|
|
7607
7568
|
inputSchema: {
|
|
7608
7569
|
action: zod_1.z
|
|
7609
7570
|
.enum(MANAGE_GATEWAY_ACTIONS)
|
|
7610
|
-
.describe(
|
|
7611
|
-
|
|
7612
|
-
.enum(["function"])
|
|
7613
|
-
.optional()
|
|
7614
|
-
.describe("目标资源类型。当前支持 function,后续可扩展"),
|
|
7571
|
+
.describe("写操作:createRoute/updateRoute/deleteRoute 管理路由;bindCustomDomain/deleteCustomDomain 管理自定义域名。" +
|
|
7572
|
+
"createRoute/updateRoute 必须提供 upstreamResourceType。"),
|
|
7615
7573
|
targetName: zod_1.z
|
|
7616
7574
|
.string()
|
|
7617
7575
|
.optional()
|
|
7618
|
-
.describe("
|
|
7576
|
+
.describe("上游资源名称(UpstreamResourceName),与 route.serviceName 二选一(route 优先)。" +
|
|
7577
|
+
"云函数=函数名;云托管=服务名;静态托管=实例名(常见 staticstore)。不会自动推断上游类型。"),
|
|
7619
7578
|
path: zod_1.z
|
|
7620
7579
|
.string()
|
|
7621
7580
|
.optional()
|
|
7622
|
-
.describe("
|
|
7623
|
-
|
|
7624
|
-
|
|
7581
|
+
.describe("触发路径(网关匹配前缀),默认 /{上游名}。例:云函数 /api/hello、云托管 /api、静态托管 / 或 /app。" +
|
|
7582
|
+
"只建网关入口;与 enablePathTransmission 共同决定上游实际收到的路径。"),
|
|
7583
|
+
upstreamResourceType: zod_1.z
|
|
7584
|
+
.enum(UPSTREAM_RESOURCE_TYPES)
|
|
7625
7585
|
.optional()
|
|
7626
|
-
.describe("
|
|
7586
|
+
.describe("上游类型(与 route.upstreamResourceType 二选一,route 优先)。" +
|
|
7587
|
+
"WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
|
|
7588
|
+
"createRoute/updateRoute 必填其一;勿把 manageFunctions 的 type=HTTP|Event 传到本字段。"),
|
|
7627
7589
|
auth: zod_1.z
|
|
7628
7590
|
.boolean()
|
|
7629
7591
|
.optional()
|
|
7630
|
-
.describe("
|
|
7592
|
+
.describe("网关路径鉴权(EnableAuth)。匿名/浏览器公网访问通常 false。" +
|
|
7593
|
+
"只控制网关入口;云函数安全规则、云托管鉴权、静态托管权限需各自工具另行配置。"),
|
|
7594
|
+
enablePathTransmission: zod_1.z
|
|
7595
|
+
.boolean()
|
|
7596
|
+
.optional()
|
|
7597
|
+
.describe("路径透传(EnablePathTransmission),平台默认 false。例 path=/api 且请求 /api/users:" +
|
|
7598
|
+
"false→上游收到 /users;true→上游收到 /api/users。" +
|
|
7599
|
+
"CBR 云托管(Express 等自管子路由)与 WEB_SCF 多路径函数常需 true;" +
|
|
7600
|
+
"STATIC_STORE 把触发路径映射到站点根目录(如 /app → 托管 /)时通常 false;" +
|
|
7601
|
+
"单入口/根路径处理保持 false。也可用 route.enablePathTransmission(route 优先)。"),
|
|
7631
7602
|
route: zod_1.z
|
|
7632
7603
|
.object({
|
|
7633
|
-
routeId: zod_1.z.string().optional(),
|
|
7634
7604
|
path: zod_1.z.string().optional(),
|
|
7635
|
-
|
|
7636
|
-
|
|
7605
|
+
serviceName: zod_1.z
|
|
7606
|
+
.string()
|
|
7607
|
+
.optional()
|
|
7608
|
+
.describe("上游实例名:云函数名 / 云托管服务名 / 静态托管实例名(常见 staticstore)/ LH 实例。优先于顶层 targetName。"),
|
|
7609
|
+
upstreamResourceType: zod_1.z
|
|
7610
|
+
.enum(UPSTREAM_RESOURCE_TYPES)
|
|
7611
|
+
.optional()
|
|
7612
|
+
.describe("同顶层 upstreamResourceType。route 内设置时优先于顶层。"),
|
|
7637
7613
|
auth: zod_1.z.boolean().optional(),
|
|
7614
|
+
enablePathTransmission: zod_1.z
|
|
7615
|
+
.boolean()
|
|
7616
|
+
.optional()
|
|
7617
|
+
.describe("同顶层 enablePathTransmission。route 内设置时优先于顶层。"),
|
|
7638
7618
|
})
|
|
7639
7619
|
.optional()
|
|
7640
|
-
.describe("
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7620
|
+
.describe("路由对象(可选写法)。例:云函数 {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"};" +
|
|
7621
|
+
"云托管 {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"};" +
|
|
7622
|
+
"静态托管 {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"}。"),
|
|
7623
|
+
domain: zod_1.z
|
|
7624
|
+
.string()
|
|
7625
|
+
.optional()
|
|
7626
|
+
.describe("域名。省略时自动使用环境 IsDefault 默认 HTTP 域名;自定义域名场景请显式传入"),
|
|
7627
|
+
certificateId: zod_1.z
|
|
7628
|
+
.string()
|
|
7629
|
+
.optional()
|
|
7630
|
+
.describe("证书 ID。bindCustomDomain 时必填"),
|
|
7645
7631
|
},
|
|
7646
7632
|
annotations: {
|
|
7647
7633
|
readOnlyHint: false,
|
|
@@ -7695,70 +7681,6 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_fs__;
|
|
|
7695
7681
|
|
|
7696
7682
|
module.exports = __WEBPACK_EXTERNAL_MODULE__modelcontextprotocol_sdk_server_mcp_js_45c326f0__;
|
|
7697
7683
|
|
|
7698
|
-
/***/ }),
|
|
7699
|
-
|
|
7700
|
-
/***/ 4760:
|
|
7701
|
-
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
7705
|
-
exports.registerInviteCodeTools = registerInviteCodeTools;
|
|
7706
|
-
const zod_1 = __webpack_require__(2971);
|
|
7707
|
-
const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
7708
|
-
function registerInviteCodeTools(server) {
|
|
7709
|
-
const cloudBaseOptions = server.cloudBaseOptions;
|
|
7710
|
-
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
7711
|
-
server.registerTool?.("activateInviteCode", {
|
|
7712
|
-
title: "激活邀请码",
|
|
7713
|
-
description: "云开发 AI编程激励计划,通过邀请码激活用户激励。",
|
|
7714
|
-
inputSchema: {
|
|
7715
|
-
InviteCode: zod_1.z.string().describe("待激活的邀请码")
|
|
7716
|
-
},
|
|
7717
|
-
annotations: {
|
|
7718
|
-
readOnlyHint: false,
|
|
7719
|
-
destructiveHint: false,
|
|
7720
|
-
idempotentHint: false,
|
|
7721
|
-
openWorldHint: true,
|
|
7722
|
-
category: "invite-code"
|
|
7723
|
-
}
|
|
7724
|
-
}, async ({ InviteCode }) => {
|
|
7725
|
-
if (!InviteCode) {
|
|
7726
|
-
return {
|
|
7727
|
-
content: [
|
|
7728
|
-
{
|
|
7729
|
-
type: "text",
|
|
7730
|
-
text: JSON.stringify({
|
|
7731
|
-
ErrorCode: 'MissingParameter',
|
|
7732
|
-
ErrorMsg: '邀请码为必填项',
|
|
7733
|
-
RequestId: ''
|
|
7734
|
-
}, null, 2)
|
|
7735
|
-
}
|
|
7736
|
-
]
|
|
7737
|
-
};
|
|
7738
|
-
}
|
|
7739
|
-
const manager = await getManager();
|
|
7740
|
-
const EnvId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
7741
|
-
const result = await manager.commonService().call({
|
|
7742
|
-
Action: 'ActivateInviteCode',
|
|
7743
|
-
Param: { InviteCode, EnvId }
|
|
7744
|
-
});
|
|
7745
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7746
|
-
return {
|
|
7747
|
-
content: [
|
|
7748
|
-
{
|
|
7749
|
-
type: "text",
|
|
7750
|
-
text: JSON.stringify({
|
|
7751
|
-
ErrorCode: result?.ErrorCode || '',
|
|
7752
|
-
ErrorMsg: result?.ErrorMsg || '',
|
|
7753
|
-
RequestId: result?.RequestId || ''
|
|
7754
|
-
}, null, 2)
|
|
7755
|
-
}
|
|
7756
|
-
]
|
|
7757
|
-
};
|
|
7758
|
-
});
|
|
7759
|
-
}
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
7684
|
/***/ }),
|
|
7763
7685
|
|
|
7764
7686
|
/***/ 4848:
|
|
@@ -8288,6 +8210,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
8288
8210
|
};
|
|
8289
8211
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
8290
8212
|
exports.CLOUDRUN_ACCESS_TYPES = exports.CLOUDRUN_SERVICE_TYPES = void 0;
|
|
8213
|
+
exports.detectCloudRunDbNetworkRisk = detectCloudRunDbNetworkRisk;
|
|
8291
8214
|
exports.registerCloudRunTools = registerCloudRunTools;
|
|
8292
8215
|
const child_process_1 = __webpack_require__(5814);
|
|
8293
8216
|
const fs_1 = __importDefault(__webpack_require__(4421));
|
|
@@ -8330,7 +8253,7 @@ const ManageCloudRunInputSchema = {
|
|
|
8330
8253
|
})).optional().describe('扩缩容配置数组,用于配置服务的自动扩缩容策略。可配置多个扩缩容策略'),
|
|
8331
8254
|
CustomLogs: zod_1.z.string().optional().describe('自定义日志配置,用于配置服务的日志收集和存储策略'),
|
|
8332
8255
|
Port: zod_1.z.number().min(1).max(65535).optional().describe('服务监听端口配置。函数型服务固定为3000,容器型服务可自定义。服务代码必须监听此端口才能正常接收请求'),
|
|
8333
|
-
EnvParams: zod_1.z.string().optional().describe('环境变量配置,JSON字符串格式。用于传递配置信息给服务代码,如\'{"DATABASE_URL":"
|
|
8256
|
+
EnvParams: zod_1.z.string().optional().describe('环境变量配置,JSON字符串格式。用于传递配置信息给服务代码,如\'{"DATABASE_URL":"postgres://user:pass@10.x.x.x:5432/db","NODE_ENV":"production"}\'。SDK v5.6.1+ 会自动对传入的环境变量进行 AES-256-CBC 加密传输。⚠️ 若 EnvParams 含 DATABASE_URL / MYSQL_* / POSTGRES_* / REDIS_* 等传统 TCP 连库变量,必须同时配置 VpcConf,否则实例通常无法访问 VPC 内数据库'),
|
|
8334
8257
|
Dockerfile: zod_1.z.string().optional().describe('Dockerfile文件名配置,仅容器型服务需要。指定用于构建容器镜像的Dockerfile文件路径,默认为项目根目录下的Dockerfile'),
|
|
8335
8258
|
BuildDir: zod_1.z.string().optional().describe('构建目录配置,指定代码构建的目录路径。当代码结构与标准不同时使用,默认为项目根目录'),
|
|
8336
8259
|
InternalAccess: zod_1.z.string().optional().describe('内网访问开关配置,控制是否启用内网访问。true=启用内网访问(可通过云开发SDK直接调用),false=关闭内网访问(仅公网访问)'),
|
|
@@ -8354,9 +8277,9 @@ const ManageCloudRunInputSchema = {
|
|
|
8354
8277
|
ReplicaNum: zod_1.z.number().min(0).describe('定时扩缩容的目标副本数,最小值0(缩容到0)')
|
|
8355
8278
|
})).optional().describe('定时扩缩容配置数组,用于配置服务的定时自动扩缩容策略。可配置多个时间段的扩缩容计划,支持每日/每周/每月循环'),
|
|
8356
8279
|
VpcConf: zod_1.z.object({
|
|
8357
|
-
VpcId: zod_1.z.string().describe('VPC网络ID
|
|
8358
|
-
SubnetId: zod_1.z.string().describe('子网ID
|
|
8359
|
-
}).optional().describe('VPC
|
|
8280
|
+
VpcId: zod_1.z.string().describe('VPC网络ID,格式如 vpc-xxxxxxxx。必须与目标数据库/Redis 处于同一地域,并优先选择同一 VPC。禁止猜测或使用占位符;须来自数据库控制台、已有资源详情、callCloudApi 或用户确认。建议首次创建即配置;已存在服务也可在 deploy 时传入,部署后必须用 queryCloudRun detail 复核是否生效'),
|
|
8281
|
+
SubnetId: zod_1.z.string().describe('子网ID,格式如 subnet-xxxxxxxx。云托管实例将占用该子网 IP,需确保有足够可用 IP'),
|
|
8282
|
+
}).optional().describe('VPC网络配置(实例出网/私有网络)。用于让云托管实例接入指定 VPC,从而内网访问 MySQL/PostgreSQL/Redis/CVM 等资源。与 OpenAccessTypes(外部如何访问本服务)是不同概念。TCP 连库场景必须配置。禁止猜测 VpcId/SubnetId,须来自数据库控制台、已有资源详情、callCloudApi 或用户确认。MCP 在创建时会将 VpcConf 映射为 SDK vpcInfo(CreateType=2);更新后必须用 queryCloudRun detail 复核 ServerConfig.VpcConf,未生效再走控制台网络设置或删建兜底'),
|
|
8360
8283
|
VolumesConf: zod_1.z.array(zod_1.z.object({
|
|
8361
8284
|
VolumeName: zod_1.z.string().describe('存储卷名称'),
|
|
8362
8285
|
VolumeType: zod_1.z.string().describe('存储卷类型,如CFS表示云文件存储'),
|
|
@@ -8460,6 +8383,74 @@ function buildManageCloudRunErrorMessage(action, serverName, error) {
|
|
|
8460
8383
|
}
|
|
8461
8384
|
return `[manageCloudRun/${action}] ${baseMessage}\n建议:${suggestions.join(" ")}`;
|
|
8462
8385
|
}
|
|
8386
|
+
const CLOUDRUN_DB_ENV_KEY_PATTERN = /^(DATABASE_URL|DB_HOST|DB_PORT|DB_USER|DB_PASSWORD|DB_NAME|MYSQL_|POSTGRES_|PGHOST|PGPORT|PGUSER|PGPASSWORD|PGDATABASE|PG_|REDIS_|MONGO_|MONGODB_|SQLALCHEMY_DATABASE_URI|SPRING_DATASOURCE_)/i;
|
|
8387
|
+
const CLOUDRUN_DB_URL_PATTERN = /(mysql|mariadb|postgres|postgresql|mongodb(\+srv)?|redis|rediss):\/\//i;
|
|
8388
|
+
/**
|
|
8389
|
+
* Detect likely TCP database/cache env usage without VpcConf.
|
|
8390
|
+
* Soft signal for AI agents — does not block deploy.
|
|
8391
|
+
*/
|
|
8392
|
+
function detectCloudRunDbNetworkRisk(options) {
|
|
8393
|
+
const vpcId = options.vpcConf?.VpcId?.trim();
|
|
8394
|
+
const subnetId = options.vpcConf?.SubnetId?.trim();
|
|
8395
|
+
if (vpcId && subnetId) {
|
|
8396
|
+
return null;
|
|
8397
|
+
}
|
|
8398
|
+
const rawEnv = options.envParams?.trim();
|
|
8399
|
+
if (!rawEnv) {
|
|
8400
|
+
return null;
|
|
8401
|
+
}
|
|
8402
|
+
let parsed;
|
|
8403
|
+
try {
|
|
8404
|
+
const value = JSON.parse(rawEnv);
|
|
8405
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
8406
|
+
return null;
|
|
8407
|
+
}
|
|
8408
|
+
parsed = value;
|
|
8409
|
+
}
|
|
8410
|
+
catch {
|
|
8411
|
+
// Non-JSON EnvParams still may embed connection URLs as plain text.
|
|
8412
|
+
if (!CLOUDRUN_DB_URL_PATTERN.test(rawEnv)) {
|
|
8413
|
+
return null;
|
|
8414
|
+
}
|
|
8415
|
+
return {
|
|
8416
|
+
code: "MISSING_VPC_FOR_DB_ENV",
|
|
8417
|
+
message: "EnvParams appears to include a database/cache connection URL, but serverConfig.VpcConf is missing. CloudRun instances usually cannot reach VPC-private MySQL/PostgreSQL/Redis without VpcConf.",
|
|
8418
|
+
matchedKeys: ["<non-json-envParams>"],
|
|
8419
|
+
remediation: [
|
|
8420
|
+
"Set serverConfig.VpcConf to the same region/VPC (and a subnet with free IPs) as the database.",
|
|
8421
|
+
"Do NOT invent VpcId/SubnetId. Resolve real IDs from the DB console, resource detail, callCloudApi, or the user.",
|
|
8422
|
+
"Use the database private/intranet hostname in EnvParams, not localhost or docker-compose service names.",
|
|
8423
|
+
"Ensure the DB security group allows the CloudRun subnet on the DB port.",
|
|
8424
|
+
"Re-deploy after VpcConf is set. OpenAccessTypes alone does not provide VPC egress to databases.",
|
|
8425
|
+
],
|
|
8426
|
+
};
|
|
8427
|
+
}
|
|
8428
|
+
const matchedKeys = [];
|
|
8429
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
8430
|
+
if (CLOUDRUN_DB_ENV_KEY_PATTERN.test(key)) {
|
|
8431
|
+
matchedKeys.push(key);
|
|
8432
|
+
continue;
|
|
8433
|
+
}
|
|
8434
|
+
if (typeof value === "string" && CLOUDRUN_DB_URL_PATTERN.test(value)) {
|
|
8435
|
+
matchedKeys.push(key);
|
|
8436
|
+
}
|
|
8437
|
+
}
|
|
8438
|
+
if (matchedKeys.length === 0) {
|
|
8439
|
+
return null;
|
|
8440
|
+
}
|
|
8441
|
+
return {
|
|
8442
|
+
code: "MISSING_VPC_FOR_DB_ENV",
|
|
8443
|
+
message: "EnvParams includes database/cache connection settings, but serverConfig.VpcConf is missing. Deploy may succeed while runtime DB connections fail.",
|
|
8444
|
+
matchedKeys,
|
|
8445
|
+
remediation: [
|
|
8446
|
+
"Set serverConfig.VpcConf.VpcId and SubnetId to the database VPC/subnet (same region).",
|
|
8447
|
+
"Do NOT invent VpcId/SubnetId. Resolve real IDs from the DB console, resource detail, callCloudApi, or the user.",
|
|
8448
|
+
"Use the private DB endpoint in EnvParams.",
|
|
8449
|
+
"Confirm security group / allowlist permits CloudRun subnet access to the DB port.",
|
|
8450
|
+
"Do not confuse OpenAccessTypes (ingress) with VpcConf (egress to VPC resources).",
|
|
8451
|
+
],
|
|
8452
|
+
};
|
|
8453
|
+
}
|
|
8463
8454
|
function getCloudRunQueryServerName(input) {
|
|
8464
8455
|
return input.detailServerName || input.serverName;
|
|
8465
8456
|
}
|
|
@@ -8957,6 +8948,17 @@ for await (let x of res.textStream) {
|
|
|
8957
8948
|
if (input.serverConfig) {
|
|
8958
8949
|
deployParams.serverConfig = input.serverConfig;
|
|
8959
8950
|
}
|
|
8951
|
+
// Manager SDK create path prefers top-level vpcInfo (CreateCloudRunServer.VpcInfo).
|
|
8952
|
+
// Map serverConfig.VpcConf → vpcInfo so first-time create actually binds VPC.
|
|
8953
|
+
// Update path still sends Items.VpcConf; platform may ignore VPC changes on existing services.
|
|
8954
|
+
const vpcConf = input.serverConfig?.VpcConf;
|
|
8955
|
+
if (vpcConf?.VpcId?.trim() && vpcConf?.SubnetId?.trim()) {
|
|
8956
|
+
deployParams.vpcInfo = {
|
|
8957
|
+
VpcId: vpcConf.VpcId.trim(),
|
|
8958
|
+
CreateType: 2,
|
|
8959
|
+
SubnetIds: [vpcConf.SubnetId.trim()],
|
|
8960
|
+
};
|
|
8961
|
+
}
|
|
8960
8962
|
let result;
|
|
8961
8963
|
try {
|
|
8962
8964
|
result = await cloudrunService.deploy(deployParams);
|
|
@@ -9030,6 +9032,14 @@ for await (let x of res.textStream) {
|
|
|
9030
9032
|
// Notification failure should not affect deployment flow
|
|
9031
9033
|
// Error is already logged in sendDeployNotification
|
|
9032
9034
|
}
|
|
9035
|
+
const dbNetworkRisk = detectCloudRunDbNetworkRisk({
|
|
9036
|
+
envParams: input.serverConfig?.EnvParams,
|
|
9037
|
+
vpcConf: input.serverConfig?.VpcConf,
|
|
9038
|
+
});
|
|
9039
|
+
const warnings = dbNetworkRisk ? [dbNetworkRisk] : [];
|
|
9040
|
+
const warningSuffix = dbNetworkRisk
|
|
9041
|
+
? ` Warning: ${dbNetworkRisk.message} Set serverConfig.VpcConf before relying on DB connectivity.`
|
|
9042
|
+
: "";
|
|
9033
9043
|
return {
|
|
9034
9044
|
content: [
|
|
9035
9045
|
{
|
|
@@ -9042,9 +9052,10 @@ for await (let x of res.textStream) {
|
|
|
9042
9052
|
deployPath: targetPath,
|
|
9043
9053
|
serverType: serverType,
|
|
9044
9054
|
cloudbasercGenerated: true,
|
|
9045
|
-
consoleUrl
|
|
9055
|
+
consoleUrl,
|
|
9056
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
9046
9057
|
},
|
|
9047
|
-
message: `Triggered deployment for ${serverType} service '${input.serverName}' from ${targetPath}. You can follow the progress in ${consoleUrl}`
|
|
9058
|
+
message: `Triggered deployment for ${serverType} service '${input.serverName}' from ${targetPath}. You can follow the progress in ${consoleUrl}.${warningSuffix}`
|
|
9048
9059
|
}, null, 2)
|
|
9049
9060
|
}
|
|
9050
9061
|
]
|
|
@@ -9973,7 +9984,7 @@ class TelemetryReporter {
|
|
|
9973
9984
|
const nodeVersion = process.version; // Node.js版本
|
|
9974
9985
|
const arch = os_1.default.arch(); // 系统架构
|
|
9975
9986
|
// 从构建时注入的版本号获取MCP版本信息
|
|
9976
|
-
const mcpVersion = process.env.npm_package_version || "2.
|
|
9987
|
+
const mcpVersion = process.env.npm_package_version || "2.25.1" || 0;
|
|
9977
9988
|
return {
|
|
9978
9989
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
9979
9990
|
deviceId: this.deviceId,
|
|
@@ -10412,8 +10423,12 @@ exports.MANAGE_FUNCTION_ACTIONS = [
|
|
|
10412
10423
|
"incrementalDeployFunction", // 增量部署,需通过 pluginOptions.functions 注入实现
|
|
10413
10424
|
];
|
|
10414
10425
|
const VPC_SCHEMA = zod_1.z.object({
|
|
10415
|
-
vpcId: zod_1.z
|
|
10416
|
-
|
|
10426
|
+
vpcId: zod_1.z
|
|
10427
|
+
.string()
|
|
10428
|
+
.describe("VPC ID from the real database/network console (e.g. vpc-xxxxxxxx). Required for non-native TCP DB access. Do NOT invent or use placeholders."),
|
|
10429
|
+
subnetId: zod_1.z
|
|
10430
|
+
.string()
|
|
10431
|
+
.describe("Subnet ID in the same VPC as the private DB endpoint (e.g. subnet-xxxxxxxx). Do NOT invent or use placeholders."),
|
|
10417
10432
|
});
|
|
10418
10433
|
// 镜像部署值,对应 SCF Runtime=CustomImage
|
|
10419
10434
|
exports.CUSTOM_IMAGE_RUNTIME = "CustomImage";
|
|
@@ -10497,8 +10512,11 @@ const CREATE_FUNCTION_SCHEMA = zod_1.z.object({
|
|
|
10497
10512
|
})
|
|
10498
10513
|
.optional(),
|
|
10499
10514
|
timeout: zod_1.z.number().optional().describe("函数超时时间"),
|
|
10500
|
-
envVariables: zod_1.z
|
|
10501
|
-
|
|
10515
|
+
envVariables: zod_1.z
|
|
10516
|
+
.record(zod_1.z.string())
|
|
10517
|
+
.optional()
|
|
10518
|
+
.describe("环境变量。若包含 DATABASE_URL / MYSQL_* / POSTGRES_* / REDIS_* 等传统 TCP 连库变量,必须同时配置 vpc(vpcId+subnetId),且 ID 必须来自真实库/网络信息,禁止猜测。原生 app.rdb()/app.database() 不需要 VPC。"),
|
|
10519
|
+
vpc: VPC_SCHEMA.optional().describe("私有网络配置(出网)。非原生 SDK、用 TCP 访问 VPC 内 MySQL/PostgreSQL/Redis 时必填。vpcId/subnetId 必须与数据库内网 VPC 一致;未知时先查控制台或询问用户,禁止填占位符。"),
|
|
10502
10520
|
runtime: zod_1.z
|
|
10503
10521
|
.string()
|
|
10504
10522
|
.optional()
|
|
@@ -10752,7 +10770,7 @@ function registerFunctionTools(server) {
|
|
|
10752
10770
|
},
|
|
10753
10771
|
{
|
|
10754
10772
|
tool: "queryGateway",
|
|
10755
|
-
action: "
|
|
10773
|
+
action: "getRoute",
|
|
10756
10774
|
reason: "查看该函数是否已暴露网关访问入口",
|
|
10757
10775
|
},
|
|
10758
10776
|
]);
|
|
@@ -11062,7 +11080,7 @@ function registerFunctionTools(server) {
|
|
|
11062
11080
|
raw: imageResult,
|
|
11063
11081
|
}, `已基于镜像 ${createImageConfig.imageUri} 创建 HTTP 函数 ${functionName}。` +
|
|
11064
11082
|
`请确认 TCR、SCF 与构建管道处于同一地域;如需通过 URL 访问,请显式调用 ` +
|
|
11065
|
-
`manageGateway(action="
|
|
11083
|
+
`manageGateway(action="createRoute", upstreamResourceType="WEB_SCF") 并按需调整函数安全规则。` +
|
|
11066
11084
|
`部署后可用 queryFunctions(action="getFunctionDetail") 确认函数已就绪。`, [
|
|
11067
11085
|
{
|
|
11068
11086
|
tool: "queryFunctions",
|
|
@@ -11076,8 +11094,8 @@ function registerFunctionTools(server) {
|
|
|
11076
11094
|
},
|
|
11077
11095
|
{
|
|
11078
11096
|
tool: "manageGateway",
|
|
11079
|
-
action: "
|
|
11080
|
-
reason: "如需通过 URL 访问镜像 HTTP
|
|
11097
|
+
action: "createRoute",
|
|
11098
|
+
reason: "如需通过 URL 访问镜像 HTTP 函数,显式创建 Domain/Route 访问入口并传 type=\"HTTP\"(映射 WEB_SCF)",
|
|
11081
11099
|
},
|
|
11082
11100
|
]);
|
|
11083
11101
|
}
|
|
@@ -11132,12 +11150,12 @@ function registerFunctionTools(server) {
|
|
|
11132
11150
|
if (func.type === "HTTP") {
|
|
11133
11151
|
nextActions.push({
|
|
11134
11152
|
tool: "manageGateway",
|
|
11135
|
-
action: "
|
|
11136
|
-
reason: "如果需要通过 URL 访问 HTTP 函数,请调用 manageGateway(action=\"
|
|
11153
|
+
action: "createRoute",
|
|
11154
|
+
reason: "如果需要通过 URL 访问 HTTP 函数,请调用 manageGateway(action=\"createRoute\") 并显式传 upstreamResourceType=\"WEB_SCF\",再按实际路径和鉴权需求创建访问入口,不要默认假设 /函数名 已存在",
|
|
11137
11155
|
});
|
|
11138
11156
|
nextActions.push({
|
|
11139
11157
|
tool: "queryGateway",
|
|
11140
|
-
action: "
|
|
11158
|
+
action: "getRoute",
|
|
11141
11159
|
reason: "交付前确认 HTTP 访问路径是否已存在并已生效",
|
|
11142
11160
|
});
|
|
11143
11161
|
nextActions.push({
|
|
@@ -11152,7 +11170,7 @@ function registerFunctionTools(server) {
|
|
|
11152
11170
|
});
|
|
11153
11171
|
}
|
|
11154
11172
|
const message = func.type === "HTTP"
|
|
11155
|
-
? `已创建 HTTP 函数 ${functionName}。如果后续需要通过 URL 访问,请显式调用 manageGateway(action="
|
|
11173
|
+
? `已创建 HTTP 函数 ${functionName}。如果后续需要通过 URL 访问,请显式调用 manageGateway(action="createRoute"),并把 upstreamResourceType="WEB_SCF" 一起传入,再按实际路径和鉴权需求创建访问入口。评测或其他外部调用方可能会以匿名身份访问,而且失败后不一定会把 EXCEED_AUTHORITY 再反馈给 AI;交付前请主动确认访问路径和函数安全规则,若已出现 EXCEED_AUTHORITY,请先调用 queryPermissions(action="getResourcePermission", resourceType="function", resourceId="${functionName}") 查看当前规则,再按需要使用 managePermissions(action="updateResourcePermission") 调整权限。`
|
|
11156
11174
|
: `已创建函数 ${functionName}`;
|
|
11157
11175
|
return buildEnvelope({
|
|
11158
11176
|
action: input.action,
|
|
@@ -11640,8 +11658,11 @@ function registerFunctionTools(server) {
|
|
|
11640
11658
|
zipFile: zod_1.z.string().optional().describe("仅兼容特殊场景:预先准备好的代码包 base64 编码。普通 createFunction/updateFunctionCode 默认不要先压缩 zip,优先使用 functionRootPath。"),
|
|
11641
11659
|
handler: zod_1.z.string().optional().describe("函数入口"),
|
|
11642
11660
|
timeout: zod_1.z.number().optional().describe("配置更新时的超时时间"),
|
|
11643
|
-
envVariables: zod_1.z
|
|
11644
|
-
|
|
11661
|
+
envVariables: zod_1.z
|
|
11662
|
+
.record(zod_1.z.string())
|
|
11663
|
+
.optional()
|
|
11664
|
+
.describe("配置更新时要合并的环境变量。若含 DATABASE_URL / MYSQL_* / POSTGRES_* / REDIS_* 等 TCP 连库变量,必须同时提供真实 vpc(或函数已绑定完整 VPC)。禁止猜测 vpcId/subnetId。"),
|
|
11665
|
+
vpc: VPC_SCHEMA.optional().describe("配置更新时的 VPC 信息。非原生 TCP 连库场景必填真实 vpcId+subnetId;不要用占位符。"),
|
|
11645
11666
|
params: zod_1.z.record(zod_1.z.any()).optional().describe("invokeFunction 的调用参数"),
|
|
11646
11667
|
triggers: zod_1.z
|
|
11647
11668
|
.array(TRIGGER_SCHEMA)
|
|
@@ -14275,7 +14296,7 @@ function registerSetupTools(server) {
|
|
|
14275
14296
|
server.registerTool("downloadTemplate", {
|
|
14276
14297
|
title: "下载项目模板",
|
|
14277
14298
|
description: `自动下载并部署CloudBase项目模板。
|
|
14278
|
-
**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.
|
|
14299
|
+
**Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.25.1" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
14279
14300
|
inputSchema: {
|
|
14280
14301
|
template: zod_1.z
|
|
14281
14302
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|