@cloudbase/cloudbase-mcp 2.25.10 → 2.26.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 +5 -5
- package/dist/index.cjs +547 -79
- package/dist/index.js +547 -79
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3321,6 +3321,7 @@ const auth_js_1 = __webpack_require__(77291);
|
|
|
3321
3321
|
const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
3322
3322
|
const logger_js_1 = __webpack_require__(13039);
|
|
3323
3323
|
const tool_result_js_1 = __webpack_require__(9835);
|
|
3324
|
+
const gateway_access_urls_js_1 = __webpack_require__(22435);
|
|
3324
3325
|
const env_setup_js_1 = __webpack_require__(6978);
|
|
3325
3326
|
/**
|
|
3326
3327
|
* Resources accepted by CreateEnv. Matches Cloud API / Manager SDK contract.
|
|
@@ -4073,6 +4074,89 @@ async function enrichEnvInfoWithMissingFields(manager, result, envId) {
|
|
|
4073
4074
|
return result;
|
|
4074
4075
|
}
|
|
4075
4076
|
}
|
|
4077
|
+
/**
|
|
4078
|
+
* Project gateway Route.Enable onto envQuery(info) without mutating StaticDomain.
|
|
4079
|
+
*
|
|
4080
|
+
* StaticStorages[].StaticDomain stays the cloud-API nominal hostname.
|
|
4081
|
+
* Sibling field staticDomainRouteEnabled (and EnvInfo-level mirrors for the
|
|
4082
|
+
* primary store) tell callers whether the default hosting domain route is
|
|
4083
|
+
* actually reachable (Enable !== false). Matches queryHosting websiteConfig.
|
|
4084
|
+
*/
|
|
4085
|
+
async function enrichEnvInfoWithStaticDomainRouteEnabled(manager, result, envId) {
|
|
4086
|
+
const envInfo = result?.EnvInfo;
|
|
4087
|
+
if (!envInfo || typeof envInfo !== "object") {
|
|
4088
|
+
return result;
|
|
4089
|
+
}
|
|
4090
|
+
const staticStorages = Array.isArray(envInfo.StaticStorages)
|
|
4091
|
+
? envInfo.StaticStorages
|
|
4092
|
+
: null;
|
|
4093
|
+
if (!staticStorages || staticStorages.length === 0) {
|
|
4094
|
+
return result;
|
|
4095
|
+
}
|
|
4096
|
+
if (typeof manager?.env?.describeHttpServiceRoute !== "function") {
|
|
4097
|
+
return result;
|
|
4098
|
+
}
|
|
4099
|
+
const resolvedEnvId = (typeof envId === "string" && envId.trim()) ||
|
|
4100
|
+
(typeof envInfo.EnvId === "string" && envInfo.EnvId.trim()) ||
|
|
4101
|
+
undefined;
|
|
4102
|
+
if (!resolvedEnvId) {
|
|
4103
|
+
return result;
|
|
4104
|
+
}
|
|
4105
|
+
try {
|
|
4106
|
+
const routeResult = await manager.env.describeHttpServiceRoute({
|
|
4107
|
+
EnvId: resolvedEnvId,
|
|
4108
|
+
Limit: 1000,
|
|
4109
|
+
});
|
|
4110
|
+
const routes = (0, gateway_access_urls_js_1.flattenHttpServiceRoutes)(routeResult);
|
|
4111
|
+
let primaryRouteEnabled = null;
|
|
4112
|
+
const enrichedStorages = staticStorages.map((store, index) => {
|
|
4113
|
+
if (!store || typeof store !== "object" || Array.isArray(store)) {
|
|
4114
|
+
return store;
|
|
4115
|
+
}
|
|
4116
|
+
const record = store;
|
|
4117
|
+
const domain = typeof record.StaticDomain === "string" ? record.StaticDomain : "";
|
|
4118
|
+
if (!domain) {
|
|
4119
|
+
return store;
|
|
4120
|
+
}
|
|
4121
|
+
const reachable = (0, gateway_access_urls_js_1.isDomainPathReachableViaGateway)(routes, domain, "/");
|
|
4122
|
+
if (index === 0) {
|
|
4123
|
+
primaryRouteEnabled = reachable;
|
|
4124
|
+
}
|
|
4125
|
+
return {
|
|
4126
|
+
...record,
|
|
4127
|
+
staticDomainRouteEnabled: reachable,
|
|
4128
|
+
};
|
|
4129
|
+
});
|
|
4130
|
+
const enrichment = {
|
|
4131
|
+
StaticStorages: enrichedStorages,
|
|
4132
|
+
};
|
|
4133
|
+
if (primaryRouteEnabled !== null) {
|
|
4134
|
+
enrichment.staticDomainRouteEnabled = primaryRouteEnabled;
|
|
4135
|
+
enrichment.accessUrlReachable = primaryRouteEnabled !== false;
|
|
4136
|
+
if (primaryRouteEnabled === false) {
|
|
4137
|
+
const primaryDomain = typeof enrichedStorages[0]
|
|
4138
|
+
?.StaticDomain === "string"
|
|
4139
|
+
? String(enrichedStorages[0].StaticDomain)
|
|
4140
|
+
: "";
|
|
4141
|
+
enrichment.routeDisabled = true;
|
|
4142
|
+
if (primaryDomain) {
|
|
4143
|
+
enrichment.disabledAccessUrls = [`https://${primaryDomain}/`];
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
}
|
|
4147
|
+
return {
|
|
4148
|
+
...result,
|
|
4149
|
+
EnvInfo: {
|
|
4150
|
+
...envInfo,
|
|
4151
|
+
...enrichment,
|
|
4152
|
+
},
|
|
4153
|
+
};
|
|
4154
|
+
}
|
|
4155
|
+
catch {
|
|
4156
|
+
// Gateway route lookup is best-effort enrichment only.
|
|
4157
|
+
return result;
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4076
4160
|
function normalizeOptionalToolString(value) {
|
|
4077
4161
|
return typeof value === "string" && value.trim().length > 0
|
|
4078
4162
|
? value.trim()
|
|
@@ -4554,8 +4638,9 @@ function registerEnvTools(server) {
|
|
|
4554
4638
|
if (action === "status") {
|
|
4555
4639
|
const loginState = await (0, auth_js_1.peekLoginState)();
|
|
4556
4640
|
const authFlowState = await (0, auth_js_1.getAuthProgressState)();
|
|
4557
|
-
//
|
|
4558
|
-
const
|
|
4641
|
+
// Detect API Key mode (CLOUDBASE_API_KEY preferred, CLOUDBASE_APIKEY fallback)
|
|
4642
|
+
const apiKeyFromEnv = (0, auth_js_1.getCloudBaseApiKeyFromEnv)();
|
|
4643
|
+
const isApiKeyMode = !!(apiKeyFromEnv && process.env.CLOUDBASE_ENV_ID);
|
|
4559
4644
|
const authStatus = loginState
|
|
4560
4645
|
? "READY"
|
|
4561
4646
|
: authFlowState.status === "PENDING"
|
|
@@ -4603,8 +4688,9 @@ function registerEnvTools(server) {
|
|
|
4603
4688
|
});
|
|
4604
4689
|
}
|
|
4605
4690
|
if (action === "start_auth") {
|
|
4606
|
-
// API Key
|
|
4607
|
-
|
|
4691
|
+
// API Key mode: exchange for temporary credentials
|
|
4692
|
+
const apiKeyFromEnv = (0, auth_js_1.getCloudBaseApiKeyFromEnv)();
|
|
4693
|
+
if (apiKeyFromEnv && process.env.CLOUDBASE_ENV_ID) {
|
|
4608
4694
|
try {
|
|
4609
4695
|
const existingLoginState = await (0, auth_js_1.peekLoginState)();
|
|
4610
4696
|
if (existingLoginState) {
|
|
@@ -4618,22 +4704,21 @@ function registerEnvTools(server) {
|
|
|
4618
4704
|
}
|
|
4619
4705
|
}
|
|
4620
4706
|
catch (e) {
|
|
4621
|
-
// peekLoginState
|
|
4707
|
+
// peekLoginState threw; log and fall through to diagnostic
|
|
4622
4708
|
(0, logger_js_1.debug)("start_auth: peekLoginState threw", { error: e instanceof Error ? e.message : String(e) });
|
|
4623
4709
|
}
|
|
4624
|
-
// API Key
|
|
4625
|
-
// 尝试获取更详细的错误信息
|
|
4710
|
+
// API Key exchange failed: return diagnostic details
|
|
4626
4711
|
let diagMessage = "当前配置了 API Key 认证模式,但换取临时密钥失败。";
|
|
4627
4712
|
const endpoint = process.env.CLOUDBASE_API_ENDPOINT || `https://${process.env.CLOUDBASE_ENV_ID}.ap-shanghai.tcb-api.tencentcloudapi.com`;
|
|
4628
4713
|
diagMessage += `\n\n诊断信息:`;
|
|
4629
4714
|
diagMessage += `\n- CLOUDBASE_ENV_ID: ${process.env.CLOUDBASE_ENV_ID}`;
|
|
4630
|
-
diagMessage += `\n- CLOUDBASE_API_KEY: ${
|
|
4715
|
+
diagMessage += `\n- CLOUDBASE_API_KEY: ${apiKeyFromEnv.slice(0, 20)}...(已截断)`;
|
|
4631
4716
|
diagMessage += `\n- Endpoint: ${endpoint}`;
|
|
4632
4717
|
diagMessage += `\n\n可能原因:`;
|
|
4633
4718
|
diagMessage += `\n1. API Key 已过期或被删除`;
|
|
4634
4719
|
diagMessage += `\n2. Endpoint 不可达(网络/DNS 问题)`;
|
|
4635
4720
|
diagMessage += `\n3. CLOUDBASE_ENV_ID 与 API Key 所属环境不匹配`;
|
|
4636
|
-
diagMessage += `\n\n建议:检查 MCP 配置中的 CLOUDBASE_API_KEY
|
|
4721
|
+
diagMessage += `\n\n建议:检查 MCP 配置中的 CLOUDBASE_API_KEY(或兼容的 CLOUDBASE_APIKEY)和 CLOUDBASE_ENV_ID 环境变量是否正确。`;
|
|
4637
4722
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
4638
4723
|
ok: false,
|
|
4639
4724
|
code: "API_KEY_AUTH_FAILED",
|
|
@@ -4919,12 +5004,12 @@ function registerEnvTools(server) {
|
|
|
4919
5004
|
});
|
|
4920
5005
|
}
|
|
4921
5006
|
if (action === "logout") {
|
|
4922
|
-
// API Key
|
|
4923
|
-
if (
|
|
5007
|
+
// Disallow logout in API Key mode
|
|
5008
|
+
if ((0, auth_js_1.getCloudBaseApiKeyFromEnv)() && process.env.CLOUDBASE_ENV_ID) {
|
|
4924
5009
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
4925
5010
|
ok: false,
|
|
4926
5011
|
code: "LOGOUT_NOT_ALLOWED",
|
|
4927
|
-
message: "当前使用 API Key 认证模式,不支持退出登录。如需切换认证方式,请移除 CLOUDBASE_API_KEY
|
|
5012
|
+
message: "当前使用 API Key 认证模式,不支持退出登录。如需切换认证方式,请移除 CLOUDBASE_API_KEY(或兼容的 CLOUDBASE_APIKEY)环境变量后重启。",
|
|
4928
5013
|
auth_mode: "api_key",
|
|
4929
5014
|
});
|
|
4930
5015
|
}
|
|
@@ -5146,6 +5231,8 @@ function registerEnvTools(server) {
|
|
|
5146
5231
|
if (envId) {
|
|
5147
5232
|
result = await enrichEnvInfoWithMissingFields(cloudbaseInfo, result, envId);
|
|
5148
5233
|
}
|
|
5234
|
+
// Project gateway Route.Enable next to StaticDomain (do not rewrite StaticDomain).
|
|
5235
|
+
result = await enrichEnvInfoWithStaticDomainRouteEnabled(cloudbaseInfo, result, envId);
|
|
5149
5236
|
result = await enrichEnvInfoWithRuntimeMode(result, cloudbaseInfo);
|
|
5150
5237
|
break;
|
|
5151
5238
|
case "domains":
|
|
@@ -5214,7 +5301,7 @@ function registerEnvTools(server) {
|
|
|
5214
5301
|
// Register primary tool name (queryEnv)
|
|
5215
5302
|
const queryEnvToolSchema = {
|
|
5216
5303
|
title: "CloudBase 环境查询",
|
|
5217
|
-
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 都不该使用。",
|
|
5304
|
+
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\n🌐 action=info 还会在不改写 `StaticStorages[].StaticDomain`(云 API 名义域名)的前提下,投影网关路由 Enable 状态:`StaticStorages[].staticDomainRouteEnabled` 与 `EnvInfo.staticDomainRouteEnabled`(与 queryHosting websiteConfig 同源)。`false` 表示默认静态域名根路由已禁用(访问会返回 GATEWAY_ROUTE_DISABLED),勿把名义域名当成可达 URL。\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 都不该使用。",
|
|
5218
5305
|
inputSchema: {
|
|
5219
5306
|
action: zod_1.z
|
|
5220
5307
|
.enum(["list", "info", "domains"])
|
|
@@ -84798,12 +84885,19 @@ exports.colors = {
|
|
|
84798
84885
|
"use strict";
|
|
84799
84886
|
|
|
84800
84887
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
84888
|
+
exports.isGatewayRouteEnabled = isGatewayRouteEnabled;
|
|
84889
|
+
exports.isDomainPathReachableViaGateway = isDomainPathReachableViaGateway;
|
|
84801
84890
|
exports.rankGatewayAccessUrls = rankGatewayAccessUrls;
|
|
84802
84891
|
exports.flattenHttpServiceRoutes = flattenHttpServiceRoutes;
|
|
84803
84892
|
exports.filterRoutesByUpstream = filterRoutesByUpstream;
|
|
84804
84893
|
exports.toAccessUrlEnvelope = toAccessUrlEnvelope;
|
|
84805
84894
|
exports.resolveGatewayAccessUrls = resolveGatewayAccessUrls;
|
|
84895
|
+
exports.resolveAllGatewayRoutes = resolveAllGatewayRoutes;
|
|
84806
84896
|
exports.preferGatewayOrFallback = preferGatewayOrFallback;
|
|
84897
|
+
/** Route is reachable unless Enable is explicitly false. */
|
|
84898
|
+
function isGatewayRouteEnabled(route) {
|
|
84899
|
+
return route.Enable !== false;
|
|
84900
|
+
}
|
|
84807
84901
|
function normalizeAccessPath(path) {
|
|
84808
84902
|
if (!path) {
|
|
84809
84903
|
return "/";
|
|
@@ -84813,13 +84907,63 @@ function normalizeAccessPath(path) {
|
|
|
84813
84907
|
function buildHttpsUrl(domain, path) {
|
|
84814
84908
|
return `https://${domain}${normalizeAccessPath(path)}`;
|
|
84815
84909
|
}
|
|
84910
|
+
function extractHostname(urlOrHost) {
|
|
84911
|
+
const trimmed = urlOrHost.trim();
|
|
84912
|
+
if (!trimmed) {
|
|
84913
|
+
return "";
|
|
84914
|
+
}
|
|
84915
|
+
try {
|
|
84916
|
+
if (trimmed.includes("://")) {
|
|
84917
|
+
return new URL(trimmed).hostname.toLowerCase();
|
|
84918
|
+
}
|
|
84919
|
+
}
|
|
84920
|
+
catch {
|
|
84921
|
+
// Fall through to treat as bare hostname.
|
|
84922
|
+
}
|
|
84923
|
+
return trimmed.replace(/\/.*$/, "").toLowerCase();
|
|
84924
|
+
}
|
|
84925
|
+
/**
|
|
84926
|
+
* Whether a hostname has an enabled covering gateway route for the given path.
|
|
84927
|
+
* Longest matching Path prefix wins. Missing route info → unknown (null).
|
|
84928
|
+
*/
|
|
84929
|
+
function isDomainPathReachableViaGateway(routes, domainOrUrl, requestPath) {
|
|
84930
|
+
const hostname = extractHostname(domainOrUrl);
|
|
84931
|
+
if (!hostname) {
|
|
84932
|
+
return null;
|
|
84933
|
+
}
|
|
84934
|
+
const normalizedRequest = normalizeAccessPath(requestPath);
|
|
84935
|
+
const domainRoutes = routes.filter((route) => Boolean(route.Domain) &&
|
|
84936
|
+
route.Domain.toLowerCase() === hostname);
|
|
84937
|
+
if (domainRoutes.length === 0) {
|
|
84938
|
+
return null;
|
|
84939
|
+
}
|
|
84940
|
+
const covering = domainRoutes
|
|
84941
|
+
.map((route) => ({
|
|
84942
|
+
route,
|
|
84943
|
+
path: normalizeAccessPath(route.Path),
|
|
84944
|
+
}))
|
|
84945
|
+
.filter(({ path }) => {
|
|
84946
|
+
if (path === "/") {
|
|
84947
|
+
return true;
|
|
84948
|
+
}
|
|
84949
|
+
return (normalizedRequest === path ||
|
|
84950
|
+
normalizedRequest.startsWith(path.endsWith("/") ? path : `${path}/`));
|
|
84951
|
+
})
|
|
84952
|
+
.sort((a, b) => b.path.length - a.path.length);
|
|
84953
|
+
if (covering.length === 0) {
|
|
84954
|
+
return null;
|
|
84955
|
+
}
|
|
84956
|
+
return isGatewayRouteEnabled(covering[0].route);
|
|
84957
|
+
}
|
|
84816
84958
|
/**
|
|
84817
84959
|
* Rank gateway route URLs: custom domains (IsDefault !== true) before default domain.
|
|
84818
|
-
* Stable within each group; dedupe by full URL.
|
|
84960
|
+
* By default skips Enable === false routes. Stable within each group; dedupe by full URL.
|
|
84819
84961
|
*/
|
|
84820
|
-
function rankGatewayAccessUrls(routes) {
|
|
84962
|
+
function rankGatewayAccessUrls(routes, options) {
|
|
84963
|
+
const includeDisabled = options?.includeDisabled === true;
|
|
84821
84964
|
const mapped = routes
|
|
84822
84965
|
.filter((route) => Boolean(route.Domain))
|
|
84966
|
+
.filter((route) => includeDisabled || isGatewayRouteEnabled(route))
|
|
84823
84967
|
.map((route) => {
|
|
84824
84968
|
const path = normalizeAccessPath(route.Path);
|
|
84825
84969
|
const isDefault = route.IsDefault === true;
|
|
@@ -84828,6 +84972,7 @@ function rankGatewayAccessUrls(routes) {
|
|
|
84828
84972
|
domain: route.Domain,
|
|
84829
84973
|
path,
|
|
84830
84974
|
isDefault,
|
|
84975
|
+
enabled: isGatewayRouteEnabled(route),
|
|
84831
84976
|
source: (isDefault
|
|
84832
84977
|
? "gateway.default"
|
|
84833
84978
|
: "gateway.custom"),
|
|
@@ -84855,6 +85000,7 @@ function flattenHttpServiceRoutes(result) {
|
|
|
84855
85000
|
Domain: domainItem.Domain ?? "",
|
|
84856
85001
|
IsDefault: domainItem.IsDefault,
|
|
84857
85002
|
Path: typeof route.Path === "string" ? route.Path : undefined,
|
|
85003
|
+
Enable: typeof route.Enable === "boolean" ? route.Enable : undefined,
|
|
84858
85004
|
UpstreamResourceType: typeof route.UpstreamResourceType === "string"
|
|
84859
85005
|
? route.UpstreamResourceType
|
|
84860
85006
|
: undefined,
|
|
@@ -84877,64 +85023,116 @@ function filterRoutesByUpstream(routes, input) {
|
|
|
84877
85023
|
types.includes(String(route.UpstreamResourceType)));
|
|
84878
85024
|
});
|
|
84879
85025
|
}
|
|
84880
|
-
function toAccessUrlEnvelope(ranked) {
|
|
84881
|
-
|
|
84882
|
-
|
|
85026
|
+
function toAccessUrlEnvelope(ranked, disabledAccessUrls = []) {
|
|
85027
|
+
const enabled = ranked.filter((item) => item.enabled);
|
|
85028
|
+
if (enabled.length === 0) {
|
|
85029
|
+
return {
|
|
85030
|
+
accessUrls: [],
|
|
85031
|
+
routes: [],
|
|
85032
|
+
disabledAccessUrls,
|
|
85033
|
+
};
|
|
84883
85034
|
}
|
|
84884
85035
|
return {
|
|
84885
|
-
accessUrl:
|
|
84886
|
-
accessUrls:
|
|
84887
|
-
accessUrlSource:
|
|
84888
|
-
routes:
|
|
85036
|
+
accessUrl: enabled[0].url,
|
|
85037
|
+
accessUrls: enabled.map((item) => item.url),
|
|
85038
|
+
accessUrlSource: enabled[0].source,
|
|
85039
|
+
routes: enabled,
|
|
85040
|
+
disabledAccessUrls,
|
|
84889
85041
|
};
|
|
84890
85042
|
}
|
|
85043
|
+
async function loadHttpServiceRoutes(getManager, envId) {
|
|
85044
|
+
const manager = await getManager();
|
|
85045
|
+
const result = await manager.env.describeHttpServiceRoute({
|
|
85046
|
+
EnvId: envId,
|
|
85047
|
+
Limit: 1000,
|
|
85048
|
+
});
|
|
85049
|
+
return flattenHttpServiceRoutes(result);
|
|
85050
|
+
}
|
|
84891
85051
|
/**
|
|
84892
85052
|
* Best-effort lookup of gateway access URLs for an upstream resource.
|
|
84893
85053
|
* Never throws — returns empty lists on failure.
|
|
85054
|
+
* Disabled routes (Enable === false) are excluded from accessUrl(s).
|
|
84894
85055
|
*/
|
|
84895
85056
|
async function resolveGatewayAccessUrls(input) {
|
|
84896
85057
|
try {
|
|
84897
|
-
const
|
|
84898
|
-
const result = await manager.env.describeHttpServiceRoute({
|
|
84899
|
-
EnvId: input.envId,
|
|
84900
|
-
Limit: 1000,
|
|
84901
|
-
});
|
|
84902
|
-
const flattened = flattenHttpServiceRoutes(result);
|
|
85058
|
+
const flattened = await loadHttpServiceRoutes(input.getManager, input.envId);
|
|
84903
85059
|
const matched = filterRoutesByUpstream(flattened, {
|
|
84904
85060
|
upstreamResourceName: input.upstreamResourceName,
|
|
84905
85061
|
upstreamResourceTypes: input.upstreamResourceTypes,
|
|
84906
85062
|
});
|
|
84907
|
-
|
|
85063
|
+
const disabledAccessUrls = rankGatewayAccessUrls(matched, {
|
|
85064
|
+
includeDisabled: true,
|
|
85065
|
+
})
|
|
85066
|
+
.filter((item) => !item.enabled)
|
|
85067
|
+
.map((item) => item.url);
|
|
85068
|
+
return toAccessUrlEnvelope(rankGatewayAccessUrls(matched), disabledAccessUrls);
|
|
85069
|
+
}
|
|
85070
|
+
catch {
|
|
85071
|
+
return { accessUrls: [], routes: [], disabledAccessUrls: [] };
|
|
85072
|
+
}
|
|
85073
|
+
}
|
|
85074
|
+
/**
|
|
85075
|
+
* Load all flattened HTTP service routes (best-effort). Used by hosting to
|
|
85076
|
+
* judge whether the static-domain fallback itself is gateway-disabled.
|
|
85077
|
+
*/
|
|
85078
|
+
async function resolveAllGatewayRoutes(input) {
|
|
85079
|
+
try {
|
|
85080
|
+
return await loadHttpServiceRoutes(input.getManager, input.envId);
|
|
84908
85081
|
}
|
|
84909
85082
|
catch {
|
|
84910
|
-
return
|
|
85083
|
+
return [];
|
|
84911
85084
|
}
|
|
84912
85085
|
}
|
|
84913
85086
|
/**
|
|
84914
85087
|
* Merge ranked gateway URLs with a resource-native fallback URL.
|
|
84915
85088
|
* Gateway custom/default always win over the fallback when present.
|
|
85089
|
+
* Unreachable fallbacks are omitted from accessUrl(s) and listed under
|
|
85090
|
+
* disabledAccessUrls instead.
|
|
84916
85091
|
*/
|
|
84917
85092
|
function preferGatewayOrFallback(input) {
|
|
85093
|
+
const disabledAccessUrls = [
|
|
85094
|
+
...(input.gateway.disabledAccessUrls ?? []),
|
|
85095
|
+
];
|
|
85096
|
+
const fallbackReachable = input.fallbackReachable !== false;
|
|
84918
85097
|
if (input.gateway.accessUrl) {
|
|
84919
85098
|
const urls = [...input.gateway.accessUrls];
|
|
84920
85099
|
if (input.fallbackUrl &&
|
|
85100
|
+
fallbackReachable &&
|
|
84921
85101
|
!urls.includes(input.fallbackUrl)) {
|
|
84922
85102
|
urls.push(input.fallbackUrl);
|
|
84923
85103
|
}
|
|
85104
|
+
else if (input.fallbackUrl &&
|
|
85105
|
+
!fallbackReachable &&
|
|
85106
|
+
!disabledAccessUrls.includes(input.fallbackUrl)) {
|
|
85107
|
+
disabledAccessUrls.push(input.fallbackUrl);
|
|
85108
|
+
}
|
|
84924
85109
|
return {
|
|
84925
85110
|
accessUrl: input.gateway.accessUrl,
|
|
84926
85111
|
accessUrls: urls,
|
|
84927
85112
|
accessUrlSource: input.gateway.accessUrlSource,
|
|
85113
|
+
accessUrlReachable: true,
|
|
85114
|
+
disabledAccessUrls,
|
|
84928
85115
|
};
|
|
84929
85116
|
}
|
|
84930
|
-
if (input.fallbackUrl) {
|
|
85117
|
+
if (input.fallbackUrl && fallbackReachable) {
|
|
84931
85118
|
return {
|
|
84932
85119
|
accessUrl: input.fallbackUrl,
|
|
84933
85120
|
accessUrls: [input.fallbackUrl],
|
|
84934
85121
|
accessUrlSource: input.fallbackSource,
|
|
85122
|
+
accessUrlReachable: true,
|
|
85123
|
+
disabledAccessUrls,
|
|
84935
85124
|
};
|
|
84936
85125
|
}
|
|
84937
|
-
|
|
85126
|
+
if (input.fallbackUrl && !fallbackReachable) {
|
|
85127
|
+
if (!disabledAccessUrls.includes(input.fallbackUrl)) {
|
|
85128
|
+
disabledAccessUrls.push(input.fallbackUrl);
|
|
85129
|
+
}
|
|
85130
|
+
}
|
|
85131
|
+
return {
|
|
85132
|
+
accessUrls: [],
|
|
85133
|
+
accessUrlReachable: false,
|
|
85134
|
+
disabledAccessUrls,
|
|
85135
|
+
};
|
|
84938
85136
|
}
|
|
84939
85137
|
|
|
84940
85138
|
|
|
@@ -123609,16 +123807,19 @@ function isDirectoryUploadTarget(localPath, cloudPath) {
|
|
|
123609
123807
|
return true;
|
|
123610
123808
|
return path_1.default.posix.extname(normalizedCloudPath) === '';
|
|
123611
123809
|
}
|
|
123612
|
-
function
|
|
123613
|
-
if (!staticDomain)
|
|
123614
|
-
return '';
|
|
123810
|
+
function hostingAccessPathname(cloudPath, localPath) {
|
|
123615
123811
|
const normalizedCloudPath = (cloudPath ?? '').trim().replace(/^\/+|\/+$/g, '');
|
|
123616
123812
|
const isDirectory = isDirectoryUploadTarget(localPath, cloudPath);
|
|
123617
123813
|
if (!normalizedCloudPath) {
|
|
123618
|
-
return
|
|
123814
|
+
return '/';
|
|
123619
123815
|
}
|
|
123620
123816
|
const pathname = isDirectory ? `${normalizedCloudPath}/` : normalizedCloudPath;
|
|
123621
|
-
return
|
|
123817
|
+
return `/${pathname}`;
|
|
123818
|
+
}
|
|
123819
|
+
function buildHostingAccessUrl(staticDomain, cloudPath, localPath) {
|
|
123820
|
+
if (!staticDomain)
|
|
123821
|
+
return '';
|
|
123822
|
+
return `https://${staticDomain}${hostingAccessPathname(cloudPath, localPath)}`;
|
|
123622
123823
|
}
|
|
123623
123824
|
function buildUploadErrorMessage(error, localPath) {
|
|
123624
123825
|
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -123799,7 +124000,7 @@ function buildDomainMutationResult(params) {
|
|
|
123799
124000
|
message: `静态托管域名${actionLabel}请求已提交。域名配置、证书校验和边缘侧传播通常需要 30 秒到 10 分钟,请继续调用 queryHosting(action="domainStatus") 确认最终结果。`,
|
|
123800
124001
|
};
|
|
123801
124002
|
}
|
|
123802
|
-
async function getHostingWebsiteConfig(cloudbase, logger) {
|
|
124003
|
+
async function getHostingWebsiteConfig(cloudbase, logger, cloudBaseOptions) {
|
|
123803
124004
|
const websiteConfig = await cloudbase.hosting.getWebsiteConfig();
|
|
123804
124005
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(logger, websiteConfig);
|
|
123805
124006
|
const hostingResult = {
|
|
@@ -123816,6 +124017,27 @@ async function getHostingWebsiteConfig(cloudbase, logger) {
|
|
|
123816
124017
|
catch {
|
|
123817
124018
|
// Ignore enrichment failures and return the website config as-is.
|
|
123818
124019
|
}
|
|
124020
|
+
try {
|
|
124021
|
+
const cdnDomain = typeof hostingResult.CdnDomain === 'string' ? hostingResult.CdnDomain : '';
|
|
124022
|
+
if (cdnDomain && typeof cloudbase.env?.describeHttpServiceRoute === 'function') {
|
|
124023
|
+
const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
124024
|
+
const routeResult = await cloudbase.env.describeHttpServiceRoute({
|
|
124025
|
+
EnvId: envId,
|
|
124026
|
+
Limit: 1000,
|
|
124027
|
+
});
|
|
124028
|
+
const routes = (0, gateway_access_urls_js_1.flattenHttpServiceRoutes)(routeResult);
|
|
124029
|
+
const reachable = (0, gateway_access_urls_js_1.isDomainPathReachableViaGateway)(routes, cdnDomain, '/');
|
|
124030
|
+
hostingResult.staticDomainRouteEnabled = reachable;
|
|
124031
|
+
hostingResult.accessUrlReachable = reachable !== false;
|
|
124032
|
+
if (reachable === false) {
|
|
124033
|
+
hostingResult.routeDisabled = true;
|
|
124034
|
+
hostingResult.disabledAccessUrls = [`https://${cdnDomain}/`];
|
|
124035
|
+
}
|
|
124036
|
+
}
|
|
124037
|
+
}
|
|
124038
|
+
catch {
|
|
124039
|
+
// Gateway route lookup is best-effort enrichment only.
|
|
124040
|
+
}
|
|
123819
124041
|
return hostingResult;
|
|
123820
124042
|
}
|
|
123821
124043
|
async function resolveHostingStaticDomain(cloudbase, logger) {
|
|
@@ -123899,7 +124121,7 @@ function registerHostingTools(server) {
|
|
|
123899
124121
|
const cloudbase = await getManager();
|
|
123900
124122
|
switch (input.action) {
|
|
123901
124123
|
case 'websiteConfig': {
|
|
123902
|
-
const websiteConfig = await getHostingWebsiteConfig(cloudbase, server.logger);
|
|
124124
|
+
const websiteConfig = await getHostingWebsiteConfig(cloudbase, server.logger, cloudBaseOptions);
|
|
123903
124125
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
123904
124126
|
success: true,
|
|
123905
124127
|
data: {
|
|
@@ -124025,7 +124247,7 @@ function registerHostingTools(server) {
|
|
|
124025
124247
|
});
|
|
124026
124248
|
server.registerTool('manageHosting', {
|
|
124027
124249
|
title: '管理 CloudBase 静态托管',
|
|
124028
|
-
description: '管理 CloudBase 静态托管的变更操作。action=upload 上传本地构建产物到共享域名(域名格式:<envId>-<appId>.tcloudbaseapp.com/<cloudPath>);action=delete 删除托管文件或目录(必须 confirm=true);action=setWebsiteDocument 设置首页/错误页/路由规则;action=enableService 开通静态托管;action=bindDomain / unbindDomain / updateDomain 管理自定义域名;action=downloadFile / downloadDirectory 下载托管内容到本地。⚠️ 新项目部署优先使用 manageApps(部署到独立子域名),本工具适合已有老项目继续使用或作为 manageApps 的 fallback。manageApps 与 manageHosting 域名不同,切换会导致老链接失效。若任务只是查看配置、文件或域名状态,请改用 queryHosting。',
|
|
124250
|
+
description: '管理 CloudBase 静态托管的变更操作。action=upload 上传本地构建产物到共享域名(域名格式:<envId>-<appId>.tcloudbaseapp.com/<cloudPath>);action=delete 删除托管文件或目录(必须 confirm=true);action=setWebsiteDocument 设置首页/错误页/路由规则;action=enableService 开通静态托管;action=bindDomain / unbindDomain / updateDomain 管理自定义域名;action=downloadFile / downloadDirectory 下载托管内容到本地。⚠️ 本工具没有关闭默认域名(*.tcloudbaseapp.com)的 action;要禁用该默认公网域名,请用 manageGateway(action="disableRoute", domain=该 STATIC_STORE IsDefault 域名, path="/")(底层 ModifyHTTPServiceRoute,不是 ModifyGatewayRoute)。⚠️ 新项目部署优先使用 manageApps(部署到独立子域名),本工具适合已有老项目继续使用或作为 manageApps 的 fallback。manageApps 与 manageHosting 域名不同,切换会导致老链接失效。若任务只是查看配置、文件或域名状态,请改用 queryHosting。',
|
|
124029
124251
|
inputSchema: manageHostingInputSchema,
|
|
124030
124252
|
annotations: {
|
|
124031
124253
|
readOnlyHint: false,
|
|
@@ -124065,7 +124287,24 @@ function registerHostingTools(server) {
|
|
|
124065
124287
|
const cdnFromStore = (store.CdnDomain ?? store.StaticDomain);
|
|
124066
124288
|
const staticDomain = cdnFromStore || await resolveHostingStaticDomain(cloudbase, server.logger);
|
|
124067
124289
|
const fallbackAccessUrl = buildHostingAccessUrl(staticDomain, input.cloudPath, input.localPath);
|
|
124290
|
+
const accessPathname = hostingAccessPathname(input.cloudPath, input.localPath);
|
|
124068
124291
|
const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
124292
|
+
const getGatewayManager = async () => {
|
|
124293
|
+
const manager = await getManager();
|
|
124294
|
+
if (!manager) {
|
|
124295
|
+
throw new Error("cloudbase manager unavailable");
|
|
124296
|
+
}
|
|
124297
|
+
return manager;
|
|
124298
|
+
};
|
|
124299
|
+
const allGatewayRoutes = await (0, gateway_access_urls_js_1.resolveAllGatewayRoutes)({
|
|
124300
|
+
envId,
|
|
124301
|
+
getManager: getGatewayManager,
|
|
124302
|
+
});
|
|
124303
|
+
const staticDomainRouteEnabled = staticDomain
|
|
124304
|
+
? (0, gateway_access_urls_js_1.isDomainPathReachableViaGateway)(allGatewayRoutes, staticDomain, accessPathname)
|
|
124305
|
+
: null;
|
|
124306
|
+
// null = no matching gateway route info → keep legacy fallback.
|
|
124307
|
+
const fallbackReachable = staticDomainRouteEnabled !== false;
|
|
124069
124308
|
const gatewayCandidates = Array.from(new Set([
|
|
124070
124309
|
store.StaticStoreName,
|
|
124071
124310
|
store.Name,
|
|
@@ -124075,34 +124314,38 @@ function registerHostingTools(server) {
|
|
|
124075
124314
|
]
|
|
124076
124315
|
.map((item) => (typeof item === "string" ? item.trim() : ""))
|
|
124077
124316
|
.filter(Boolean)));
|
|
124078
|
-
let accessUrl = fallbackAccessUrl;
|
|
124317
|
+
let accessUrl = fallbackReachable ? fallbackAccessUrl : "";
|
|
124079
124318
|
let accessUrls = accessUrl ? [accessUrl] : [];
|
|
124080
124319
|
let accessUrlSource = accessUrl
|
|
124081
124320
|
? "hosting.staticDomain"
|
|
124082
124321
|
: undefined;
|
|
124322
|
+
let accessUrlReachable = Boolean(accessUrl);
|
|
124323
|
+
let disabledAccessUrls = [];
|
|
124324
|
+
if (fallbackAccessUrl && !fallbackReachable) {
|
|
124325
|
+
disabledAccessUrls.push(fallbackAccessUrl);
|
|
124326
|
+
}
|
|
124083
124327
|
for (const upstreamName of gatewayCandidates) {
|
|
124084
124328
|
const gateway = await (0, gateway_access_urls_js_1.resolveGatewayAccessUrls)({
|
|
124085
124329
|
envId,
|
|
124086
124330
|
upstreamResourceName: upstreamName,
|
|
124087
124331
|
upstreamResourceTypes: ["STATIC_STORE"],
|
|
124088
|
-
getManager:
|
|
124089
|
-
const manager = await getManager();
|
|
124090
|
-
if (!manager) {
|
|
124091
|
-
throw new Error("cloudbase manager unavailable");
|
|
124092
|
-
}
|
|
124093
|
-
return manager;
|
|
124094
|
-
},
|
|
124332
|
+
getManager: getGatewayManager,
|
|
124095
124333
|
});
|
|
124096
124334
|
const preferred = (0, gateway_access_urls_js_1.preferGatewayOrFallback)({
|
|
124097
124335
|
gateway,
|
|
124098
124336
|
fallbackUrl: fallbackAccessUrl || undefined,
|
|
124099
124337
|
fallbackSource: "hosting.staticDomain",
|
|
124338
|
+
fallbackReachable,
|
|
124100
124339
|
});
|
|
124101
|
-
if (preferred.accessUrl) {
|
|
124102
|
-
accessUrl = preferred.accessUrl;
|
|
124340
|
+
if (preferred.accessUrl || preferred.disabledAccessUrls.length > 0) {
|
|
124341
|
+
accessUrl = preferred.accessUrl ?? "";
|
|
124103
124342
|
accessUrls = preferred.accessUrls;
|
|
124104
124343
|
accessUrlSource = preferred.accessUrlSource;
|
|
124105
|
-
|
|
124344
|
+
accessUrlReachable = preferred.accessUrlReachable;
|
|
124345
|
+
disabledAccessUrls = preferred.disabledAccessUrls;
|
|
124346
|
+
if (preferred.accessUrl) {
|
|
124347
|
+
break;
|
|
124348
|
+
}
|
|
124106
124349
|
}
|
|
124107
124350
|
}
|
|
124108
124351
|
try {
|
|
@@ -124118,18 +124361,31 @@ function registerHostingTools(server) {
|
|
|
124118
124361
|
projectName = path_1.default.basename(input.localPath);
|
|
124119
124362
|
}
|
|
124120
124363
|
}
|
|
124121
|
-
|
|
124122
|
-
|
|
124123
|
-
|
|
124124
|
-
|
|
124125
|
-
|
|
124126
|
-
|
|
124127
|
-
|
|
124364
|
+
// sendDeployNotification requires a concrete url; skip when no reachable accessUrl
|
|
124365
|
+
if (accessUrl) {
|
|
124366
|
+
await (0, notification_js_1.sendDeployNotification)(server, {
|
|
124367
|
+
deployType: 'hosting',
|
|
124368
|
+
url: accessUrl,
|
|
124369
|
+
projectId: envId,
|
|
124370
|
+
projectName,
|
|
124371
|
+
consoleUrl: `https://tcb.cloud.tencent.com/dev?envId=${envId}#/static-hosting`,
|
|
124372
|
+
});
|
|
124373
|
+
}
|
|
124128
124374
|
}
|
|
124129
124375
|
catch {
|
|
124130
124376
|
// Notification failure should not block uploads.
|
|
124131
124377
|
}
|
|
124132
124378
|
const uploadPrefix = input.cloudPath ?? input.files?.[0]?.cloudPath ?? '';
|
|
124379
|
+
const routeDisabled = staticDomainRouteEnabled === false;
|
|
124380
|
+
let message = '静态托管文件上传成功。若需要校验上传结果,请继续调用 queryHosting(action="findFiles") 或 queryHosting(action="listFiles")。';
|
|
124381
|
+
if (routeDisabled && accessUrlReachable) {
|
|
124382
|
+
message =
|
|
124383
|
+
'静态托管文件上传成功。默认静态托管域名的网关路由已禁用(访问会返回 GATEWAY_ROUTE_DISABLED),已改用其他可达域名作为 accessUrl。可用 manageGateway(action="updateRoute", route.enable=true) 重新启用默认域路由。';
|
|
124384
|
+
}
|
|
124385
|
+
else if (routeDisabled && !accessUrlReachable) {
|
|
124386
|
+
message =
|
|
124387
|
+
'静态托管文件上传成功,但默认静态托管域名的网关路由已禁用(GATEWAY_ROUTE_DISABLED),当前没有可达的 accessUrl。请用 manageGateway(action="updateRoute", domain="<静态域名>", path="/", upstreamResourceType="STATIC_STORE", targetName="staticstore", route.enable=true) 启用路由,或改用已启用的自定义域名 / CloudBase Sites 域名访问。';
|
|
124388
|
+
}
|
|
124133
124389
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
124134
124390
|
success: true,
|
|
124135
124391
|
data: {
|
|
@@ -124139,13 +124395,19 @@ function registerHostingTools(server) {
|
|
|
124139
124395
|
files: input.files ?? [],
|
|
124140
124396
|
ignore: input.ignore,
|
|
124141
124397
|
staticDomain,
|
|
124142
|
-
|
|
124398
|
+
staticDomainRouteEnabled,
|
|
124399
|
+
accessUrl: accessUrl || undefined,
|
|
124143
124400
|
accessUrls,
|
|
124144
124401
|
accessUrlSource,
|
|
124402
|
+
accessUrlReachable,
|
|
124403
|
+
...(disabledAccessUrls.length > 0
|
|
124404
|
+
? { disabledAccessUrls }
|
|
124405
|
+
: {}),
|
|
124406
|
+
...(routeDisabled ? { routeDisabled: true } : {}),
|
|
124145
124407
|
result,
|
|
124146
124408
|
nextActions: uploadPrefix ? [buildFindFilesNextStep(uploadPrefix.replace(/^\/+/, ''))] : undefined,
|
|
124147
124409
|
},
|
|
124148
|
-
message
|
|
124410
|
+
message,
|
|
124149
124411
|
});
|
|
124150
124412
|
}
|
|
124151
124413
|
case 'delete': {
|
|
@@ -142128,7 +142390,7 @@ class TelemetryReporter {
|
|
|
142128
142390
|
const arch = os_1.default.arch(); // 系统架构
|
|
142129
142391
|
// Build-time injected version; guard for vitest / non-webpack runtimes.
|
|
142130
142392
|
const mcpVersion = process.env.npm_package_version ||
|
|
142131
|
-
( true ? "2.
|
|
142393
|
+
( true ? "2.26.0" : 0);
|
|
142132
142394
|
return {
|
|
142133
142395
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
142134
142396
|
deviceId: this.deviceId,
|
|
@@ -223544,7 +223806,7 @@ ${envIdSection}
|
|
|
223544
223806
|
## 环境信息
|
|
223545
223807
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
223546
223808
|
- Node.js版本: ${process.version}
|
|
223547
|
-
- MCP 版本:${process.env.npm_package_version || ( true ? "2.
|
|
223809
|
+
- MCP 版本:${process.env.npm_package_version || ( true ? "2.26.0" : 0)}
|
|
223548
223810
|
- 系统架构: ${os_1.default.arch()}
|
|
223549
223811
|
- 时间: ${new Date().toISOString()}
|
|
223550
223812
|
- 请求ID: ${requestId}
|
|
@@ -223625,7 +223887,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
223625
223887
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
223626
223888
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
223627
223889
|
});
|
|
223628
|
-
const mcpVersion = true ? "2.
|
|
223890
|
+
const mcpVersion = true ? "2.26.0" : 0;
|
|
223629
223891
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
223630
223892
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
223631
223893
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -240845,7 +241107,7 @@ function registerSetupTools(server) {
|
|
|
240845
241107
|
server.registerTool("downloadTemplate", {
|
|
240846
241108
|
title: "下载项目模板",
|
|
240847
241109
|
description: `自动下载并部署CloudBase项目模板。
|
|
240848
|
-
**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.
|
|
241110
|
+
**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.26.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
240849
241111
|
inputSchema: {
|
|
240850
241112
|
template: zod_1.z
|
|
240851
241113
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -242259,6 +242521,7 @@ module.exports.sync = path => {
|
|
|
242259
242521
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
242260
242522
|
exports.buildVerificationUriComplete = buildVerificationUriComplete;
|
|
242261
242523
|
exports.buildDeviceAuthChallengePayload = buildDeviceAuthChallengePayload;
|
|
242524
|
+
exports.getCloudBaseApiKeyFromEnv = getCloudBaseApiKeyFromEnv;
|
|
242262
242525
|
exports.getAuthProgressStateSync = getAuthProgressStateSync;
|
|
242263
242526
|
exports.getAuthProgressState = getAuthProgressState;
|
|
242264
242527
|
exports.resolveAuthOptions = resolveAuthOptions;
|
|
@@ -242360,16 +242623,24 @@ function updateAuthProgressState(partial) {
|
|
|
242360
242623
|
});
|
|
242361
242624
|
return getAuthProgressStateSync();
|
|
242362
242625
|
}
|
|
242626
|
+
/**
|
|
242627
|
+
* Resolve CloudBase API Key from env.
|
|
242628
|
+
* Prefer CLOUDBASE_API_KEY; fall back to CLOUDBASE_APIKEY (js-sdk / node-sdk convention).
|
|
242629
|
+
*/
|
|
242630
|
+
function getCloudBaseApiKeyFromEnv() {
|
|
242631
|
+
return process.env.CLOUDBASE_API_KEY || process.env.CLOUDBASE_APIKEY || undefined;
|
|
242632
|
+
}
|
|
242363
242633
|
function normalizeLoginStateFromEnvVars(options) {
|
|
242364
242634
|
if (options?.ignoreEnvVars) {
|
|
242365
242635
|
return null;
|
|
242366
242636
|
}
|
|
242367
|
-
//
|
|
242368
|
-
const
|
|
242369
|
-
|
|
242637
|
+
// Prefer CloudBase API Key over TENCENTCLOUD_*
|
|
242638
|
+
const apiKey = getCloudBaseApiKeyFromEnv();
|
|
242639
|
+
const CLOUDBASE_ENV = process.env.CLOUDBASE_ENV_ID;
|
|
242640
|
+
if (apiKey && CLOUDBASE_ENV) {
|
|
242370
242641
|
return {
|
|
242371
242642
|
_type: 'api_key',
|
|
242372
|
-
apiKey
|
|
242643
|
+
apiKey,
|
|
242373
242644
|
envId: CLOUDBASE_ENV,
|
|
242374
242645
|
};
|
|
242375
242646
|
}
|
|
@@ -242541,7 +242812,7 @@ async function peekLoginState(options) {
|
|
|
242541
242812
|
}
|
|
242542
242813
|
async function ensureLogin(options) {
|
|
242543
242814
|
(0, logger_js_1.debug)("TENCENTCLOUD_SECRETID", { hasSecretId: !!process.env.TENCENTCLOUD_SECRETID });
|
|
242544
|
-
(0, logger_js_1.debug)("CLOUDBASE_API_KEY", { hasApiKey: !!
|
|
242815
|
+
(0, logger_js_1.debug)("CLOUDBASE_API_KEY", { hasApiKey: !!getCloudBaseApiKeyFromEnv() });
|
|
242545
242816
|
const loginState = await peekLoginState({
|
|
242546
242817
|
ignoreEnvVars: options?.ignoreEnvVars,
|
|
242547
242818
|
});
|
|
@@ -263813,6 +264084,8 @@ const MANAGE_GATEWAY_ACTIONS = [
|
|
|
263813
264084
|
"createRoute",
|
|
263814
264085
|
"updateRoute",
|
|
263815
264086
|
"deleteRoute",
|
|
264087
|
+
"enableRoute",
|
|
264088
|
+
"disableRoute",
|
|
263816
264089
|
"bindCustomDomain",
|
|
263817
264090
|
"deleteCustomDomain",
|
|
263818
264091
|
"enableService",
|
|
@@ -263885,6 +264158,7 @@ function registerGatewayTools(server) {
|
|
|
263885
264158
|
Domain: route.Domain,
|
|
263886
264159
|
Path: route.Path,
|
|
263887
264160
|
IsDefault: route.IsDefault,
|
|
264161
|
+
Enable: typeof route.Enable === "boolean" ? route.Enable : undefined,
|
|
263888
264162
|
UpstreamResourceType: route.UpstreamResourceType,
|
|
263889
264163
|
UpstreamResourceName: route.UpstreamResourceName,
|
|
263890
264164
|
}));
|
|
@@ -263895,16 +264169,26 @@ function registerGatewayTools(server) {
|
|
|
263895
264169
|
Domain: route.Domain,
|
|
263896
264170
|
Path: route.Path,
|
|
263897
264171
|
IsDefault: route.IsDefault,
|
|
264172
|
+
Enable: typeof route.Enable === "boolean" ? route.Enable : undefined,
|
|
263898
264173
|
UpstreamResourceType: route.UpstreamResourceType,
|
|
263899
264174
|
UpstreamResourceName: route.UpstreamResourceName,
|
|
263900
264175
|
}));
|
|
263901
|
-
const
|
|
264176
|
+
const disabledAccessUrls = (0, gateway_access_urls_js_1.rankGatewayAccessUrls)(candidates, {
|
|
264177
|
+
includeDisabled: true,
|
|
264178
|
+
})
|
|
264179
|
+
.filter((item) => !item.enabled)
|
|
264180
|
+
.map((item) => item.url);
|
|
264181
|
+
const envelope = (0, gateway_access_urls_js_1.toAccessUrlEnvelope)((0, gateway_access_urls_js_1.rankGatewayAccessUrls)(candidates), disabledAccessUrls);
|
|
263902
264182
|
return {
|
|
263903
264183
|
...(envelope.accessUrl ? { accessUrl: envelope.accessUrl } : {}),
|
|
263904
264184
|
accessUrls: envelope.accessUrls,
|
|
263905
264185
|
...(envelope.accessUrlSource
|
|
263906
264186
|
? { accessUrlSource: envelope.accessUrlSource }
|
|
263907
264187
|
: {}),
|
|
264188
|
+
accessUrlReachable: Boolean(envelope.accessUrl),
|
|
264189
|
+
...(disabledAccessUrls.length > 0
|
|
264190
|
+
? { disabledAccessUrls, routeDisabled: true }
|
|
264191
|
+
: {}),
|
|
263908
264192
|
};
|
|
263909
264193
|
};
|
|
263910
264194
|
const pickUsableDefaultDomain = (domains) => {
|
|
@@ -263994,6 +264278,15 @@ function registerGatewayTools(server) {
|
|
|
263994
264278
|
: input.enablePathTransmission !== undefined
|
|
263995
264279
|
? input.enablePathTransmission
|
|
263996
264280
|
: undefined;
|
|
264281
|
+
// Route enable: prefer route.enable. For updateRoute only, top-level
|
|
264282
|
+
// enable also maps to Route.Enable (enableService/authSwitch use the
|
|
264283
|
+
// same field name but different actions).
|
|
264284
|
+
const routeEnable = input.route?.enable !== undefined
|
|
264285
|
+
? input.route.enable
|
|
264286
|
+
: input.action === "updateRoute" &&
|
|
264287
|
+
typeof input.enable === "boolean"
|
|
264288
|
+
? input.enable
|
|
264289
|
+
: undefined;
|
|
263997
264290
|
const route = {
|
|
263998
264291
|
Path: path,
|
|
263999
264292
|
UpstreamResourceType: upstreamResourceType,
|
|
@@ -264003,6 +264296,9 @@ function registerGatewayTools(server) {
|
|
|
264003
264296
|
if (enablePathTransmission !== undefined) {
|
|
264004
264297
|
route.EnablePathTransmission = enablePathTransmission;
|
|
264005
264298
|
}
|
|
264299
|
+
if (routeEnable !== undefined) {
|
|
264300
|
+
route.Enable = routeEnable;
|
|
264301
|
+
}
|
|
264006
264302
|
return {
|
|
264007
264303
|
EnvId: await resolveEnvId(),
|
|
264008
264304
|
Domain: {
|
|
@@ -264016,9 +264312,46 @@ function registerGatewayTools(server) {
|
|
|
264016
264312
|
upstreamResourceName,
|
|
264017
264313
|
enableAuth,
|
|
264018
264314
|
enablePathTransmission,
|
|
264315
|
+
enable: routeEnable,
|
|
264019
264316
|
},
|
|
264020
264317
|
};
|
|
264021
264318
|
};
|
|
264319
|
+
const resolveExistingRouteForToggle = async (input) => {
|
|
264320
|
+
const routePath = input.routePath ?? input.path;
|
|
264321
|
+
if (!routePath) {
|
|
264322
|
+
throw new Error("action=enableRoute/disableRoute 时必须提供 path 或 route.path(例如 \"/\" 或 \"/api\")");
|
|
264323
|
+
}
|
|
264324
|
+
const normalizedPath = normalizeAccessPath(routePath);
|
|
264325
|
+
const preferredDomain = input.domain;
|
|
264326
|
+
const result = await listHttpServiceRoutes(preferredDomain ? { domain: preferredDomain } : undefined);
|
|
264327
|
+
const upstreamName = input.routeServiceName ?? input.targetName;
|
|
264328
|
+
const matches = flattenRoutes(result).filter((item) => {
|
|
264329
|
+
if (preferredDomain &&
|
|
264330
|
+
item.Domain !== preferredDomain) {
|
|
264331
|
+
return false;
|
|
264332
|
+
}
|
|
264333
|
+
if (normalizeAccessPath(String(item.Path ?? "")) !== normalizedPath) {
|
|
264334
|
+
return false;
|
|
264335
|
+
}
|
|
264336
|
+
if (upstreamName && item.UpstreamResourceName !== upstreamName) {
|
|
264337
|
+
return false;
|
|
264338
|
+
}
|
|
264339
|
+
return true;
|
|
264340
|
+
});
|
|
264341
|
+
if (matches.length === 0) {
|
|
264342
|
+
throw new Error(`未找到路径 ${normalizedPath} 的路由` +
|
|
264343
|
+
(preferredDomain ? `(域名 ${preferredDomain})` : "") +
|
|
264344
|
+
(upstreamName ? `(上游 ${upstreamName})` : "") +
|
|
264345
|
+
"。请先用 queryGateway(action=\"listRoutes\") 确认 Domain / Path,再调用 enableRoute/disableRoute。" +
|
|
264346
|
+
"关闭静态托管默认域名(*.tcloudbaseapp.com)时,请显式传 domain=该 STATIC_STORE IsDefault 域名,并通常 path=\"/\"。");
|
|
264347
|
+
}
|
|
264348
|
+
if (matches.length > 1) {
|
|
264349
|
+
const domains = [...new Set(matches.map((item) => item.Domain))];
|
|
264350
|
+
throw new Error(`路径 ${normalizedPath} 匹配到 ${matches.length} 条路由(域名:${domains.join(", ")})。` +
|
|
264351
|
+
"请补充 domain(必要时再加 targetName/route.serviceName)精确定位后再 enableRoute/disableRoute。");
|
|
264352
|
+
}
|
|
264353
|
+
return matches[0];
|
|
264354
|
+
};
|
|
264022
264355
|
const routeMutationNextActions = (targetName) => [
|
|
264023
264356
|
{
|
|
264024
264357
|
tool: "queryGateway",
|
|
@@ -264222,6 +264555,10 @@ function registerGatewayTools(server) {
|
|
|
264222
264555
|
Domain: payload.Domain,
|
|
264223
264556
|
});
|
|
264224
264557
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
264558
|
+
const routeEnabled = payload.resolved.enable !== false;
|
|
264559
|
+
const accessUrl = routeEnabled
|
|
264560
|
+
? `https://${payload.resolved.domain}${payload.resolved.path}`
|
|
264561
|
+
: undefined;
|
|
264225
264562
|
return buildEnvelope({
|
|
264226
264563
|
action: input.action,
|
|
264227
264564
|
model: "httpServiceRoute",
|
|
@@ -264231,13 +264568,31 @@ function registerGatewayTools(server) {
|
|
|
264231
264568
|
upstreamResourceName: payload.resolved.upstreamResourceName,
|
|
264232
264569
|
auth: payload.resolved.enableAuth ?? null,
|
|
264233
264570
|
enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
|
|
264234
|
-
|
|
264235
|
-
|
|
264571
|
+
enable: payload.resolved.enable ?? null,
|
|
264572
|
+
...(accessUrl
|
|
264573
|
+
? {
|
|
264574
|
+
accessUrl,
|
|
264575
|
+
accessUrls: [accessUrl],
|
|
264576
|
+
accessUrlReachable: true,
|
|
264577
|
+
}
|
|
264578
|
+
: {
|
|
264579
|
+
accessUrls: [],
|
|
264580
|
+
accessUrlReachable: false,
|
|
264581
|
+
routeDisabled: true,
|
|
264582
|
+
disabledAccessUrls: [
|
|
264583
|
+
`https://${payload.resolved.domain}${payload.resolved.path}`,
|
|
264584
|
+
],
|
|
264585
|
+
}),
|
|
264236
264586
|
accessUrlSource: input.domain && input.domain === payload.resolved.domain
|
|
264237
264587
|
? "gateway.custom"
|
|
264238
264588
|
: "gateway.default",
|
|
264239
264589
|
raw: result,
|
|
264240
264590
|
}, `HTTP 路由更新成功(${payload.resolved.domain}${payload.resolved.path}` +
|
|
264591
|
+
(payload.resolved.enable === false
|
|
264592
|
+
? ",路由已禁用 Enable=false"
|
|
264593
|
+
: payload.resolved.enable === true
|
|
264594
|
+
? ",路由已启用 Enable=true"
|
|
264595
|
+
: "") +
|
|
264241
264596
|
(payload.resolved.enablePathTransmission === true
|
|
264242
264597
|
? ",路径透传=开启"
|
|
264243
264598
|
: payload.resolved.enablePathTransmission === false
|
|
@@ -264245,6 +264600,102 @@ function registerGatewayTools(server) {
|
|
|
264245
264600
|
: "") +
|
|
264246
264601
|
`)`, routeMutationNextActions(payload.resolved.upstreamResourceName));
|
|
264247
264602
|
}
|
|
264603
|
+
case "enableRoute":
|
|
264604
|
+
case "disableRoute": {
|
|
264605
|
+
const enable = input.action === "enableRoute";
|
|
264606
|
+
const existing = await resolveExistingRouteForToggle({
|
|
264607
|
+
path: input.path,
|
|
264608
|
+
routePath: input.route?.path,
|
|
264609
|
+
domain: input.domain,
|
|
264610
|
+
targetName: input.targetName,
|
|
264611
|
+
routeServiceName: input.route?.serviceName,
|
|
264612
|
+
});
|
|
264613
|
+
const upstreamResourceType = resolveUpstreamResourceType({
|
|
264614
|
+
upstreamResourceType: input.upstreamResourceType ??
|
|
264615
|
+
existing.UpstreamResourceType,
|
|
264616
|
+
routeUpstreamResourceType: input.route?.upstreamResourceType,
|
|
264617
|
+
});
|
|
264618
|
+
const upstreamResourceName = input.route?.serviceName ??
|
|
264619
|
+
input.targetName ??
|
|
264620
|
+
String(existing.UpstreamResourceName ?? "");
|
|
264621
|
+
if (!upstreamResourceName) {
|
|
264622
|
+
throw new Error(`action=${input.action} 无法解析上游资源名;请用 queryGateway(listRoutes) 确认后传入 targetName 或 route.serviceName。`);
|
|
264623
|
+
}
|
|
264624
|
+
const normalizedPath = normalizeAccessPath(String(existing.Path ?? input.route?.path ?? input.path ?? "/"));
|
|
264625
|
+
const domain = existing.Domain;
|
|
264626
|
+
const enableAuth = input.route?.auth !== undefined
|
|
264627
|
+
? input.route.auth
|
|
264628
|
+
: input.auth !== undefined
|
|
264629
|
+
? input.auth
|
|
264630
|
+
: existing.EnableAuth;
|
|
264631
|
+
const enablePathTransmission = input.route?.enablePathTransmission !== undefined
|
|
264632
|
+
? input.route.enablePathTransmission
|
|
264633
|
+
: input.enablePathTransmission !== undefined
|
|
264634
|
+
? input.enablePathTransmission
|
|
264635
|
+
: existing.EnablePathTransmission;
|
|
264636
|
+
const route = {
|
|
264637
|
+
Path: normalizedPath,
|
|
264638
|
+
UpstreamResourceType: upstreamResourceType,
|
|
264639
|
+
UpstreamResourceName: upstreamResourceName,
|
|
264640
|
+
Enable: enable,
|
|
264641
|
+
};
|
|
264642
|
+
if (enableAuth !== undefined) {
|
|
264643
|
+
route.EnableAuth = enableAuth;
|
|
264644
|
+
}
|
|
264645
|
+
if (enablePathTransmission !== undefined) {
|
|
264646
|
+
route.EnablePathTransmission = enablePathTransmission;
|
|
264647
|
+
}
|
|
264648
|
+
const cloudbase = await getManager();
|
|
264649
|
+
const result = await cloudbase.env.modifyHttpServiceRoute({
|
|
264650
|
+
EnvId: await resolveEnvId(),
|
|
264651
|
+
Domain: {
|
|
264652
|
+
Domain: domain,
|
|
264653
|
+
Routes: [route],
|
|
264654
|
+
},
|
|
264655
|
+
});
|
|
264656
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
264657
|
+
const verb = enable ? "启用" : "禁用";
|
|
264658
|
+
const accessUrl = enable
|
|
264659
|
+
? `https://${domain}${normalizedPath}`
|
|
264660
|
+
: undefined;
|
|
264661
|
+
return buildEnvelope({
|
|
264662
|
+
action: input.action,
|
|
264663
|
+
model: "httpServiceRoute",
|
|
264664
|
+
domain,
|
|
264665
|
+
path: normalizedPath,
|
|
264666
|
+
upstreamResourceType,
|
|
264667
|
+
upstreamResourceName,
|
|
264668
|
+
enable,
|
|
264669
|
+
auth: enableAuth ?? null,
|
|
264670
|
+
enablePathTransmission: enablePathTransmission ?? null,
|
|
264671
|
+
...(accessUrl
|
|
264672
|
+
? {
|
|
264673
|
+
accessUrl,
|
|
264674
|
+
accessUrls: [accessUrl],
|
|
264675
|
+
accessUrlReachable: true,
|
|
264676
|
+
}
|
|
264677
|
+
: {
|
|
264678
|
+
accessUrls: [],
|
|
264679
|
+
accessUrlReachable: false,
|
|
264680
|
+
routeDisabled: true,
|
|
264681
|
+
disabledAccessUrls: [`https://${domain}${normalizedPath}`],
|
|
264682
|
+
}),
|
|
264683
|
+
accessUrlSource: existing.IsDefault
|
|
264684
|
+
? "gateway.default"
|
|
264685
|
+
: "gateway.custom",
|
|
264686
|
+
raw: result,
|
|
264687
|
+
}, `HTTP 路由已${verb}(${domain}${normalizedPath},Enable=${enable})。` +
|
|
264688
|
+
(enable
|
|
264689
|
+
? "启用后通常数秒到约 30 秒内可访问;请用 queryGateway(getRoute) 或探测 accessUrl 确认。"
|
|
264690
|
+
: "禁用后该路径将不可公网访问(GATEWAY_ROUTE_DISABLED);关闭静态托管默认域名(*.tcloudbaseapp.com)时,请确认 DomainType=STATIC_STORE 且 IsDefault=true。") +
|
|
264691
|
+
" 底层对应 tcb ModifyHTTPServiceRoute(不是 ModifyGatewayRoute)。", [
|
|
264692
|
+
{
|
|
264693
|
+
tool: "queryGateway",
|
|
264694
|
+
action: "getRoute",
|
|
264695
|
+
reason: `复核路由 Enable=${enable} 是否已生效`,
|
|
264696
|
+
},
|
|
264697
|
+
]);
|
|
264698
|
+
}
|
|
264248
264699
|
case "deleteRoute": {
|
|
264249
264700
|
const routePath = input.route?.path ?? input.path;
|
|
264250
264701
|
if (!routePath) {
|
|
@@ -264452,8 +264903,11 @@ function registerGatewayTools(server) {
|
|
|
264452
264903
|
}, async (input) => withEnvelope(() => handleQueryGateway(input)));
|
|
264453
264904
|
server.registerTool?.("manageGateway", {
|
|
264454
264905
|
title: "管理 CloudBase 网关",
|
|
264455
|
-
description: "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path
|
|
264906
|
+
description: "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path 转到上游;" +
|
|
264907
|
+
"enableRoute/disableRoute 启用或禁用已有路由(底层 ModifyHTTPServiceRoute 的 Routes[].Enable,不是 ModifyGatewayRoute)。" +
|
|
264908
|
+
"未传 domain 时用 DomainType=HTTPSERVICE 的 IsDefault 默认 HTTP 域名(形如 *.{region}.app.tcloudbase.com),不会使用静态托管 CDN 域名(*.tcloudbaseapp.com,DomainType=STATIC_STORE)。" +
|
|
264456
264909
|
"这是网关默认域上的路径路由,不是 STATIC_STORE 上游绑定;STATIC_STORE 上游必须显式传 upstreamResourceType=STATIC_STORE。" +
|
|
264910
|
+
"关闭静态托管默认域名(*.tcloudbaseapp.com):先 queryGateway(listRoutes) 找到 DomainType=STATIC_STORE 且 IsDefault=true 的 domain,再 manageGateway(action=\"disableRoute\", domain=该域名, path=\"/\");勿用 manageHosting。" +
|
|
264457
264911
|
"创建后可用 queryGateway(action=\"listRoutes\") 核对 Domain / DomainType / Path / UpstreamResourceType。" +
|
|
264458
264912
|
"上游类型只用一个参数 upstreamResourceType(也可写在 route.upstreamResourceType,route 优先):" +
|
|
264459
264913
|
"WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器;" +
|
|
@@ -264466,9 +264920,12 @@ function registerGatewayTools(server) {
|
|
|
264466
264920
|
inputSchema: {
|
|
264467
264921
|
action: zod_1.z
|
|
264468
264922
|
.enum(MANAGE_GATEWAY_ACTIONS)
|
|
264469
|
-
.describe("写操作:createRoute/updateRoute/deleteRoute 管理路由;
|
|
264923
|
+
.describe("写操作:createRoute/updateRoute/deleteRoute 管理路由;enableRoute/disableRoute 启用或禁用已有路由(需 path,建议显式传 domain);" +
|
|
264924
|
+
"bindCustomDomain/deleteCustomDomain 管理自定义域名;" +
|
|
264470
264925
|
"enableService/authSwitch 开关 HTTP 网关总开关与访问鉴权(需配合 enable 参数)。" +
|
|
264471
|
-
"createRoute/updateRoute 必须提供 upstreamResourceType
|
|
264926
|
+
"createRoute/updateRoute 必须提供 upstreamResourceType;enableRoute/disableRoute 会先 listRoutes 定位已有路由,通常不必重填上游。" +
|
|
264927
|
+
"updateRoute 也可传 enable/route.enable 直接改 Routes[].Enable。" +
|
|
264928
|
+
"关闭 *.tcloudbaseapp.com 默认静态托管域:disableRoute + domain=该 STATIC_STORE IsDefault 域名 + path=\"/\"。" +
|
|
264472
264929
|
"已有自定义域名时优先 createRoute(domain=已有域名) 实现访问,不必再次 bindCustomDomain / 传入 certificateId;" +
|
|
264473
264930
|
"bindCustomDomain 仅用于首次绑定新域名(需 certificateId;可选 accessType=DIRECT|CDN|CUSTOM,CUSTOM 需 customCname;普通场景用默认 DIRECT)。" +
|
|
264474
264931
|
"接入说明:https://docs.cloudbase.net/service/custom-domain"),
|
|
@@ -264517,16 +264974,25 @@ function registerGatewayTools(server) {
|
|
|
264517
264974
|
.boolean()
|
|
264518
264975
|
.optional()
|
|
264519
264976
|
.describe("同顶层 enablePathTransmission。route 内设置时优先于顶层。"),
|
|
264977
|
+
enable: zod_1.z
|
|
264978
|
+
.boolean()
|
|
264979
|
+
.optional()
|
|
264980
|
+
.describe("路由级开关(Routes[].Enable / Route.Enable)。createRoute/updateRoute 可用:" +
|
|
264981
|
+
"enable=false 禁用该 Domain+Path(访问返回 GATEWAY_ROUTE_DISABLED);" +
|
|
264982
|
+
"enable=true 重新启用。updateRoute 也可用顶层 enable 表达同一语义;" +
|
|
264983
|
+
"也可用专用 action enableRoute/disableRoute。route.enable 优先于顶层 enable。"),
|
|
264520
264984
|
})
|
|
264521
264985
|
.optional()
|
|
264522
264986
|
.describe("路由对象(可选写法)。例:云函数 {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"};" +
|
|
264523
264987
|
"云托管 {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"};" +
|
|
264524
|
-
"静态托管 {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"}
|
|
264988
|
+
"静态托管 {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"};" +
|
|
264989
|
+
"禁用路由 {path:\"/\",enable:false}(配合 updateRoute,或直接用 disableRoute)。"),
|
|
264525
264990
|
domain: zod_1.z
|
|
264526
264991
|
.string()
|
|
264527
264992
|
.optional()
|
|
264528
264993
|
.describe("域名。省略时自动使用环境 DomainType=HTTPSERVICE 的 IsDefault 默认 HTTP 域名(*.{region}.app.tcloudbase.com),不会回退到静态托管 CDN 域名(*.tcloudbaseapp.com,DomainType=STATIC_STORE);也不是 STATIC_STORE 上游绑定。" +
|
|
264529
264994
|
"可用 queryGateway(action=\"listRoutes\") 核对实际 Domain / DomainType。" +
|
|
264995
|
+
"enableRoute/disableRoute 操作 *.tcloudbaseapp.com 时必须显式传入该域名。" +
|
|
264530
264996
|
"已有自定义域名时请显式传入该域名并 createRoute/updateRoute/deleteRoute,即可实现自定义域名访问且无需证书 ID;" +
|
|
264531
264997
|
"仅 bindCustomDomain 时表示要新绑定的域名。"),
|
|
264532
264998
|
certificateId: zod_1.z
|
|
@@ -264549,9 +265015,11 @@ function registerGatewayTools(server) {
|
|
|
264549
265015
|
enable: zod_1.z
|
|
264550
265016
|
.boolean()
|
|
264551
265017
|
.optional()
|
|
264552
|
-
.describe("
|
|
264553
|
-
"bindCustomDomain 可选:enable=false
|
|
264554
|
-
"
|
|
265018
|
+
.describe("开关目标状态:enableService / authSwitch 必填(true 开启 / false 关闭);" +
|
|
265019
|
+
"bindCustomDomain 可选:enable=false 表示绑定后禁用域名(默认启用);" +
|
|
265020
|
+
"updateRoute 可选:映射到 Routes[].Enable(也可用 route.enable,route 优先;" +
|
|
265021
|
+
"也可用专用 action enableRoute/disableRoute)。" +
|
|
265022
|
+
"省略或非布尔值在 enableService/authSwitch 会返回参数错误。"),
|
|
264555
265023
|
},
|
|
264556
265024
|
annotations: {
|
|
264557
265025
|
readOnlyHint: false,
|