@cloudbase/cloudbase-mcp 2.25.10 → 2.27.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/README.md +7 -1
- package/README.zh-CN.md +1 -1
- package/dist/341.index.js +6 -1
- package/dist/602.index.cjs +6 -1
- package/dist/cli.cjs +5 -5
- package/dist/index.cjs +1334 -605
- package/dist/index.d.ts +8 -1
- package/dist/index.js +1357 -612
- 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"])
|
|
@@ -28365,6 +28452,7 @@ const manager_node_1 = __importDefault(__webpack_require__(95492));
|
|
|
28365
28452
|
const auth_js_1 = __webpack_require__(77291);
|
|
28366
28453
|
const logger_js_1 = __webpack_require__(13039);
|
|
28367
28454
|
const tool_result_js_1 = __webpack_require__(9835);
|
|
28455
|
+
const site_map_js_1 = __webpack_require__(68107);
|
|
28368
28456
|
// Timeout for envId auto-resolution flow.
|
|
28369
28457
|
// 10 minutes (600 seconds) - matches InteractiveServer timeout
|
|
28370
28458
|
const ENV_ID_TIMEOUT = 600000;
|
|
@@ -28389,7 +28477,7 @@ function createManagerFromLoginState(loginState, region) {
|
|
|
28389
28477
|
envId: loginState.envId,
|
|
28390
28478
|
token: loginState.token,
|
|
28391
28479
|
proxy: process.env.http_proxy,
|
|
28392
|
-
region: region ??
|
|
28480
|
+
region: region ?? (0, site_map_js_1.resolveSiteAndRegion)().region,
|
|
28393
28481
|
});
|
|
28394
28482
|
}
|
|
28395
28483
|
async function listAvailableEnvCandidates(options) {
|
|
@@ -28417,7 +28505,7 @@ async function listAvailableEnvCandidates(options) {
|
|
|
28417
28505
|
if (!loginState?.secretId || !loginState?.secretKey) {
|
|
28418
28506
|
return [];
|
|
28419
28507
|
}
|
|
28420
|
-
const region = cloudBaseOptions
|
|
28508
|
+
const region = (0, site_map_js_1.resolveSiteAndRegion)(cloudBaseOptions ?? {}).region;
|
|
28421
28509
|
cloudbase = createManagerFromLoginState(loginState, region);
|
|
28422
28510
|
}
|
|
28423
28511
|
try {
|
|
@@ -28618,7 +28706,10 @@ function getCachedEnvId() {
|
|
|
28618
28706
|
const databaseInstanceIdCache = new Map();
|
|
28619
28707
|
function buildDatabaseInstanceIdCacheKey(options) {
|
|
28620
28708
|
const envId = options?.envId ?? options?.cloudBaseOptions?.envId ?? process.env.CLOUDBASE_ENV_ID ?? 'unknown';
|
|
28621
|
-
const region =
|
|
28709
|
+
const region = (0, site_map_js_1.resolveSiteAndRegion)({
|
|
28710
|
+
site: options?.cloudBaseOptions?.site,
|
|
28711
|
+
region: options?.region ?? options?.cloudBaseOptions?.region,
|
|
28712
|
+
}).region;
|
|
28622
28713
|
return `${region}:${envId}`;
|
|
28623
28714
|
}
|
|
28624
28715
|
function resetDatabaseInstanceIdCache() {
|
|
@@ -28724,11 +28815,11 @@ async function getCloudBaseManager(options = {}) {
|
|
|
28724
28815
|
});
|
|
28725
28816
|
}
|
|
28726
28817
|
try {
|
|
28727
|
-
// Region priority: explicit option > env
|
|
28728
|
-
const fallbackRegion =
|
|
28818
|
+
// Region priority: explicit option > TCB_REGION/TCB_SITE env > .cloudbase/project.json > ap-shanghai default
|
|
28819
|
+
const { region: fallbackRegion, site } = (0, site_map_js_1.resolveSiteAndRegion)(cloudBaseOptions ?? {});
|
|
28729
28820
|
const loginState = authStrategy === 'ensure'
|
|
28730
|
-
? await (0, auth_js_1.getLoginState)({ region: fallbackRegion })
|
|
28731
|
-
: await (0, auth_js_1.peekLoginState)();
|
|
28821
|
+
? await (0, auth_js_1.getLoginState)({ region: fallbackRegion, site })
|
|
28822
|
+
: await (0, auth_js_1.peekLoginState)({ region: fallbackRegion, site });
|
|
28732
28823
|
if (!loginState) {
|
|
28733
28824
|
const authState = await (0, auth_js_1.getAuthProgressState)();
|
|
28734
28825
|
if (authState.status === 'PENDING') {
|
|
@@ -28776,7 +28867,7 @@ async function getCloudBaseManager(options = {}) {
|
|
|
28776
28867
|
}
|
|
28777
28868
|
// envId priority: explicit option > envManager cache > loginState.envId
|
|
28778
28869
|
const resolvedEnvId = finalEnvId || loginEnvId;
|
|
28779
|
-
// region 直接使用 fallbackRegion
|
|
28870
|
+
// region 直接使用 fallbackRegion(resolveSiteAndRegion 统一解析)
|
|
28780
28871
|
// 不再通过 DescribeEnvs 查询 region,以兼容 STS 临时密钥场景
|
|
28781
28872
|
const region = fallbackRegion;
|
|
28782
28873
|
const manager = new manager_node_1.default({
|
|
@@ -28808,8 +28899,8 @@ function createCloudBaseManagerWithOptions(cloudBaseOptions) {
|
|
|
28808
28899
|
hasSecretKey: !!cloudBaseOptions.secretKey,
|
|
28809
28900
|
hasToken: !!cloudBaseOptions.token,
|
|
28810
28901
|
});
|
|
28811
|
-
// Region priority: explicit option > env
|
|
28812
|
-
const region = cloudBaseOptions.region
|
|
28902
|
+
// Region priority: explicit option > TCB_REGION/TCB_SITE env > .cloudbase/project.json > ap-shanghai default
|
|
28903
|
+
const region = (0, site_map_js_1.resolveSiteAndRegion)(cloudBaseOptions).region;
|
|
28813
28904
|
const manager = new manager_node_1.default({
|
|
28814
28905
|
...cloudBaseOptions,
|
|
28815
28906
|
proxy: cloudBaseOptions.proxy || process.env.http_proxy,
|
|
@@ -41721,424 +41812,6 @@ function done(stream, er, data) {
|
|
|
41721
41812
|
})));
|
|
41722
41813
|
|
|
41723
41814
|
|
|
41724
|
-
/***/ }),
|
|
41725
|
-
|
|
41726
|
-
/***/ 9097:
|
|
41727
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
41728
|
-
|
|
41729
|
-
"use strict";
|
|
41730
|
-
|
|
41731
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
41732
|
-
if (k2 === undefined) k2 = k;
|
|
41733
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
41734
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
41735
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
41736
|
-
}
|
|
41737
|
-
Object.defineProperty(o, k2, desc);
|
|
41738
|
-
}) : (function(o, m, k, k2) {
|
|
41739
|
-
if (k2 === undefined) k2 = k;
|
|
41740
|
-
o[k2] = m[k];
|
|
41741
|
-
}));
|
|
41742
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
41743
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
41744
|
-
}) : function(o, v) {
|
|
41745
|
-
o["default"] = v;
|
|
41746
|
-
});
|
|
41747
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
41748
|
-
var ownKeys = function(o) {
|
|
41749
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41750
|
-
var ar = [];
|
|
41751
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41752
|
-
return ar;
|
|
41753
|
-
};
|
|
41754
|
-
return ownKeys(o);
|
|
41755
|
-
};
|
|
41756
|
-
return function (mod) {
|
|
41757
|
-
if (mod && mod.__esModule) return mod;
|
|
41758
|
-
var result = {};
|
|
41759
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41760
|
-
__setModuleDefault(result, mod);
|
|
41761
|
-
return result;
|
|
41762
|
-
};
|
|
41763
|
-
})();
|
|
41764
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
41765
|
-
exports.registerDownloadTools = registerDownloadTools;
|
|
41766
|
-
const crypto = __importStar(__webpack_require__(55511));
|
|
41767
|
-
const fs = __importStar(__webpack_require__(29021));
|
|
41768
|
-
const fsPromises = __importStar(__webpack_require__(79748));
|
|
41769
|
-
const http = __importStar(__webpack_require__(81630));
|
|
41770
|
-
const https = __importStar(__webpack_require__(55591));
|
|
41771
|
-
const os = __importStar(__webpack_require__(21820));
|
|
41772
|
-
const path = __importStar(__webpack_require__(33873));
|
|
41773
|
-
const url_1 = __webpack_require__(79551);
|
|
41774
|
-
const zod_1 = __webpack_require__(7552);
|
|
41775
|
-
const remote_url_safety_js_1 = __webpack_require__(89192);
|
|
41776
|
-
// 常量定义
|
|
41777
|
-
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
|
|
41778
|
-
const MAX_REDIRECT_HOPS = 5;
|
|
41779
|
-
const ALLOWED_CONTENT_TYPES = [
|
|
41780
|
-
"text/",
|
|
41781
|
-
"image/",
|
|
41782
|
-
"application/json",
|
|
41783
|
-
"application/xml",
|
|
41784
|
-
"application/pdf",
|
|
41785
|
-
"application/zip",
|
|
41786
|
-
"application/x-zip-compressed"
|
|
41787
|
-
];
|
|
41788
|
-
// 获取项目根目录
|
|
41789
|
-
function getProjectRoot() {
|
|
41790
|
-
// 优先级:环境变量 > 当前工作目录
|
|
41791
|
-
return process.env.WORKSPACE_FOLDER_PATHS ||
|
|
41792
|
-
process.env.PROJECT_ROOT ||
|
|
41793
|
-
process.env.GITHUB_WORKSPACE ||
|
|
41794
|
-
process.env.CI_PROJECT_DIR ||
|
|
41795
|
-
process.env.BUILD_SOURCESDIRECTORY ||
|
|
41796
|
-
process.cwd();
|
|
41797
|
-
}
|
|
41798
|
-
// 验证相对路径是否安全(不允许路径遍历)
|
|
41799
|
-
function isPathSafe(relativePath) {
|
|
41800
|
-
// 检查是否包含路径遍历操作
|
|
41801
|
-
if (relativePath.includes('..') ||
|
|
41802
|
-
relativePath.includes('~') ||
|
|
41803
|
-
path.isAbsolute(relativePath)) {
|
|
41804
|
-
return false;
|
|
41805
|
-
}
|
|
41806
|
-
// 检查路径是否规范化后仍然安全
|
|
41807
|
-
const normalizedPath = path.normalize(relativePath);
|
|
41808
|
-
if (normalizedPath.startsWith('..') ||
|
|
41809
|
-
normalizedPath.startsWith('/') ||
|
|
41810
|
-
normalizedPath.startsWith('\\')) {
|
|
41811
|
-
return false;
|
|
41812
|
-
}
|
|
41813
|
-
return true;
|
|
41814
|
-
}
|
|
41815
|
-
// 计算最终下载路径
|
|
41816
|
-
function calculateDownloadPath(relativePath) {
|
|
41817
|
-
const projectRoot = getProjectRoot();
|
|
41818
|
-
const finalPath = path.join(projectRoot, relativePath);
|
|
41819
|
-
// 确保最终路径在项目根目录内
|
|
41820
|
-
const normalizedProjectRoot = path.resolve(projectRoot);
|
|
41821
|
-
const normalizedFinalPath = path.resolve(finalPath);
|
|
41822
|
-
if (!normalizedFinalPath.startsWith(normalizedProjectRoot)) {
|
|
41823
|
-
throw new Error('相对路径超出项目根目录范围');
|
|
41824
|
-
}
|
|
41825
|
-
return finalPath;
|
|
41826
|
-
}
|
|
41827
|
-
// 生成随机文件名
|
|
41828
|
-
function generateRandomFileName(extension = '') {
|
|
41829
|
-
const randomBytes = crypto.randomBytes(16);
|
|
41830
|
-
const fileName = randomBytes.toString('hex');
|
|
41831
|
-
return `${fileName}${extension}`;
|
|
41832
|
-
}
|
|
41833
|
-
// 获取安全的临时文件路径
|
|
41834
|
-
function getSafeTempFilePath(fileName) {
|
|
41835
|
-
return path.join(os.tmpdir(), fileName);
|
|
41836
|
-
}
|
|
41837
|
-
// 从 URL 或 Content-Disposition 获取文件扩展名
|
|
41838
|
-
function getFileExtension(url, contentType, contentDisposition) {
|
|
41839
|
-
let extension = "";
|
|
41840
|
-
// 从 URL 获取扩展名
|
|
41841
|
-
const urlPath = new url_1.URL(url).pathname;
|
|
41842
|
-
const urlExt = path.extname(urlPath);
|
|
41843
|
-
if (urlExt) {
|
|
41844
|
-
extension = urlExt;
|
|
41845
|
-
}
|
|
41846
|
-
// 从 Content-Disposition 获取扩展名
|
|
41847
|
-
if (contentDisposition) {
|
|
41848
|
-
const filenameMatch = contentDisposition.match(/filename=["']?([^"']+)["']?/);
|
|
41849
|
-
if (filenameMatch) {
|
|
41850
|
-
const dispositionExt = path.extname(filenameMatch[1]);
|
|
41851
|
-
if (dispositionExt) {
|
|
41852
|
-
extension = dispositionExt;
|
|
41853
|
-
}
|
|
41854
|
-
}
|
|
41855
|
-
}
|
|
41856
|
-
// 从 Content-Type 获取扩展名
|
|
41857
|
-
if (!extension && contentType) {
|
|
41858
|
-
const mimeToExt = {
|
|
41859
|
-
"text/plain": ".txt",
|
|
41860
|
-
"text/html": ".html",
|
|
41861
|
-
"text/css": ".css",
|
|
41862
|
-
"text/javascript": ".js",
|
|
41863
|
-
"image/jpeg": ".jpg",
|
|
41864
|
-
"image/png": ".png",
|
|
41865
|
-
"image/gif": ".gif",
|
|
41866
|
-
"image/webp": ".webp",
|
|
41867
|
-
"application/json": ".json",
|
|
41868
|
-
"application/xml": ".xml",
|
|
41869
|
-
"application/pdf": ".pdf",
|
|
41870
|
-
"application/zip": ".zip",
|
|
41871
|
-
"application/x-zip-compressed": ".zip"
|
|
41872
|
-
};
|
|
41873
|
-
extension = mimeToExt[contentType] || "";
|
|
41874
|
-
}
|
|
41875
|
-
return extension;
|
|
41876
|
-
}
|
|
41877
|
-
function isAllowedContentType(contentType) {
|
|
41878
|
-
return ALLOWED_CONTENT_TYPES.some((allowedType) => contentType.startsWith(allowedType));
|
|
41879
|
-
}
|
|
41880
|
-
// 检查是否为可重试的网络错误
|
|
41881
|
-
function isRetryableError(error) {
|
|
41882
|
-
const retryableCodes = [
|
|
41883
|
-
'ECONNRESET',
|
|
41884
|
-
'ECONNREFUSED',
|
|
41885
|
-
'ETIMEDOUT',
|
|
41886
|
-
'ESOCKETTIMEDOUT',
|
|
41887
|
-
'ENOTFOUND',
|
|
41888
|
-
'EHOSTUNREACH',
|
|
41889
|
-
'ENETUNREACH',
|
|
41890
|
-
'ECONNABORTED'
|
|
41891
|
-
];
|
|
41892
|
-
return retryableCodes.includes(error.code || '');
|
|
41893
|
-
}
|
|
41894
|
-
// 下载文件到指定路径(带重试逻辑)
|
|
41895
|
-
async function downloadFileToPath(url, targetPath, retryCount = 0, redirectCount = 0) {
|
|
41896
|
-
const MAX_RETRIES = 3;
|
|
41897
|
-
const BASE_DELAY_MS = 1000; // 1秒基础延迟
|
|
41898
|
-
if (redirectCount > MAX_REDIRECT_HOPS) {
|
|
41899
|
-
throw new Error(`重定向次数超过 ${MAX_REDIRECT_HOPS} 次限制`);
|
|
41900
|
-
}
|
|
41901
|
-
const { requestUrl, requestOptions } = await (0, remote_url_safety_js_1.prepareSafeRemoteRequest)(url);
|
|
41902
|
-
const client = requestUrl.protocol === "https:" ? https : http;
|
|
41903
|
-
return new Promise((resolve, reject) => {
|
|
41904
|
-
const request = client.get(requestUrl, requestOptions ?? {}, async (res) => {
|
|
41905
|
-
// 处理重定向
|
|
41906
|
-
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
41907
|
-
try {
|
|
41908
|
-
const redirectUrl = new url_1.URL(res.headers.location, requestUrl).toString();
|
|
41909
|
-
return resolve(await downloadFileToPath(redirectUrl, targetPath, retryCount, redirectCount + 1));
|
|
41910
|
-
}
|
|
41911
|
-
catch (error) {
|
|
41912
|
-
return reject(error);
|
|
41913
|
-
}
|
|
41914
|
-
}
|
|
41915
|
-
if (res.statusCode !== 200) {
|
|
41916
|
-
reject(new Error(`HTTP Error: ${res.statusCode}`));
|
|
41917
|
-
return;
|
|
41918
|
-
}
|
|
41919
|
-
const contentType = res.headers["content-type"] || "";
|
|
41920
|
-
const contentLength = parseInt(res.headers["content-length"] || "0", 10);
|
|
41921
|
-
if (!isAllowedContentType(contentType)) {
|
|
41922
|
-
reject(new Error(`不允许的内容类型: ${contentType || "unknown"}`));
|
|
41923
|
-
return;
|
|
41924
|
-
}
|
|
41925
|
-
// 文件大小检查
|
|
41926
|
-
if (contentLength > MAX_FILE_SIZE) {
|
|
41927
|
-
reject(new Error(`文件大小 ${contentLength} 字节超过 ${MAX_FILE_SIZE} 字节限制`));
|
|
41928
|
-
return;
|
|
41929
|
-
}
|
|
41930
|
-
// 确保目标目录存在
|
|
41931
|
-
const targetDir = path.dirname(targetPath);
|
|
41932
|
-
try {
|
|
41933
|
-
await fsPromises.mkdir(targetDir, { recursive: true });
|
|
41934
|
-
}
|
|
41935
|
-
catch (error) {
|
|
41936
|
-
reject(new Error(`无法创建目标目录: ${error instanceof Error ? error.message : "未知错误"}`));
|
|
41937
|
-
return;
|
|
41938
|
-
}
|
|
41939
|
-
// 创建写入流
|
|
41940
|
-
const fileStream = fs.createWriteStream(targetPath);
|
|
41941
|
-
let downloadedSize = 0;
|
|
41942
|
-
res.on("data", (chunk) => {
|
|
41943
|
-
downloadedSize += chunk.length;
|
|
41944
|
-
if (downloadedSize > MAX_FILE_SIZE) {
|
|
41945
|
-
fileStream.destroy();
|
|
41946
|
-
fsPromises.unlink(targetPath).catch(() => { });
|
|
41947
|
-
reject(new Error(`文件大小超过 ${MAX_FILE_SIZE} 字节限制`));
|
|
41948
|
-
}
|
|
41949
|
-
});
|
|
41950
|
-
res.pipe(fileStream);
|
|
41951
|
-
fileStream.on("finish", () => {
|
|
41952
|
-
resolve({
|
|
41953
|
-
filePath: targetPath,
|
|
41954
|
-
contentType,
|
|
41955
|
-
fileSize: downloadedSize,
|
|
41956
|
-
});
|
|
41957
|
-
});
|
|
41958
|
-
fileStream.on("error", (error) => {
|
|
41959
|
-
fsPromises.unlink(targetPath).catch(() => { });
|
|
41960
|
-
reject(error);
|
|
41961
|
-
});
|
|
41962
|
-
});
|
|
41963
|
-
request.on("error", async (error) => {
|
|
41964
|
-
// 检查是否可重试
|
|
41965
|
-
if (isRetryableError(error) && retryCount < MAX_RETRIES) {
|
|
41966
|
-
const delay = BASE_DELAY_MS * Math.pow(2, retryCount); // 指数退避
|
|
41967
|
-
console.warn(`下载失败 (${error.code}),${delay}ms 后重试 (${retryCount + 1}/${MAX_RETRIES})...`);
|
|
41968
|
-
await new Promise((resolveDelay) => setTimeout(resolveDelay, delay));
|
|
41969
|
-
try {
|
|
41970
|
-
return resolve(await downloadFileToPath(url, targetPath, retryCount + 1, redirectCount));
|
|
41971
|
-
}
|
|
41972
|
-
catch (retryError) {
|
|
41973
|
-
return reject(retryError);
|
|
41974
|
-
}
|
|
41975
|
-
}
|
|
41976
|
-
reject(error);
|
|
41977
|
-
});
|
|
41978
|
-
});
|
|
41979
|
-
}
|
|
41980
|
-
// 下载文件到临时目录(保持向后兼容,带重试逻辑)
|
|
41981
|
-
async function downloadFile(url, retryCount = 0, redirectCount = 0) {
|
|
41982
|
-
const MAX_RETRIES = 3;
|
|
41983
|
-
const BASE_DELAY_MS = 1000; // 1秒基础延迟
|
|
41984
|
-
if (redirectCount > MAX_REDIRECT_HOPS) {
|
|
41985
|
-
throw new Error(`重定向次数超过 ${MAX_REDIRECT_HOPS} 次限制`);
|
|
41986
|
-
}
|
|
41987
|
-
const { requestUrl, requestOptions } = await (0, remote_url_safety_js_1.prepareSafeRemoteRequest)(url);
|
|
41988
|
-
const client = requestUrl.protocol === "https:" ? https : http;
|
|
41989
|
-
return new Promise((resolve, reject) => {
|
|
41990
|
-
const request = client.get(requestUrl, requestOptions ?? {}, async (res) => {
|
|
41991
|
-
// 处理重定向
|
|
41992
|
-
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
41993
|
-
try {
|
|
41994
|
-
const redirectUrl = new url_1.URL(res.headers.location, requestUrl).toString();
|
|
41995
|
-
return resolve(await downloadFile(redirectUrl, retryCount, redirectCount + 1));
|
|
41996
|
-
}
|
|
41997
|
-
catch (error) {
|
|
41998
|
-
return reject(error);
|
|
41999
|
-
}
|
|
42000
|
-
}
|
|
42001
|
-
if (res.statusCode !== 200) {
|
|
42002
|
-
reject(new Error(`HTTP Error: ${res.statusCode}`));
|
|
42003
|
-
return;
|
|
42004
|
-
}
|
|
42005
|
-
const contentType = res.headers["content-type"] || "";
|
|
42006
|
-
const contentLength = parseInt(res.headers["content-length"] || "0", 10);
|
|
42007
|
-
const contentDisposition = res.headers["content-disposition"];
|
|
42008
|
-
if (!isAllowedContentType(contentType)) {
|
|
42009
|
-
reject(new Error(`不允许的内容类型: ${contentType || "unknown"}`));
|
|
42010
|
-
return;
|
|
42011
|
-
}
|
|
42012
|
-
// 文件大小检查
|
|
42013
|
-
if (contentLength > MAX_FILE_SIZE) {
|
|
42014
|
-
reject(new Error(`文件大小 ${contentLength} 字节超过 ${MAX_FILE_SIZE} 字节限制`));
|
|
42015
|
-
return;
|
|
42016
|
-
}
|
|
42017
|
-
// 生成临时文件路径
|
|
42018
|
-
const extension = getFileExtension(requestUrl.toString(), contentType, contentDisposition);
|
|
42019
|
-
const fileName = generateRandomFileName(extension);
|
|
42020
|
-
const filePath = getSafeTempFilePath(fileName);
|
|
42021
|
-
// 创建写入流
|
|
42022
|
-
const fileStream = fs.createWriteStream(filePath);
|
|
42023
|
-
let downloadedSize = 0;
|
|
42024
|
-
res.on("data", (chunk) => {
|
|
42025
|
-
downloadedSize += chunk.length;
|
|
42026
|
-
if (downloadedSize > MAX_FILE_SIZE) {
|
|
42027
|
-
fileStream.destroy();
|
|
42028
|
-
fsPromises.unlink(filePath).catch(() => { });
|
|
42029
|
-
reject(new Error(`文件大小超过 ${MAX_FILE_SIZE} 字节限制`));
|
|
42030
|
-
}
|
|
42031
|
-
});
|
|
42032
|
-
res.pipe(fileStream);
|
|
42033
|
-
fileStream.on("finish", () => {
|
|
42034
|
-
resolve({
|
|
42035
|
-
filePath,
|
|
42036
|
-
contentType,
|
|
42037
|
-
fileSize: downloadedSize,
|
|
42038
|
-
});
|
|
42039
|
-
});
|
|
42040
|
-
fileStream.on("error", (error) => {
|
|
42041
|
-
fsPromises.unlink(filePath).catch(() => { });
|
|
42042
|
-
reject(error);
|
|
42043
|
-
});
|
|
42044
|
-
});
|
|
42045
|
-
request.on("error", async (error) => {
|
|
42046
|
-
// 检查是否可重试
|
|
42047
|
-
if (isRetryableError(error) && retryCount < MAX_RETRIES) {
|
|
42048
|
-
const delay = BASE_DELAY_MS * Math.pow(2, retryCount); // 指数退避
|
|
42049
|
-
console.warn(`下载失败 (${error.code}),${delay}ms 后重试 (${retryCount + 1}/${MAX_RETRIES})...`);
|
|
42050
|
-
await new Promise((resolveDelay) => setTimeout(resolveDelay, delay));
|
|
42051
|
-
try {
|
|
42052
|
-
return resolve(await downloadFile(url, retryCount + 1, redirectCount));
|
|
42053
|
-
}
|
|
42054
|
-
catch (retryError) {
|
|
42055
|
-
return reject(retryError);
|
|
42056
|
-
}
|
|
42057
|
-
}
|
|
42058
|
-
reject(error);
|
|
42059
|
-
});
|
|
42060
|
-
});
|
|
42061
|
-
}
|
|
42062
|
-
function buildDownloadRemoteFileErrorMessage(url, error) {
|
|
42063
|
-
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
42064
|
-
const suggestions = [];
|
|
42065
|
-
if (/HTTP Error:\s*404/i.test(baseMessage)) {
|
|
42066
|
-
suggestions.push("目标 URL 当前返回 404,说明资源链接已失效或路径不正确。");
|
|
42067
|
-
suggestions.push("请先在浏览器中确认该链接可访问,再重新执行下载。");
|
|
42068
|
-
}
|
|
42069
|
-
if (/ENOTFOUND|ECONNRESET|ETIMEDOUT|socket hang up/i.test(baseMessage)) {
|
|
42070
|
-
suggestions.push("下载过程中出现网络异常,系统已自动重试 3 次但均失败。");
|
|
42071
|
-
suggestions.push("建议:1) 检查本地网络或代理设置;2) 该网站可能限频,请尝试更换其他图片源(如 Unsplash、Pexels);3) 稍后重试。");
|
|
42072
|
-
}
|
|
42073
|
-
if (suggestions.length === 0) {
|
|
42074
|
-
suggestions.push("请检查 URL 是否可访问,以及目标资源是否允许直接下载。");
|
|
42075
|
-
}
|
|
42076
|
-
return `[downloadRemoteFile] ${baseMessage}\nURL: ${url}\n建议:${suggestions.join(" ")}`;
|
|
42077
|
-
}
|
|
42078
|
-
function registerDownloadTools(server) {
|
|
42079
|
-
server.registerTool("downloadRemoteFile", {
|
|
42080
|
-
title: "下载远程文件到指定路径",
|
|
42081
|
-
description: "下载远程文件到项目根目录下的指定相对路径。例如:小程序的 Tabbar 等素材图片,必须使用 **png** 格式,可以从 Unsplash、wikimedia【一般选用 500 大小即可、Pexels、Apple 官方 UI 等资源中选择来下载。",
|
|
42082
|
-
inputSchema: {
|
|
42083
|
-
url: zod_1.z.string().describe("远程文件的 URL 地址"),
|
|
42084
|
-
relativePath: zod_1.z.string().describe("相对于项目根目录的路径,例如:'assets/images/logo.png' 或 'docs/api.md'。不允许使用 ../ 等路径遍历操作。")
|
|
42085
|
-
},
|
|
42086
|
-
annotations: {
|
|
42087
|
-
readOnlyHint: false,
|
|
42088
|
-
destructiveHint: false,
|
|
42089
|
-
idempotentHint: false,
|
|
42090
|
-
openWorldHint: true,
|
|
42091
|
-
category: "download"
|
|
42092
|
-
}
|
|
42093
|
-
}, async ({ url, relativePath }) => {
|
|
42094
|
-
if (!isPathSafe(relativePath)) {
|
|
42095
|
-
return {
|
|
42096
|
-
content: [
|
|
42097
|
-
{
|
|
42098
|
-
type: "text",
|
|
42099
|
-
text: JSON.stringify({
|
|
42100
|
-
success: false,
|
|
42101
|
-
error: "不安全的相对路径",
|
|
42102
|
-
message: "相对路径包含路径遍历操作(../)或绝对路径,出于安全考虑已拒绝",
|
|
42103
|
-
suggestion: "请使用项目根目录下的相对路径,例如:'assets/images/logo.png'"
|
|
42104
|
-
}, null, 2)
|
|
42105
|
-
}
|
|
42106
|
-
]
|
|
42107
|
-
};
|
|
42108
|
-
}
|
|
42109
|
-
const targetPath = calculateDownloadPath(relativePath);
|
|
42110
|
-
const projectRoot = getProjectRoot();
|
|
42111
|
-
console.log(`📁 项目根目录: ${projectRoot}`);
|
|
42112
|
-
console.log(`📁 相对路径: ${relativePath}`);
|
|
42113
|
-
console.log(`📁 最终路径: ${targetPath}`);
|
|
42114
|
-
let result;
|
|
42115
|
-
try {
|
|
42116
|
-
result = await downloadFileToPath(url, targetPath);
|
|
42117
|
-
}
|
|
42118
|
-
catch (error) {
|
|
42119
|
-
throw new Error(buildDownloadRemoteFileErrorMessage(url, error));
|
|
42120
|
-
}
|
|
42121
|
-
return {
|
|
42122
|
-
content: [
|
|
42123
|
-
{
|
|
42124
|
-
type: "text",
|
|
42125
|
-
text: JSON.stringify({
|
|
42126
|
-
success: true,
|
|
42127
|
-
filePath: result.filePath,
|
|
42128
|
-
relativePath: relativePath,
|
|
42129
|
-
contentType: result.contentType,
|
|
42130
|
-
fileSize: result.fileSize,
|
|
42131
|
-
projectRoot: projectRoot,
|
|
42132
|
-
message: "文件下载成功到指定路径",
|
|
42133
|
-
note: `文件已保存到项目目录: ${relativePath}`
|
|
42134
|
-
}, null, 2)
|
|
42135
|
-
}
|
|
42136
|
-
]
|
|
42137
|
-
};
|
|
42138
|
-
});
|
|
42139
|
-
}
|
|
42140
|
-
|
|
42141
|
-
|
|
42142
41815
|
/***/ }),
|
|
42143
41816
|
|
|
42144
41817
|
/***/ 9141:
|
|
@@ -83594,27 +83267,6 @@ function done(stream, er, data) {
|
|
|
83594
83267
|
return stream.push(null);
|
|
83595
83268
|
}
|
|
83596
83269
|
|
|
83597
|
-
/***/ }),
|
|
83598
|
-
|
|
83599
|
-
/***/ 21454:
|
|
83600
|
-
/***/ ((__unused_webpack_module, exports) => {
|
|
83601
|
-
|
|
83602
|
-
"use strict";
|
|
83603
|
-
|
|
83604
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
83605
|
-
exports.isInternationalRegion = void 0;
|
|
83606
|
-
exports.isValidRegion = isValidRegion;
|
|
83607
|
-
const REGION = {
|
|
83608
|
-
SHANGHAI: 'ap-shanghai',
|
|
83609
|
-
SINGAPORE: 'ap-singapore',
|
|
83610
|
-
};
|
|
83611
|
-
const isInternationalRegion = (region) => region === REGION.SINGAPORE;
|
|
83612
|
-
exports.isInternationalRegion = isInternationalRegion;
|
|
83613
|
-
function isValidRegion(region) {
|
|
83614
|
-
return Object.values(REGION).includes(region);
|
|
83615
|
-
}
|
|
83616
|
-
|
|
83617
|
-
|
|
83618
83270
|
/***/ }),
|
|
83619
83271
|
|
|
83620
83272
|
/***/ 21605:
|
|
@@ -84798,12 +84450,19 @@ exports.colors = {
|
|
|
84798
84450
|
"use strict";
|
|
84799
84451
|
|
|
84800
84452
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
84453
|
+
exports.isGatewayRouteEnabled = isGatewayRouteEnabled;
|
|
84454
|
+
exports.isDomainPathReachableViaGateway = isDomainPathReachableViaGateway;
|
|
84801
84455
|
exports.rankGatewayAccessUrls = rankGatewayAccessUrls;
|
|
84802
84456
|
exports.flattenHttpServiceRoutes = flattenHttpServiceRoutes;
|
|
84803
84457
|
exports.filterRoutesByUpstream = filterRoutesByUpstream;
|
|
84804
84458
|
exports.toAccessUrlEnvelope = toAccessUrlEnvelope;
|
|
84805
84459
|
exports.resolveGatewayAccessUrls = resolveGatewayAccessUrls;
|
|
84460
|
+
exports.resolveAllGatewayRoutes = resolveAllGatewayRoutes;
|
|
84806
84461
|
exports.preferGatewayOrFallback = preferGatewayOrFallback;
|
|
84462
|
+
/** Route is reachable unless Enable is explicitly false. */
|
|
84463
|
+
function isGatewayRouteEnabled(route) {
|
|
84464
|
+
return route.Enable !== false;
|
|
84465
|
+
}
|
|
84807
84466
|
function normalizeAccessPath(path) {
|
|
84808
84467
|
if (!path) {
|
|
84809
84468
|
return "/";
|
|
@@ -84813,13 +84472,63 @@ function normalizeAccessPath(path) {
|
|
|
84813
84472
|
function buildHttpsUrl(domain, path) {
|
|
84814
84473
|
return `https://${domain}${normalizeAccessPath(path)}`;
|
|
84815
84474
|
}
|
|
84475
|
+
function extractHostname(urlOrHost) {
|
|
84476
|
+
const trimmed = urlOrHost.trim();
|
|
84477
|
+
if (!trimmed) {
|
|
84478
|
+
return "";
|
|
84479
|
+
}
|
|
84480
|
+
try {
|
|
84481
|
+
if (trimmed.includes("://")) {
|
|
84482
|
+
return new URL(trimmed).hostname.toLowerCase();
|
|
84483
|
+
}
|
|
84484
|
+
}
|
|
84485
|
+
catch {
|
|
84486
|
+
// Fall through to treat as bare hostname.
|
|
84487
|
+
}
|
|
84488
|
+
return trimmed.replace(/\/.*$/, "").toLowerCase();
|
|
84489
|
+
}
|
|
84490
|
+
/**
|
|
84491
|
+
* Whether a hostname has an enabled covering gateway route for the given path.
|
|
84492
|
+
* Longest matching Path prefix wins. Missing route info → unknown (null).
|
|
84493
|
+
*/
|
|
84494
|
+
function isDomainPathReachableViaGateway(routes, domainOrUrl, requestPath) {
|
|
84495
|
+
const hostname = extractHostname(domainOrUrl);
|
|
84496
|
+
if (!hostname) {
|
|
84497
|
+
return null;
|
|
84498
|
+
}
|
|
84499
|
+
const normalizedRequest = normalizeAccessPath(requestPath);
|
|
84500
|
+
const domainRoutes = routes.filter((route) => Boolean(route.Domain) &&
|
|
84501
|
+
route.Domain.toLowerCase() === hostname);
|
|
84502
|
+
if (domainRoutes.length === 0) {
|
|
84503
|
+
return null;
|
|
84504
|
+
}
|
|
84505
|
+
const covering = domainRoutes
|
|
84506
|
+
.map((route) => ({
|
|
84507
|
+
route,
|
|
84508
|
+
path: normalizeAccessPath(route.Path),
|
|
84509
|
+
}))
|
|
84510
|
+
.filter(({ path }) => {
|
|
84511
|
+
if (path === "/") {
|
|
84512
|
+
return true;
|
|
84513
|
+
}
|
|
84514
|
+
return (normalizedRequest === path ||
|
|
84515
|
+
normalizedRequest.startsWith(path.endsWith("/") ? path : `${path}/`));
|
|
84516
|
+
})
|
|
84517
|
+
.sort((a, b) => b.path.length - a.path.length);
|
|
84518
|
+
if (covering.length === 0) {
|
|
84519
|
+
return null;
|
|
84520
|
+
}
|
|
84521
|
+
return isGatewayRouteEnabled(covering[0].route);
|
|
84522
|
+
}
|
|
84816
84523
|
/**
|
|
84817
84524
|
* Rank gateway route URLs: custom domains (IsDefault !== true) before default domain.
|
|
84818
|
-
* Stable within each group; dedupe by full URL.
|
|
84525
|
+
* By default skips Enable === false routes. Stable within each group; dedupe by full URL.
|
|
84819
84526
|
*/
|
|
84820
|
-
function rankGatewayAccessUrls(routes) {
|
|
84527
|
+
function rankGatewayAccessUrls(routes, options) {
|
|
84528
|
+
const includeDisabled = options?.includeDisabled === true;
|
|
84821
84529
|
const mapped = routes
|
|
84822
84530
|
.filter((route) => Boolean(route.Domain))
|
|
84531
|
+
.filter((route) => includeDisabled || isGatewayRouteEnabled(route))
|
|
84823
84532
|
.map((route) => {
|
|
84824
84533
|
const path = normalizeAccessPath(route.Path);
|
|
84825
84534
|
const isDefault = route.IsDefault === true;
|
|
@@ -84828,6 +84537,7 @@ function rankGatewayAccessUrls(routes) {
|
|
|
84828
84537
|
domain: route.Domain,
|
|
84829
84538
|
path,
|
|
84830
84539
|
isDefault,
|
|
84540
|
+
enabled: isGatewayRouteEnabled(route),
|
|
84831
84541
|
source: (isDefault
|
|
84832
84542
|
? "gateway.default"
|
|
84833
84543
|
: "gateway.custom"),
|
|
@@ -84855,6 +84565,7 @@ function flattenHttpServiceRoutes(result) {
|
|
|
84855
84565
|
Domain: domainItem.Domain ?? "",
|
|
84856
84566
|
IsDefault: domainItem.IsDefault,
|
|
84857
84567
|
Path: typeof route.Path === "string" ? route.Path : undefined,
|
|
84568
|
+
Enable: typeof route.Enable === "boolean" ? route.Enable : undefined,
|
|
84858
84569
|
UpstreamResourceType: typeof route.UpstreamResourceType === "string"
|
|
84859
84570
|
? route.UpstreamResourceType
|
|
84860
84571
|
: undefined,
|
|
@@ -84877,64 +84588,116 @@ function filterRoutesByUpstream(routes, input) {
|
|
|
84877
84588
|
types.includes(String(route.UpstreamResourceType)));
|
|
84878
84589
|
});
|
|
84879
84590
|
}
|
|
84880
|
-
function toAccessUrlEnvelope(ranked) {
|
|
84881
|
-
|
|
84882
|
-
|
|
84591
|
+
function toAccessUrlEnvelope(ranked, disabledAccessUrls = []) {
|
|
84592
|
+
const enabled = ranked.filter((item) => item.enabled);
|
|
84593
|
+
if (enabled.length === 0) {
|
|
84594
|
+
return {
|
|
84595
|
+
accessUrls: [],
|
|
84596
|
+
routes: [],
|
|
84597
|
+
disabledAccessUrls,
|
|
84598
|
+
};
|
|
84883
84599
|
}
|
|
84884
84600
|
return {
|
|
84885
|
-
accessUrl:
|
|
84886
|
-
accessUrls:
|
|
84887
|
-
accessUrlSource:
|
|
84888
|
-
routes:
|
|
84601
|
+
accessUrl: enabled[0].url,
|
|
84602
|
+
accessUrls: enabled.map((item) => item.url),
|
|
84603
|
+
accessUrlSource: enabled[0].source,
|
|
84604
|
+
routes: enabled,
|
|
84605
|
+
disabledAccessUrls,
|
|
84889
84606
|
};
|
|
84890
84607
|
}
|
|
84608
|
+
async function loadHttpServiceRoutes(getManager, envId) {
|
|
84609
|
+
const manager = await getManager();
|
|
84610
|
+
const result = await manager.env.describeHttpServiceRoute({
|
|
84611
|
+
EnvId: envId,
|
|
84612
|
+
Limit: 1000,
|
|
84613
|
+
});
|
|
84614
|
+
return flattenHttpServiceRoutes(result);
|
|
84615
|
+
}
|
|
84891
84616
|
/**
|
|
84892
84617
|
* Best-effort lookup of gateway access URLs for an upstream resource.
|
|
84893
84618
|
* Never throws — returns empty lists on failure.
|
|
84619
|
+
* Disabled routes (Enable === false) are excluded from accessUrl(s).
|
|
84894
84620
|
*/
|
|
84895
84621
|
async function resolveGatewayAccessUrls(input) {
|
|
84896
84622
|
try {
|
|
84897
|
-
const
|
|
84898
|
-
const result = await manager.env.describeHttpServiceRoute({
|
|
84899
|
-
EnvId: input.envId,
|
|
84900
|
-
Limit: 1000,
|
|
84901
|
-
});
|
|
84902
|
-
const flattened = flattenHttpServiceRoutes(result);
|
|
84623
|
+
const flattened = await loadHttpServiceRoutes(input.getManager, input.envId);
|
|
84903
84624
|
const matched = filterRoutesByUpstream(flattened, {
|
|
84904
84625
|
upstreamResourceName: input.upstreamResourceName,
|
|
84905
84626
|
upstreamResourceTypes: input.upstreamResourceTypes,
|
|
84906
84627
|
});
|
|
84907
|
-
|
|
84628
|
+
const disabledAccessUrls = rankGatewayAccessUrls(matched, {
|
|
84629
|
+
includeDisabled: true,
|
|
84630
|
+
})
|
|
84631
|
+
.filter((item) => !item.enabled)
|
|
84632
|
+
.map((item) => item.url);
|
|
84633
|
+
return toAccessUrlEnvelope(rankGatewayAccessUrls(matched), disabledAccessUrls);
|
|
84908
84634
|
}
|
|
84909
84635
|
catch {
|
|
84910
|
-
return { accessUrls: [], routes: [] };
|
|
84636
|
+
return { accessUrls: [], routes: [], disabledAccessUrls: [] };
|
|
84637
|
+
}
|
|
84638
|
+
}
|
|
84639
|
+
/**
|
|
84640
|
+
* Load all flattened HTTP service routes (best-effort). Used by hosting to
|
|
84641
|
+
* judge whether the static-domain fallback itself is gateway-disabled.
|
|
84642
|
+
*/
|
|
84643
|
+
async function resolveAllGatewayRoutes(input) {
|
|
84644
|
+
try {
|
|
84645
|
+
return await loadHttpServiceRoutes(input.getManager, input.envId);
|
|
84646
|
+
}
|
|
84647
|
+
catch {
|
|
84648
|
+
return [];
|
|
84911
84649
|
}
|
|
84912
84650
|
}
|
|
84913
84651
|
/**
|
|
84914
84652
|
* Merge ranked gateway URLs with a resource-native fallback URL.
|
|
84915
84653
|
* Gateway custom/default always win over the fallback when present.
|
|
84654
|
+
* Unreachable fallbacks are omitted from accessUrl(s) and listed under
|
|
84655
|
+
* disabledAccessUrls instead.
|
|
84916
84656
|
*/
|
|
84917
84657
|
function preferGatewayOrFallback(input) {
|
|
84658
|
+
const disabledAccessUrls = [
|
|
84659
|
+
...(input.gateway.disabledAccessUrls ?? []),
|
|
84660
|
+
];
|
|
84661
|
+
const fallbackReachable = input.fallbackReachable !== false;
|
|
84918
84662
|
if (input.gateway.accessUrl) {
|
|
84919
84663
|
const urls = [...input.gateway.accessUrls];
|
|
84920
84664
|
if (input.fallbackUrl &&
|
|
84665
|
+
fallbackReachable &&
|
|
84921
84666
|
!urls.includes(input.fallbackUrl)) {
|
|
84922
84667
|
urls.push(input.fallbackUrl);
|
|
84923
84668
|
}
|
|
84669
|
+
else if (input.fallbackUrl &&
|
|
84670
|
+
!fallbackReachable &&
|
|
84671
|
+
!disabledAccessUrls.includes(input.fallbackUrl)) {
|
|
84672
|
+
disabledAccessUrls.push(input.fallbackUrl);
|
|
84673
|
+
}
|
|
84924
84674
|
return {
|
|
84925
84675
|
accessUrl: input.gateway.accessUrl,
|
|
84926
84676
|
accessUrls: urls,
|
|
84927
84677
|
accessUrlSource: input.gateway.accessUrlSource,
|
|
84678
|
+
accessUrlReachable: true,
|
|
84679
|
+
disabledAccessUrls,
|
|
84928
84680
|
};
|
|
84929
84681
|
}
|
|
84930
|
-
if (input.fallbackUrl) {
|
|
84682
|
+
if (input.fallbackUrl && fallbackReachable) {
|
|
84931
84683
|
return {
|
|
84932
84684
|
accessUrl: input.fallbackUrl,
|
|
84933
84685
|
accessUrls: [input.fallbackUrl],
|
|
84934
84686
|
accessUrlSource: input.fallbackSource,
|
|
84687
|
+
accessUrlReachable: true,
|
|
84688
|
+
disabledAccessUrls,
|
|
84935
84689
|
};
|
|
84936
84690
|
}
|
|
84937
|
-
|
|
84691
|
+
if (input.fallbackUrl && !fallbackReachable) {
|
|
84692
|
+
if (!disabledAccessUrls.includes(input.fallbackUrl)) {
|
|
84693
|
+
disabledAccessUrls.push(input.fallbackUrl);
|
|
84694
|
+
}
|
|
84695
|
+
}
|
|
84696
|
+
return {
|
|
84697
|
+
accessUrls: [],
|
|
84698
|
+
accessUrlReachable: false,
|
|
84699
|
+
disabledAccessUrls,
|
|
84700
|
+
};
|
|
84938
84701
|
}
|
|
84939
84702
|
|
|
84940
84703
|
|
|
@@ -91262,8 +91025,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
91262
91025
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
91263
91026
|
};
|
|
91264
91027
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
91265
|
-
exports.CUSTOM_IMAGE_RUNTIME = exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = void 0;
|
|
91028
|
+
exports.CUSTOM_IMAGE_RUNTIME = exports.LAYER_SOFT_WARN = exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = void 0;
|
|
91266
91029
|
exports.formatRuntimeList = formatRuntimeList;
|
|
91030
|
+
exports.layerNameIncludesEnvId = layerNameIncludesEnvId;
|
|
91031
|
+
exports.buildCreateLayerNameWarning = buildCreateLayerNameWarning;
|
|
91267
91032
|
exports.validateTimerCron = validateTimerCron;
|
|
91268
91033
|
exports.pickManageFunctionName = pickManageFunctionName;
|
|
91269
91034
|
exports.shouldInstallDependencyForFunction = shouldInstallDependencyForFunction;
|
|
@@ -91368,6 +91133,24 @@ exports.MANAGE_FUNCTION_ACTIONS = [
|
|
|
91368
91133
|
"updateFunctionLayers",
|
|
91369
91134
|
"incrementalDeployFunction", // 增量部署,需通过 pluginOptions.functions 注入实现
|
|
91370
91135
|
];
|
|
91136
|
+
/** Layer soft-warn copy — account-scoped SCF LayerName guidance (no hard fail). */
|
|
91137
|
+
exports.LAYER_SOFT_WARN = {
|
|
91138
|
+
createNameFormat: (envId) => `建议使用 {layerName}_${envId} 格式,当前名称可能与其他环境共享版本序列`,
|
|
91139
|
+
deleteVersion: "该层为账号级共享资源,删除版本会影响所有绑定该版本的环境的函数,请确认",
|
|
91140
|
+
bindShared: "层为账号级共享,绑定/解绑影响所有引用该层名的环境",
|
|
91141
|
+
accountLevelView: "返回账号级视图,含其他环境创建的层",
|
|
91142
|
+
};
|
|
91143
|
+
function layerNameIncludesEnvId(layerName, envId) {
|
|
91144
|
+
if (!envId)
|
|
91145
|
+
return true;
|
|
91146
|
+
return layerName.includes(envId);
|
|
91147
|
+
}
|
|
91148
|
+
function buildCreateLayerNameWarning(layerName, envId) {
|
|
91149
|
+
if (!envId || layerNameIncludesEnvId(layerName, envId)) {
|
|
91150
|
+
return undefined;
|
|
91151
|
+
}
|
|
91152
|
+
return exports.LAYER_SOFT_WARN.createNameFormat(envId);
|
|
91153
|
+
}
|
|
91371
91154
|
const VPC_SCHEMA = zod_1.z.object({
|
|
91372
91155
|
vpcId: zod_1.z
|
|
91373
91156
|
.string()
|
|
@@ -91609,11 +91392,12 @@ function registerFunctionTools(server) {
|
|
|
91609
91392
|
const cloudBaseOptions = server.cloudBaseOptions;
|
|
91610
91393
|
const deployOverrides = server.pluginOptions?.functions;
|
|
91611
91394
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
|
|
91612
|
-
const buildEnvelope = (data, message, nextActions) => ({
|
|
91395
|
+
const buildEnvelope = (data, message, nextActions, warnings) => ({
|
|
91613
91396
|
success: true,
|
|
91614
91397
|
data,
|
|
91615
91398
|
message,
|
|
91616
91399
|
...(nextActions?.length ? { nextActions } : {}),
|
|
91400
|
+
...(warnings?.length ? { warnings } : {}),
|
|
91617
91401
|
});
|
|
91618
91402
|
const buildErrorEnvelope = (error, errorCode) => ({
|
|
91619
91403
|
success: false,
|
|
@@ -91864,7 +91648,7 @@ function registerFunctionTools(server) {
|
|
|
91864
91648
|
action: "createLayerVersion",
|
|
91865
91649
|
reason: "发布新的层版本",
|
|
91866
91650
|
},
|
|
91867
|
-
]);
|
|
91651
|
+
], [exports.LAYER_SOFT_WARN.accountLevelView]);
|
|
91868
91652
|
}
|
|
91869
91653
|
case "listLayerVersions": {
|
|
91870
91654
|
if (!input.layerName) {
|
|
@@ -91892,7 +91676,7 @@ function registerFunctionTools(server) {
|
|
|
91892
91676
|
action: "attachLayer",
|
|
91893
91677
|
reason: "将某个层版本绑定到函数",
|
|
91894
91678
|
},
|
|
91895
|
-
]);
|
|
91679
|
+
], [exports.LAYER_SOFT_WARN.accountLevelView]);
|
|
91896
91680
|
}
|
|
91897
91681
|
case "getLayerVersionDetail": {
|
|
91898
91682
|
if (!input.layerName) {
|
|
@@ -91925,7 +91709,7 @@ function registerFunctionTools(server) {
|
|
|
91925
91709
|
action: "deleteLayerVersion",
|
|
91926
91710
|
reason: "删除该层版本",
|
|
91927
91711
|
},
|
|
91928
|
-
]);
|
|
91712
|
+
], [exports.LAYER_SOFT_WARN.accountLevelView]);
|
|
91929
91713
|
}
|
|
91930
91714
|
case "listFunctionTriggers": {
|
|
91931
91715
|
if (!input.functionName) {
|
|
@@ -92441,6 +92225,8 @@ function registerFunctionTools(server) {
|
|
|
92441
92225
|
if (!input.contentPath && !input.base64Content) {
|
|
92442
92226
|
throw new Error("createLayerVersion 操作时,contentPath 和 base64Content 至少需要提供一个");
|
|
92443
92227
|
}
|
|
92228
|
+
const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
92229
|
+
const nameWarning = buildCreateLayerNameWarning(input.layerName, envId);
|
|
92444
92230
|
const cloudbase = await getManager();
|
|
92445
92231
|
const result = await cloudbase.functions.createLayer({
|
|
92446
92232
|
name: input.layerName,
|
|
@@ -92463,7 +92249,7 @@ function registerFunctionTools(server) {
|
|
|
92463
92249
|
action: "listLayerVersions",
|
|
92464
92250
|
reason: "查看该层的全部版本",
|
|
92465
92251
|
},
|
|
92466
|
-
]);
|
|
92252
|
+
], nameWarning ? [nameWarning] : undefined);
|
|
92467
92253
|
}
|
|
92468
92254
|
case "deleteLayerVersion": {
|
|
92469
92255
|
if (!input.layerName) {
|
|
@@ -92490,7 +92276,7 @@ function registerFunctionTools(server) {
|
|
|
92490
92276
|
action: "listLayerVersions",
|
|
92491
92277
|
reason: "确认剩余层版本",
|
|
92492
92278
|
},
|
|
92493
|
-
]);
|
|
92279
|
+
], [exports.LAYER_SOFT_WARN.deleteVersion]);
|
|
92494
92280
|
}
|
|
92495
92281
|
case "attachLayer":
|
|
92496
92282
|
case "detachLayer":
|
|
@@ -92500,6 +92286,7 @@ function registerFunctionTools(server) {
|
|
|
92500
92286
|
}
|
|
92501
92287
|
const cloudbase = await getManager();
|
|
92502
92288
|
const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
92289
|
+
const bindWarnings = [exports.LAYER_SOFT_WARN.bindShared];
|
|
92503
92290
|
if (input.action === "attachLayer") {
|
|
92504
92291
|
if (!input.layerName) {
|
|
92505
92292
|
throw new Error("attachLayer 操作时,layerName 参数是必需的");
|
|
@@ -92528,7 +92315,7 @@ function registerFunctionTools(server) {
|
|
|
92528
92315
|
action: "listFunctionLayers",
|
|
92529
92316
|
reason: "确认函数当前绑定层列表",
|
|
92530
92317
|
},
|
|
92531
|
-
]);
|
|
92318
|
+
], bindWarnings);
|
|
92532
92319
|
}
|
|
92533
92320
|
if (input.action === "detachLayer") {
|
|
92534
92321
|
if (!input.layerName) {
|
|
@@ -92559,7 +92346,7 @@ function registerFunctionTools(server) {
|
|
|
92559
92346
|
action: "listFunctionLayers",
|
|
92560
92347
|
reason: "确认解绑后的层列表",
|
|
92561
92348
|
},
|
|
92562
|
-
]);
|
|
92349
|
+
], bindWarnings);
|
|
92563
92350
|
}
|
|
92564
92351
|
const normalizedLayers = normalizeManageLayers(input.layers);
|
|
92565
92352
|
if (!normalizedLayers.length) {
|
|
@@ -92584,7 +92371,7 @@ function registerFunctionTools(server) {
|
|
|
92584
92371
|
action: "listFunctionLayers",
|
|
92585
92372
|
reason: "确认最新层顺序和绑定结果",
|
|
92586
92373
|
},
|
|
92587
|
-
]);
|
|
92374
|
+
], bindWarnings);
|
|
92588
92375
|
}
|
|
92589
92376
|
default:
|
|
92590
92377
|
// incrementalDeployFunction:无默认实现,必须通过 pluginOptions 注入
|
|
@@ -92614,6 +92401,10 @@ function registerFunctionTools(server) {
|
|
|
92614
92401
|
"\n- 示例:`queryFunctions(action=\"listFunctionLogs\", functionName=\"my-function\")`" +
|
|
92615
92402
|
"\n- 如需查看日志详情:`queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
|
|
92616
92403
|
"\n\n**定时任务 / cron / 定时跑**:使用 `listFunctionTriggers` 查询函数的 timer 触发器配置。" +
|
|
92404
|
+
"\n\n**层(Layer)说明**:" +
|
|
92405
|
+
"\n- 层为 SCF 账号级共享命名空间:不同环境创建同名层会共享同一层的版本序列;删除某版本会影响所有绑定该版本的环境的函数" +
|
|
92406
|
+
"\n- 创建层必须用带环境标识的唯一层名,固定格式:`{layerName}_{当前envId}`(如 `common_cloud1-d9ghadgak3edf6b36`)。不要在不同环境使用相同裸层名,创建前先 `listLayers` 查重" +
|
|
92407
|
+
"\n- `listLayers` / `listLayerVersions` / `getLayerVersionDetail` 返回账号级视图,可能含其他环境创建的层" +
|
|
92617
92408
|
"\n\n**区分 `queryLogs` 工具**:" +
|
|
92618
92409
|
"\n- 本工具用于查询特定 CloudBase 云函数的执行日志" +
|
|
92619
92410
|
"\n- `queryLogs` 工具用于搜索 CLS 日志服务(跨服务日志聚合)",
|
|
@@ -92626,9 +92417,9 @@ function registerFunctionTools(server) {
|
|
|
92626
92417
|
"\n- `listFunctionLogs`: 查询 CloudBase 云函数执行日志(需要 functionName)" +
|
|
92627
92418
|
"\n- `getFunctionLogDetail`: 获取日志详情(需要 requestId)" +
|
|
92628
92419
|
"\n- `listFunctionLayers`: 列出函数绑定的层" +
|
|
92629
|
-
"\n- `listLayers`:
|
|
92630
|
-
"\n- `listLayerVersions`: 列出层的版本(注意:是 Versions 不是 Version
|
|
92631
|
-
"\n- `getLayerVersionDetail`:
|
|
92420
|
+
"\n- `listLayers`: 列出所有层(账号级视图,含其他环境创建的层)" +
|
|
92421
|
+
"\n- `listLayerVersions`: 列出层的版本(注意:是 Versions 不是 Version;账号级视图)" +
|
|
92422
|
+
"\n- `getLayerVersionDetail`: 获取层版本详情(账号级视图)" +
|
|
92632
92423
|
"\n- `listFunctionTriggers`: 列出函数触发器(用于查看定时任务 / cron / timer 配置)" +
|
|
92633
92424
|
"\n- `getFunctionDownloadUrl`: 获取函数代码下载地址"),
|
|
92634
92425
|
functionName: zod_1.z
|
|
@@ -92655,7 +92446,11 @@ function registerFunctionTools(server) {
|
|
|
92655
92446
|
qualifier: zod_1.z.string().optional().describe("函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选"),
|
|
92656
92447
|
runtime: zod_1.z.string().optional().describe("层查询的运行时筛选,如 Nodejs18.15"),
|
|
92657
92448
|
searchKey: zod_1.z.string().optional().describe("层名称搜索关键字"),
|
|
92658
|
-
layerName: zod_1.z
|
|
92449
|
+
layerName: zod_1.z
|
|
92450
|
+
.string()
|
|
92451
|
+
.optional()
|
|
92452
|
+
.describe("层名称。`listLayerVersions`、`getLayerVersionDetail` 操作必填。" +
|
|
92453
|
+
"层为账号级共享命名空间;推荐固定格式 `{layerName}_{当前envId}`(如 common_cloud1-d9ghadgak3edf6b36)"),
|
|
92659
92454
|
layerVersion: zod_1.z.number().optional().describe("层版本号。`getLayerVersionDetail` 操作必填"),
|
|
92660
92455
|
},
|
|
92661
92456
|
annotations: {
|
|
@@ -92670,12 +92465,18 @@ function registerFunctionTools(server) {
|
|
|
92670
92465
|
"如果要创建 cron 定时任务,先用 createFunction 创建函数,再用 createFunctionTrigger 创建 timer 触发器(支持7段cron表达式),deleteFunctionTrigger 删除触发器。" +
|
|
92671
92466
|
"镜像部署(Runtime=CustomImage):先把代码经 zip→COS→CloudApp custom 构建→TCR 推镜像(这一阶段为裸腾讯云 API,本工具不覆盖)," +
|
|
92672
92467
|
"再用 createFunction(func.runtime=\"CustomImage\", imageConfig) 基于 TCR 镜像创建 HTTP 函数;后续迭代用 updateFunctionCode + imageConfig 换镜像 tag。" +
|
|
92673
|
-
"危险操作需要显式 confirm=true。"
|
|
92468
|
+
"危险操作需要显式 confirm=true。" +
|
|
92469
|
+
"\n\n**层(Layer)说明**:" +
|
|
92470
|
+
"\n- 层为 SCF 账号级共享命名空间:不同环境创建同名层会共享同一层的版本序列;删除某版本会影响所有绑定该版本的环境的函数" +
|
|
92471
|
+
"\n- 创建层必须用带环境标识的唯一层名,固定格式:`{layerName}_{当前envId}`(如 `common_cloud1-d9ghadgak3edf6b36`)。不要在不同环境使用相同裸层名,创建前先 `listLayers` 查重" +
|
|
92472
|
+
"\n- 相关 action:`createLayerVersion` / `deleteLayerVersion` / `attachLayer` / `detachLayer` / `updateFunctionLayers`(只读查询见 queryFunctions 的 listLayers / listLayerVersions / getLayerVersionDetail)",
|
|
92674
92473
|
inputSchema: {
|
|
92675
92474
|
action: zod_1.z
|
|
92676
92475
|
.enum(exports.MANAGE_FUNCTION_ACTIONS)
|
|
92677
92476
|
.describe("写操作类型,例如 createFunction、updateFunctionCode、incrementalDeployFunction、invokeFunction、deleteFunction、" +
|
|
92678
|
-
"createFunctionTrigger(定时任务 / cron / timer)、deleteFunctionTrigger、
|
|
92477
|
+
"createFunctionTrigger(定时任务 / cron / timer)、deleteFunctionTrigger、" +
|
|
92478
|
+
"createLayerVersion、deleteLayerVersion、attachLayer、detachLayer、updateFunctionLayers。" +
|
|
92479
|
+
"层名推荐固定格式 `{layerName}_{当前envId}`(如 common_cloud1-d9ghadgak3edf6b36)"),
|
|
92679
92480
|
func: CREATE_FUNCTION_SCHEMA.optional().describe("createFunction 操作的函数配置"),
|
|
92680
92481
|
functionRootPath: zod_1.z.string().optional().describe("创建或更新函数代码时默认推荐的本地目录方式。" +
|
|
92681
92482
|
"必须是直接包含函数文件夹的目录绝对路径(如 /abs/path/cloudfunctions 或 /abs/path/functions)," +
|
|
@@ -92705,7 +92506,11 @@ function registerFunctionTools(server) {
|
|
|
92705
92506
|
.describe("createFunctionTrigger 的触发器列表,用于定时跑 / 定时任务 / scheduled job。timer 触发器使用7段 cron 表达式(秒 分 时 日 月 星期 年)," +
|
|
92706
92507
|
'如 "0 */5 * * * * *" 表示每5分钟执行一次'),
|
|
92707
92508
|
triggerName: zod_1.z.string().optional().describe("deleteFunctionTrigger 的目标触发器名称"),
|
|
92708
|
-
layerName: zod_1.z
|
|
92509
|
+
layerName: zod_1.z
|
|
92510
|
+
.string()
|
|
92511
|
+
.optional()
|
|
92512
|
+
.describe("层名称。创建层推荐固定格式 `{layerName}_{当前envId}`(如 common_cloud1-d9ghadgak3edf6b36);" +
|
|
92513
|
+
"不要跨环境复用裸层名。层为账号级共享命名空间"),
|
|
92709
92514
|
layerVersion: zod_1.z.number().optional().describe("层版本号"),
|
|
92710
92515
|
contentPath: zod_1.z.string().optional().describe("层内容路径,可为目录或 ZIP 文件"),
|
|
92711
92516
|
base64Content: zod_1.z.string().optional().describe("层内容的 base64 编码"),
|
|
@@ -105248,7 +105053,6 @@ const mcp_js_1 = __webpack_require__(7011);
|
|
|
105248
105053
|
const databaseNoSQL_js_1 = __webpack_require__(73903);
|
|
105249
105054
|
const databasePG_js_1 = __webpack_require__(56473);
|
|
105250
105055
|
const databaseSQL_js_1 = __webpack_require__(9782);
|
|
105251
|
-
const download_js_1 = __webpack_require__(9097);
|
|
105252
105056
|
const env_js_1 = __webpack_require__(622);
|
|
105253
105057
|
const functions_js_1 = __webpack_require__(25936);
|
|
105254
105058
|
const hosting_js_1 = __webpack_require__(37279);
|
|
@@ -105269,7 +105073,7 @@ const logs_js_1 = __webpack_require__(92322);
|
|
|
105269
105073
|
const permissions_js_1 = __webpack_require__(76485);
|
|
105270
105074
|
const cloud_mode_js_1 = __webpack_require__(89684);
|
|
105271
105075
|
const logger_js_1 = __webpack_require__(13039);
|
|
105272
|
-
const
|
|
105076
|
+
const site_map_js_1 = __webpack_require__(68107);
|
|
105273
105077
|
const tool_result_js_1 = __webpack_require__(9835);
|
|
105274
105078
|
const tool_wrapper_js_1 = __webpack_require__(71363);
|
|
105275
105079
|
// 默认插件列表
|
|
@@ -105291,13 +105095,15 @@ const DEFAULT_PLUGINS = [
|
|
|
105291
105095
|
"permissions",
|
|
105292
105096
|
"logs",
|
|
105293
105097
|
"agents",
|
|
105294
|
-
"download",
|
|
105295
105098
|
"capi",
|
|
105296
105099
|
];
|
|
105297
105100
|
function registerDatabase(server) {
|
|
105298
|
-
//
|
|
105299
|
-
const
|
|
105300
|
-
|
|
105101
|
+
// NoSQL 数据库按站点能力集合注册(国际站默认不支持 NoSQL)
|
|
105102
|
+
const { site } = (0, site_map_js_1.resolveSiteAndRegion)({
|
|
105103
|
+
site: server.cloudBaseOptions?.site,
|
|
105104
|
+
region: server.cloudBaseOptions?.region,
|
|
105105
|
+
});
|
|
105106
|
+
if (site_map_js_1.SITE_REGION_MAP[site].capabilities.noSql) {
|
|
105301
105107
|
(0, databaseNoSQL_js_1.registerDatabaseTools)(server);
|
|
105302
105108
|
}
|
|
105303
105109
|
(0, dataModel_js_1.registerDataModelTools)(server);
|
|
@@ -105306,8 +105112,11 @@ function registerMysqlDatabase(server) {
|
|
|
105306
105112
|
(0, databaseSQL_js_1.registerSQLDatabaseTools)(server);
|
|
105307
105113
|
}
|
|
105308
105114
|
function registerNoSQLDatabase(server) {
|
|
105309
|
-
const
|
|
105310
|
-
|
|
105115
|
+
const { site } = (0, site_map_js_1.resolveSiteAndRegion)({
|
|
105116
|
+
site: server.cloudBaseOptions?.site,
|
|
105117
|
+
region: server.cloudBaseOptions?.region,
|
|
105118
|
+
});
|
|
105119
|
+
if (site_map_js_1.SITE_REGION_MAP[site].capabilities.noSql) {
|
|
105311
105120
|
(0, databaseNoSQL_js_1.registerDatabaseTools)(server);
|
|
105312
105121
|
}
|
|
105313
105122
|
}
|
|
@@ -105326,7 +105135,6 @@ const AVAILABLE_PLUGINS = {
|
|
|
105326
105135
|
storage: { name: "storage", register: storage_js_1.registerStorageTools },
|
|
105327
105136
|
setup: { name: "setup", register: setup_js_1.registerSetupTools },
|
|
105328
105137
|
rag: { name: "rag", register: rag_js_1.registerRagTools },
|
|
105329
|
-
download: { name: "download", register: download_js_1.registerDownloadTools },
|
|
105330
105138
|
gateway: { name: "gateway", register: gateway_js_1.registerGatewayTools },
|
|
105331
105139
|
"app-auth": { name: "app-auth", register: app_auth_js_1.registerAppAuthTools },
|
|
105332
105140
|
permissions: { name: "permissions", register: permissions_js_1.registerPermissionTools },
|
|
@@ -116766,8 +116574,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
116766
116574
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
116767
116575
|
};
|
|
116768
116576
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
116769
|
-
exports.CLOUDRUN_ACCESS_TYPES = exports.CLOUDRUN_SERVICE_TYPES = void 0;
|
|
116577
|
+
exports.CLOUDRUN_PACKAGE_TYPES = exports.CLOUDRUN_ACCESS_TYPES = exports.CLOUDRUN_SERVICE_TYPES = void 0;
|
|
116578
|
+
exports.queryCloudRunEnvStatus = queryCloudRunEnvStatus;
|
|
116579
|
+
exports.describeCloudRunEnvStatus = describeCloudRunEnvStatus;
|
|
116580
|
+
exports.ensureCloudRunEnvInitialized = ensureCloudRunEnvInitialized;
|
|
116770
116581
|
exports.detectCloudRunDbNetworkRisk = detectCloudRunDbNetworkRisk;
|
|
116582
|
+
exports.extractCloudRunImageInfo = extractCloudRunImageInfo;
|
|
116771
116583
|
exports.registerCloudRunTools = registerCloudRunTools;
|
|
116772
116584
|
const child_process_1 = __webpack_require__(79646);
|
|
116773
116585
|
const fs_1 = __importDefault(__webpack_require__(29021));
|
|
@@ -116782,24 +116594,31 @@ const cloudrun_config_js_1 = __webpack_require__(48266);
|
|
|
116782
116594
|
exports.CLOUDRUN_SERVICE_TYPES = ['function', 'container'];
|
|
116783
116595
|
// CloudRun access types
|
|
116784
116596
|
exports.CLOUDRUN_ACCESS_TYPES = ['OA', 'PUBLIC', 'MINIAPP', 'VPC'];
|
|
116597
|
+
// CloudRun env package types (CreateCloudRunEnv.PackageType)
|
|
116598
|
+
exports.CLOUDRUN_PACKAGE_TYPES = ['Trial', 'Standard', 'Professional', 'Enterprise'];
|
|
116785
116599
|
// Input schema for queryCloudRun tool
|
|
116786
116600
|
const queryCloudRunInputSchema = {
|
|
116787
|
-
action: zod_1.z.enum(['list', 'detail', 'templates', 'getDeployLog']).describe('查询操作类型:list=获取云托管服务列表(支持分页和筛选),detail=查询指定服务的详细信息(包含服务配置和最新部署状态),templates=获取可用的项目模板列表(用于初始化新项目),getDeployLog
|
|
116601
|
+
action: zod_1.z.enum(['list', 'detail', 'templates', 'getDeployLog', 'envStatus']).describe('查询操作类型:list=获取云托管服务列表(支持分页和筛选),detail=查询指定服务的详细信息(包含服务配置和最新部署状态),templates=获取可用的项目模板列表(用于初始化新项目),getDeployLog=获取指定服务最近一次或指定构建的部署日志,envStatus=查询当前环境云托管是否已开通及开通状态(Status=creating开通中/normal已开通),用于initEnv之后轮询进度或deploy之前确认环境是否就绪'),
|
|
116788
116602
|
// List operation parameters
|
|
116789
116603
|
pageSize: zod_1.z.number().min(1).max(100).optional().default(10).describe('分页大小,控制每页返回的服务数量。取值范围:1-100,默认值:10。建议根据网络性能和显示需求调整'),
|
|
116790
116604
|
pageNum: zod_1.z.number().min(1).optional().default(1).describe('页码,用于分页查询。从1开始,默认值:1。配合pageSize使用可实现分页浏览'),
|
|
116791
116605
|
serverName: zod_1.z.string().optional().describe('服务名称筛选条件,支持模糊匹配。例如:输入"test"可匹配"test-service"、"my-test-app"等服务名称。留空则查询所有服务'),
|
|
116792
116606
|
serverType: zod_1.z.enum(exports.CLOUDRUN_SERVICE_TYPES).optional().describe('服务类型筛选条件:function=函数型云托管(仅支持Node.js,有特殊的开发要求和限制,适合简单的API服务),container=容器型服务(推荐使用,支持任意语言和框架如Java/Go/Python/PHP/.NET等,适合大多数应用场景)'),
|
|
116607
|
+
envId: zod_1.z.string().optional().describe('环境 ID(action=envStatus 时使用;不传则使用当前配置的环境)。格式如 env-xxxxxx'),
|
|
116793
116608
|
// Detail and log operation parameters
|
|
116794
116609
|
detailServerName: zod_1.z.string().optional().describe('要查询详细信息或部署日志的服务名称。当action为detail或getDeployLog时建议提供,必须是已存在的服务名称。可通过list操作获取可用的服务名称列表'),
|
|
116795
116610
|
buildId: zod_1.z.number().optional().describe('构建ID,仅在action=getDeployLog时使用。不传时默认返回最近一次部署的构建日志'),
|
|
116796
116611
|
};
|
|
116797
116612
|
// Input schema for manageCloudRun tool
|
|
116798
116613
|
const ManageCloudRunInputSchema = {
|
|
116799
|
-
action: zod_1.z.enum(['init', 'download', 'run', 'deploy', 'delete', 'createAgent', 'updateConfig']).describe('云托管服务管理操作类型:init=从模板初始化新的云托管项目代码(在targetPath目录下创建以serverName命名的子目录,支持多种语言和框架模板),download=从云端下载现有服务的代码到本地进行开发,run=在本地运行函数型云托管服务(用于开发和调试,仅支持函数型服务),deploy
|
|
116800
|
-
serverName: zod_1.z.string().describe('云托管服务名称,用于标识和管理服务。命名规则:支持大小写字母、数字、连字符和下划线,必须以字母开头,长度3-45个字符。在init操作中会作为在targetPath
|
|
116614
|
+
action: zod_1.z.enum(['init', 'download', 'run', 'deploy', 'delete', 'createAgent', 'updateConfig', 'initEnv']).describe('云托管服务管理操作类型:init=从模板初始化新的云托管项目代码(在targetPath目录下创建以serverName命名的子目录,支持多种语言和框架模板),download=从云端下载现有服务的代码到本地进行开发,run=在本地运行函数型云托管服务(用于开发和调试,仅支持函数型服务),deploy=将本地代码部署到云端云托管服务(支持函数型和容器型;传 imageUrl 时改为已有镜像部署,走 DeployType=image 容器型,targetPath 可省略;已存在服务会 Read-Merge-Write 保留远程 VpcConf/EnvParams/OpenAccessTypes),updateConfig=仅更新服务配置不重新上传代码(对齐控制台服务设置,走 SubmitServerConfigChangeDiff;不需要 targetPath),delete=删除指定的云托管服务(不可恢复,需要确认),createAgent=创建函数型Agent(基于函数型云托管开发AI智能体),initEnv=开通当前环境的云托管(异步创建云托管环境,幂等:已开通直接返回;适合新环境首次部署前使用)'),
|
|
116615
|
+
serverName: zod_1.z.string().describe('云托管服务名称,用于标识和管理服务。命名规则:支持大小写字母、数字、连字符和下划线,必须以字母开头,长度3-45个字符。在init操作中会作为在targetPath下创建的子目录名,在其他操作中作为目标服务名。initEnv 操作不需要此参数'),
|
|
116616
|
+
// InitEnv operation parameters
|
|
116617
|
+
envId: zod_1.z.string().optional().describe('环境 ID(action=initEnv 时使用;不传则使用当前配置的环境)。格式如 env-xxxxxx'),
|
|
116618
|
+
packageType: zod_1.z.enum(exports.CLOUDRUN_PACKAGE_TYPES).optional().default('Trial').describe('云托管环境套餐类型(action=initEnv 时使用):Trial=试用,Standard=标准,Professional=专业,Enterprise=企业。默认 Trial'),
|
|
116801
116619
|
// Deploy operation parameters
|
|
116802
|
-
targetPath: zod_1.z.string().optional().describe('本地代码路径,必须是绝对路径。在deploy操作中指定要部署的代码目录,在download操作中指定下载目标目录,在init操作中指定云托管服务的上级目录(会在该目录下创建以serverName命名的子目录)。updateConfig 不需要此参数。建议约定:项目根目录下的cloudrun/目录,例如:/Users/username/projects/my-project/cloudrun'),
|
|
116620
|
+
targetPath: zod_1.z.string().optional().describe('本地代码路径,必须是绝对路径。在deploy操作中指定要部署的代码目录,在download操作中指定下载目标目录,在init操作中指定云托管服务的上级目录(会在该目录下创建以serverName命名的子目录)。updateConfig 不需要此参数。建议约定:项目根目录下的cloudrun/目录,例如:/Users/username/projects/my-project/cloudrun。使用 imageUrl 部署已有镜像时此参数可省略'),
|
|
116621
|
+
imageUrl: zod_1.z.string().optional().describe('已有镜像部署(action=deploy 时使用):直接指定容器镜像地址,如 ccr.ccs.tencentyun.com/ns/img:v1 或公网 registry 地址。传入后走 DeployType="image"(容器型)部署,无需本地源码目录(targetPath 可省略)。支持:1) 公网匿名可拉取的镜像直填地址;2) 私有/需登录的镜像(如 ghcr.io)需先在本地 docker pull → docker tag/push 到腾讯云 CCR → 填入 CCR 地址。不传则维持源码构建(本地代码打包上传)。注意:无论哪种部署方式,环境都需先开通云托管(未开通时先调用 initEnv,Status=normal 后再部署)'),
|
|
116803
116622
|
envParamsReplaceAll: zod_1.z.boolean().optional().default(false).describe('EnvParams 合并策略(deploy / updateConfig):false(默认)= 与远程按 key 合并(输入覆盖同名 key,远程其余 key 保留);true= 用输入 EnvParams 整包替换远程。仅当显式传入 EnvParams 时生效'),
|
|
116804
116623
|
serverConfig: zod_1.z.object({
|
|
116805
116624
|
OpenAccessTypes: zod_1.z.array(zod_1.z.enum(exports.CLOUDRUN_ACCESS_TYPES)).optional().describe('公网访问类型配置,控制服务的访问权限:OA=办公网访问,PUBLIC=公网访问(默认,可通过HTTPS域名访问),MINIAPP=小程序访问,VPC=VPC访问(仅同VPC内可访问)。可配置多个类型'),
|
|
@@ -116945,6 +116764,88 @@ function buildManageCloudRunErrorMessage(action, serverName, error) {
|
|
|
116945
116764
|
}
|
|
116946
116765
|
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;
|
|
116947
116766
|
const CLOUDRUN_DB_URL_PATTERN = /(mysql|mariadb|postgres|postgresql|mongodb(\+srv)?|redis|rediss):\/\//i;
|
|
116767
|
+
async function queryCloudRunEnvStatus(options) {
|
|
116768
|
+
const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions: options.cloudBaseOptions });
|
|
116769
|
+
if (!manager?.commonService) {
|
|
116770
|
+
throw new Error("Current CloudBase Manager does not support commonService; cannot query CloudRun env status.");
|
|
116771
|
+
}
|
|
116772
|
+
return describeCloudRunEnvStatus(manager, options.envId);
|
|
116773
|
+
}
|
|
116774
|
+
/**
|
|
116775
|
+
* 核心实现:用已获取的 manager 查询云托管开通状态(供 initEnv/envStatus 及
|
|
116776
|
+
* ensureCloudRunEnvInitialized 复用,避免重复 getCloudBaseManager)。
|
|
116777
|
+
*/
|
|
116778
|
+
async function describeCloudRunEnvStatus(manager, envId) {
|
|
116779
|
+
const result = await manager
|
|
116780
|
+
.commonService("tcbr", "2022-02-17")
|
|
116781
|
+
.call({
|
|
116782
|
+
Action: "DescribeEnvBaseInfo",
|
|
116783
|
+
Param: { EnvId: envId },
|
|
116784
|
+
});
|
|
116785
|
+
const data = (result ?? {});
|
|
116786
|
+
const baseInfo = (data.EnvBaseInfo ?? {}) ?? {};
|
|
116787
|
+
if (data.IsExist !== true) {
|
|
116788
|
+
return { isExist: false, status: "unopened", baseInfo };
|
|
116789
|
+
}
|
|
116790
|
+
const rawStatus = typeof baseInfo.Status === "string" ? baseInfo.Status : "";
|
|
116791
|
+
const status = rawStatus === "creating" || rawStatus === "normal"
|
|
116792
|
+
? rawStatus
|
|
116793
|
+
: "unknown";
|
|
116794
|
+
return { isExist: true, status, baseInfo };
|
|
116795
|
+
}
|
|
116796
|
+
/**
|
|
116797
|
+
* 探测当前环境云托管(大租户)是否已初始化。
|
|
116798
|
+
*
|
|
116799
|
+
* 背景(2026-08-13 用户实测):新环境未调 CreateCloudRunEnv 初始化云托管时,
|
|
116800
|
+
* 直接 CreateCloudRunServer 会因"无大租户记录"被默认转入小租户,创建出小租户的
|
|
116801
|
+
* 服务和版本——这是错误路径。本函数在 deploy 创建新服务前用 tcbr DescribeEnvBaseInfo
|
|
116802
|
+
* 探测环境是否已开通云托管;未开通则抛错引导先初始化,而不是默默走到小租户路径。
|
|
116803
|
+
*
|
|
116804
|
+
* 实测(2026-08-13 真实凭据):tcbr 不存在 DescribeCloudRunEnv(单数)Action,调用
|
|
116805
|
+
* 恒返回 InvalidAction;正确的探测接口是 DescribeEnvBaseInfo。未开通云托管的环境返回
|
|
116806
|
+
* IsExist=false 且 EnvBaseInfo 为空结构(不抛错);已开通的环境(实测 ai-share-
|
|
116807
|
+
* d2guukyxybb63b206)返回 IsExist=true 且 EnvBaseInfo 含完整字段(Status="normal"、
|
|
116808
|
+
* PackageType/Region/EnvType/CreateTime 等已填充),两分支可明确区分。
|
|
116809
|
+
*
|
|
116810
|
+
* 双验证:DescribeCloudRunServers 未初始化与已初始化但无服务均返回 ServerList=[],
|
|
116811
|
+
* 无法区分,故不作为初始化判定依据。
|
|
116812
|
+
*
|
|
116813
|
+
* @returns 已初始化返回 true;探测到未初始化抛出带引导信息的 Error。
|
|
116814
|
+
*/
|
|
116815
|
+
async function ensureCloudRunEnvInitialized(options) {
|
|
116816
|
+
const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions: options.cloudBaseOptions });
|
|
116817
|
+
if (!manager?.commonService) {
|
|
116818
|
+
// 老 SDK 无 commonService 时退化为不拦截(保持既有行为,避免误伤)。
|
|
116819
|
+
return true;
|
|
116820
|
+
}
|
|
116821
|
+
try {
|
|
116822
|
+
const status = await describeCloudRunEnvStatus(manager, options.envId);
|
|
116823
|
+
if (!status.isExist) {
|
|
116824
|
+
throwCloudRunEnvNotInitialized(options.envId);
|
|
116825
|
+
}
|
|
116826
|
+
return true;
|
|
116827
|
+
}
|
|
116828
|
+
catch (error) {
|
|
116829
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
116830
|
+
// 错误兜底:未初始化/未开通类错误码。
|
|
116831
|
+
// 注意:裸 InvalidParameter 可能是普通参数错误,不拦截;仅当其带 Env/CloudRun
|
|
116832
|
+
// 上下文(疑似 "EnvironmentId not found / CloudRun Env 未开通")时才按未初始化处理。
|
|
116833
|
+
if (/ResourceNotFound|not.?initialized|未开通|未初始化/i.test(msg) ||
|
|
116834
|
+
/InvalidParameter.*(?:Env|CloudRun)/i.test(msg)) {
|
|
116835
|
+
throwCloudRunEnvNotInitialized(options.envId);
|
|
116836
|
+
}
|
|
116837
|
+
// 其他错误(网络/权限等)不拦截,让上层按原逻辑处理。
|
|
116838
|
+
return true;
|
|
116839
|
+
}
|
|
116840
|
+
}
|
|
116841
|
+
function throwCloudRunEnvNotInitialized(envId) {
|
|
116842
|
+
throw new Error(`当前环境(${envId})尚未初始化云托管(CloudRun Env)。` +
|
|
116843
|
+
`不能直接创建服务(CreateCloudRunServer 在无大租户记录时会默认创建到小租户,产生错误的小租户服务与版本)。\n` +
|
|
116844
|
+
`请先开通云托管环境,再重试部署:\n` +
|
|
116845
|
+
`- MCP:manageCloudRun(action="initEnv", envId="${envId}")(异步开通,幂等;开通完成后可用 queryCloudRun(action="envStatus", envId="${envId}") 查询 Status=normal)\n` +
|
|
116846
|
+
`- 或控制台:环境 → 云托管 → 开通(https://tcb.cloud.tencent.com/dev?envId=${envId}#/platform-run)\n` +
|
|
116847
|
+
`初始化完成后重新调用 manageCloudRun(action="deploy")。`);
|
|
116848
|
+
}
|
|
116948
116849
|
/**
|
|
116949
116850
|
* Detect likely TCP database/cache env usage without VpcConf.
|
|
116950
116851
|
* Soft signal for AI agents — does not block deploy.
|
|
@@ -117041,6 +116942,36 @@ function normalizeCloudRunDomainUrl(input) {
|
|
|
117041
116942
|
? raw
|
|
117042
116943
|
: `https://${raw}`;
|
|
117043
116944
|
}
|
|
116945
|
+
/**
|
|
116946
|
+
* 从服务详情 / 最新部署记录中提取镜像信息(镜像部署时才有)。
|
|
116947
|
+
* 镜像部署(DeployType=image)的服务详情与部署记录通常会携带 ImageUrl 等字段;
|
|
116948
|
+
* 源码构建的服务无此字段,返回 undefined 表示无镜像信息。
|
|
116949
|
+
*/
|
|
116950
|
+
function extractCloudRunImageInfo(serviceDetail, latestDeploy) {
|
|
116951
|
+
const candidates = [
|
|
116952
|
+
latestDeploy,
|
|
116953
|
+
serviceDetail?.ServerConfig,
|
|
116954
|
+
serviceDetail?.BaseInfo,
|
|
116955
|
+
serviceDetail,
|
|
116956
|
+
];
|
|
116957
|
+
for (const source of candidates) {
|
|
116958
|
+
if (!source || typeof source !== "object")
|
|
116959
|
+
continue;
|
|
116960
|
+
const imageUrl = source.ImageUrl ??
|
|
116961
|
+
source.imageUrl ??
|
|
116962
|
+
(typeof source.ImageInfo === "string" ? source.ImageInfo : undefined) ??
|
|
116963
|
+
(typeof source.ImageInfo?.ImageUrl === "string" ? source.ImageInfo.ImageUrl : undefined);
|
|
116964
|
+
if (typeof imageUrl === "string" && imageUrl.trim()) {
|
|
116965
|
+
const deployType = typeof source.DeployType === "string"
|
|
116966
|
+
? source.DeployType
|
|
116967
|
+
: typeof source.deployType === "string"
|
|
116968
|
+
? source.deployType
|
|
116969
|
+
: undefined;
|
|
116970
|
+
return { imageUrl: imageUrl.trim(), ...(deployType ? { deployType } : {}) };
|
|
116971
|
+
}
|
|
116972
|
+
}
|
|
116973
|
+
return undefined;
|
|
116974
|
+
}
|
|
117044
116975
|
function resolveCloudRunFallbackAccess(details) {
|
|
117045
116976
|
const custom = normalizeCloudRunDomainUrl(details?.BaseInfo?.CustomDomainName);
|
|
117046
116977
|
if (custom)
|
|
@@ -117071,7 +117002,7 @@ function registerCloudRunTools(server) {
|
|
|
117071
117002
|
// Tool 1: Get CloudRun service information (read operations)
|
|
117072
117003
|
server.registerTool("queryCloudRun", {
|
|
117073
117004
|
title: "查询 CloudRun 服务信息",
|
|
117074
|
-
description: "
|
|
117005
|
+
description: "查询云托管服务信息,支持获取服务列表、查询服务详情、获取可用模板列表、获取部署日志以及查询环境云托管开通状态(envStatus)。返回的服务信息包括服务名称、状态、访问类型、配置详情以及最近部署上下文。",
|
|
117075
117006
|
inputSchema: queryCloudRunInputSchema,
|
|
117076
117007
|
annotations: {
|
|
117077
117008
|
readOnlyHint: true,
|
|
@@ -117183,6 +117114,10 @@ function registerCloudRunTools(server) {
|
|
|
117183
117114
|
data: {
|
|
117184
117115
|
service: result,
|
|
117185
117116
|
latestDeploy,
|
|
117117
|
+
// 若最新部署记录带镜像信息(镜像部署),透出便于展示
|
|
117118
|
+
...(extractCloudRunImageInfo(result, latestDeploy)
|
|
117119
|
+
? { imageInfo: extractCloudRunImageInfo(result, latestDeploy) }
|
|
117120
|
+
: {}),
|
|
117186
117121
|
...(deployRecordsWarning ? { deployRecordsWarning } : {})
|
|
117187
117122
|
},
|
|
117188
117123
|
message
|
|
@@ -117281,6 +117216,65 @@ function registerCloudRunTools(server) {
|
|
|
117281
117216
|
]
|
|
117282
117217
|
};
|
|
117283
117218
|
}
|
|
117219
|
+
case 'envStatus': {
|
|
117220
|
+
const envId = input.envId?.trim() || (await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions));
|
|
117221
|
+
if (!manager.commonService) {
|
|
117222
|
+
throw new Error("Current CloudBase Manager does not support commonService; cannot query CloudRun env status.");
|
|
117223
|
+
}
|
|
117224
|
+
let status;
|
|
117225
|
+
try {
|
|
117226
|
+
status = await describeCloudRunEnvStatus(manager, envId);
|
|
117227
|
+
}
|
|
117228
|
+
catch (error) {
|
|
117229
|
+
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
117230
|
+
if (/ResourceNotFound|not.?initialized|未开通|未初始化/i.test(baseMessage) ||
|
|
117231
|
+
/InvalidParameter.*(?:Env|CloudRun)/i.test(baseMessage)) {
|
|
117232
|
+
status = { isExist: false, status: "unopened", baseInfo: {} };
|
|
117233
|
+
}
|
|
117234
|
+
else {
|
|
117235
|
+
throw new Error(`[queryCloudRun/envStatus] ${baseMessage}`);
|
|
117236
|
+
}
|
|
117237
|
+
}
|
|
117238
|
+
let message;
|
|
117239
|
+
if (!status.isExist) {
|
|
117240
|
+
message = `环境 ${envId} 尚未开通云托管。请先调用 manageCloudRun(action="initEnv", envId="${envId}") 开通(异步、幂等),或前往控制台 环境 → 云托管 → 开通;Status=normal 后即可 deploy。`;
|
|
117241
|
+
}
|
|
117242
|
+
else if (status.status === "creating") {
|
|
117243
|
+
message = `环境 ${envId} 云托管正在开通中(Status=creating)。请稍后重试 manageCloudRun(action="deploy"),或用本 action 再次查询直到 Status=normal。`;
|
|
117244
|
+
}
|
|
117245
|
+
else if (status.status === "normal") {
|
|
117246
|
+
message = `环境 ${envId} 云托管已开通(Status=normal),可直接 manageCloudRun(action="deploy")。`;
|
|
117247
|
+
}
|
|
117248
|
+
else {
|
|
117249
|
+
message = `环境 ${envId} 云托管状态未知(Status=${status.status ?? "unknown"}),请稍后重试。`;
|
|
117250
|
+
}
|
|
117251
|
+
return {
|
|
117252
|
+
content: [
|
|
117253
|
+
{
|
|
117254
|
+
type: "text",
|
|
117255
|
+
text: JSON.stringify({
|
|
117256
|
+
success: true,
|
|
117257
|
+
data: {
|
|
117258
|
+
envId,
|
|
117259
|
+
status: status.status,
|
|
117260
|
+
isExist: status.isExist,
|
|
117261
|
+
...(status.isExist
|
|
117262
|
+
? {
|
|
117263
|
+
envBaseInfo: {
|
|
117264
|
+
Status: status.baseInfo.Status ?? null,
|
|
117265
|
+
PackageType: status.baseInfo.PackageType ?? null,
|
|
117266
|
+
Region: status.baseInfo.Region ?? null,
|
|
117267
|
+
EnvType: status.baseInfo.EnvType ?? null,
|
|
117268
|
+
},
|
|
117269
|
+
}
|
|
117270
|
+
: {}),
|
|
117271
|
+
},
|
|
117272
|
+
message
|
|
117273
|
+
}, null, 2)
|
|
117274
|
+
}
|
|
117275
|
+
]
|
|
117276
|
+
};
|
|
117277
|
+
}
|
|
117284
117278
|
default:
|
|
117285
117279
|
throw new Error(`Unsupported action: ${input.action}`);
|
|
117286
117280
|
}
|
|
@@ -117290,7 +117284,7 @@ function registerCloudRunTools(server) {
|
|
|
117290
117284
|
// Tool 2: Manage CloudRun services (write operations)
|
|
117291
117285
|
server.registerTool("manageCloudRun", {
|
|
117292
117286
|
title: "管理 CloudRun 服务",
|
|
117293
|
-
description: "
|
|
117287
|
+
description: "管理云托管服务,按开发顺序支持:开通云托管环境(initEnv)、初始化项目(可从模板开始,模板列表可通过 queryCloudRun 查询)、下载服务代码、本地运行(仅函数型服务)、部署代码、仅更新配置(updateConfig,无需重新上传代码)、删除服务。deploy 支持两种方式:1) 源码构建(传入 targetPath,本地代码打包上传,默认路径);2) 已有镜像部署(传入 imageUrl,如 ccr.ccs.tencentyun.com/ns/img:v1,走 DeployType=image 容器型部署,targetPath 可省略)。deploy 对已存在服务会先读取远程配置再合并(保留 VpcConf/EnvParams/OpenAccessTypes)。updateConfig 对齐控制台服务设置页。删除操作需要确认,建议设置force=true。新环境首次部署前若提示未开通云托管,先调用 initEnv 开通(异步、幂等)。",
|
|
117294
117288
|
inputSchema: ManageCloudRunInputSchema,
|
|
117295
117289
|
annotations: {
|
|
117296
117290
|
readOnlyHint: false,
|
|
@@ -117311,6 +117305,99 @@ function registerCloudRunTools(server) {
|
|
|
117311
117305
|
targetPath = validateAndNormalizePath(input.targetPath);
|
|
117312
117306
|
}
|
|
117313
117307
|
switch (input.action) {
|
|
117308
|
+
case 'initEnv': {
|
|
117309
|
+
const envId = input.envId?.trim() || (await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions));
|
|
117310
|
+
const packageType = input.packageType || 'Trial';
|
|
117311
|
+
if (!manager.commonService) {
|
|
117312
|
+
throw new Error("Current CloudBase Manager does not support commonService; cannot initialize CloudRun env.");
|
|
117313
|
+
}
|
|
117314
|
+
// 幂等:先查当前开通状态,已开通 / 开通中不重复创建。
|
|
117315
|
+
let current;
|
|
117316
|
+
try {
|
|
117317
|
+
current = await describeCloudRunEnvStatus(manager, envId);
|
|
117318
|
+
}
|
|
117319
|
+
catch (error) {
|
|
117320
|
+
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
117321
|
+
if (/ResourceNotFound|not.?initialized|未开通|未初始化/i.test(baseMessage) ||
|
|
117322
|
+
/InvalidParameter.*(?:Env|CloudRun)/i.test(baseMessage)) {
|
|
117323
|
+
current = { isExist: false, status: "unopened", baseInfo: {} };
|
|
117324
|
+
}
|
|
117325
|
+
else {
|
|
117326
|
+
throw new Error(`[manageCloudRun/initEnv] ${baseMessage}`);
|
|
117327
|
+
}
|
|
117328
|
+
}
|
|
117329
|
+
if (current.isExist && current.status === "normal") {
|
|
117330
|
+
return {
|
|
117331
|
+
content: [
|
|
117332
|
+
{
|
|
117333
|
+
type: "text",
|
|
117334
|
+
text: JSON.stringify({
|
|
117335
|
+
success: true,
|
|
117336
|
+
data: {
|
|
117337
|
+
envId,
|
|
117338
|
+
status: "normal",
|
|
117339
|
+
packageType: current.baseInfo.PackageType ?? packageType,
|
|
117340
|
+
created: false
|
|
117341
|
+
},
|
|
117342
|
+
message: `环境 ${envId} 已开通云托管(Status=normal),无需重复开通。可直接 manageCloudRun(action="deploy")。`
|
|
117343
|
+
}, null, 2)
|
|
117344
|
+
}
|
|
117345
|
+
]
|
|
117346
|
+
};
|
|
117347
|
+
}
|
|
117348
|
+
if (current.isExist && current.status === "creating") {
|
|
117349
|
+
return {
|
|
117350
|
+
content: [
|
|
117351
|
+
{
|
|
117352
|
+
type: "text",
|
|
117353
|
+
text: JSON.stringify({
|
|
117354
|
+
success: true,
|
|
117355
|
+
data: {
|
|
117356
|
+
envId,
|
|
117357
|
+
status: "creating",
|
|
117358
|
+
created: false
|
|
117359
|
+
},
|
|
117360
|
+
message: `环境 ${envId} 云托管正在开通中(Status=creating),无需重复开通。请稍后用 queryCloudRun(action="envStatus", envId="${envId}") 查询,Status=normal 后即可 deploy。`
|
|
117361
|
+
}, null, 2)
|
|
117362
|
+
}
|
|
117363
|
+
]
|
|
117364
|
+
};
|
|
117365
|
+
}
|
|
117366
|
+
// 未开通 → 发起异步开通(CreateCloudRunEnv 异步,不阻塞等待)。
|
|
117367
|
+
let createResult;
|
|
117368
|
+
try {
|
|
117369
|
+
createResult = await manager
|
|
117370
|
+
.commonService("tcbr", "2022-02-17")
|
|
117371
|
+
.call({
|
|
117372
|
+
Action: "CreateCloudRunEnv",
|
|
117373
|
+
Param: { EnvId: envId, PackageType: packageType },
|
|
117374
|
+
});
|
|
117375
|
+
}
|
|
117376
|
+
catch (error) {
|
|
117377
|
+
throw new Error(buildManageCloudRunErrorMessage('initEnv', envId, error));
|
|
117378
|
+
}
|
|
117379
|
+
const tranId = createResult?.TranId ??
|
|
117380
|
+
createResult?.Response?.TranId ??
|
|
117381
|
+
createResult?.tranId;
|
|
117382
|
+
return {
|
|
117383
|
+
content: [
|
|
117384
|
+
{
|
|
117385
|
+
type: "text",
|
|
117386
|
+
text: JSON.stringify({
|
|
117387
|
+
success: true,
|
|
117388
|
+
data: {
|
|
117389
|
+
envId,
|
|
117390
|
+
status: "creating",
|
|
117391
|
+
packageType,
|
|
117392
|
+
created: true,
|
|
117393
|
+
...(tranId ? { tranId } : {})
|
|
117394
|
+
},
|
|
117395
|
+
message: `已发起云托管开通(异步,Status=creating)。请稍后用 queryCloudRun(action="envStatus", envId="${envId}") 查询状态,Status=normal 后即可 manageCloudRun(action="deploy")。`
|
|
117396
|
+
}, null, 2)
|
|
117397
|
+
}
|
|
117398
|
+
]
|
|
117399
|
+
};
|
|
117400
|
+
}
|
|
117314
117401
|
case 'createAgent': {
|
|
117315
117402
|
if (!targetPath) {
|
|
117316
117403
|
throw new Error("targetPath is required for createAgent operation");
|
|
@@ -117476,14 +117563,18 @@ for await (let x of res.textStream) {
|
|
|
117476
117563
|
};
|
|
117477
117564
|
}
|
|
117478
117565
|
case 'deploy': {
|
|
117479
|
-
if (!targetPath) {
|
|
117480
|
-
throw new Error("targetPath is required for deploy operation");
|
|
117566
|
+
if (!targetPath && !input.imageUrl) {
|
|
117567
|
+
throw new Error("targetPath (source build) or imageUrl (existing image) is required for deploy operation");
|
|
117481
117568
|
}
|
|
117482
117569
|
// Determine service type - use input.serverType if provided, otherwise auto-detect
|
|
117483
117570
|
let serverType;
|
|
117484
117571
|
let remoteServerConfig = null;
|
|
117485
117572
|
let existingService = false;
|
|
117486
|
-
if (input.
|
|
117573
|
+
if (input.imageUrl) {
|
|
117574
|
+
// Image deploy is always container type (SDK: DeployInfo={DeployType:"image", ImageUrl}).
|
|
117575
|
+
serverType = 'container';
|
|
117576
|
+
}
|
|
117577
|
+
else if (input.serverType) {
|
|
117487
117578
|
serverType = input.serverType;
|
|
117488
117579
|
}
|
|
117489
117580
|
else {
|
|
@@ -117537,6 +117628,17 @@ for await (let x of res.textStream) {
|
|
|
117537
117628
|
// New service create path
|
|
117538
117629
|
}
|
|
117539
117630
|
}
|
|
117631
|
+
// 新环境必须先初始化云托管(大租户),否则 CreateCloudRunServer 会默认
|
|
117632
|
+
// 创建小租户服务。仅创建新服务(非 existing)前探测一次。
|
|
117633
|
+
// (2026-08-13 用户实测:luapi-v2 因未初始化云托管被创建到小租户)
|
|
117634
|
+
if (!existingService) {
|
|
117635
|
+
const currentEnvId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
117636
|
+
await ensureCloudRunEnvInitialized({
|
|
117637
|
+
cloudBaseOptions,
|
|
117638
|
+
envId: currentEnvId,
|
|
117639
|
+
serverName: input.serverName,
|
|
117640
|
+
});
|
|
117641
|
+
}
|
|
117540
117642
|
let mergedFromRemote = [];
|
|
117541
117643
|
let effectiveServerConfig = input.serverConfig;
|
|
117542
117644
|
if (existingService) {
|
|
@@ -117554,6 +117656,9 @@ for await (let x of res.textStream) {
|
|
|
117554
117656
|
force: input.force,
|
|
117555
117657
|
serverType: serverType,
|
|
117556
117658
|
};
|
|
117659
|
+
if (input.imageUrl) {
|
|
117660
|
+
deployParams.imageUrl = input.imageUrl;
|
|
117661
|
+
}
|
|
117557
117662
|
if (effectiveServerConfig && Object.keys(effectiveServerConfig).length > 0) {
|
|
117558
117663
|
deployParams.serverConfig = effectiveServerConfig;
|
|
117559
117664
|
}
|
|
@@ -117574,22 +117679,26 @@ for await (let x of res.textStream) {
|
|
|
117574
117679
|
catch (error) {
|
|
117575
117680
|
throw new Error(buildManageCloudRunErrorMessage('deploy', input.serverName, error));
|
|
117576
117681
|
}
|
|
117577
|
-
// Generate cloudbaserc.json configuration file
|
|
117682
|
+
// Generate cloudbaserc.json configuration file (source-build only; image deploy has no local project dir)
|
|
117578
117683
|
const currentEnvId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
117579
|
-
|
|
117580
|
-
|
|
117581
|
-
|
|
117582
|
-
|
|
117583
|
-
|
|
117684
|
+
let cloudbasercGenerated = false;
|
|
117685
|
+
if (targetPath) {
|
|
117686
|
+
const cloudbasercPath = path_1.default.join(targetPath, 'cloudbaserc.json');
|
|
117687
|
+
const cloudbasercContent = {
|
|
117688
|
+
envId: currentEnvId,
|
|
117689
|
+
cloudrun: {
|
|
117690
|
+
name: input.serverName
|
|
117691
|
+
}
|
|
117692
|
+
};
|
|
117693
|
+
try {
|
|
117694
|
+
fs_1.default.writeFileSync(cloudbasercPath, JSON.stringify(cloudbasercContent, null, 2));
|
|
117695
|
+
cloudbasercGenerated = true;
|
|
117696
|
+
}
|
|
117697
|
+
catch (error) {
|
|
117698
|
+
(0, logger_js_1.debug)('cloudbaserc.json creation skipped:', error instanceof Error ? error : new Error(String(error)));
|
|
117584
117699
|
}
|
|
117585
|
-
};
|
|
117586
|
-
const consoleUrl = `https://tcb.cloud.tencent.com/dev?envId=${currentEnvId}#/platform-run/service/detail?serverName=${input.serverName}&tabId=overview&envId=${currentEnvId}`;
|
|
117587
|
-
try {
|
|
117588
|
-
fs_1.default.writeFileSync(cloudbasercPath, JSON.stringify(cloudbasercContent, null, 2));
|
|
117589
|
-
}
|
|
117590
|
-
catch (error) {
|
|
117591
|
-
(0, logger_js_1.debug)('cloudbaserc.json creation skipped:', error instanceof Error ? error : new Error(String(error)));
|
|
117592
117700
|
}
|
|
117701
|
+
const consoleUrl = `https://tcb.cloud.tencent.com/dev?envId=${currentEnvId}#/platform-run/service/detail?serverName=${input.serverName}&tabId=overview&envId=${currentEnvId}`;
|
|
117593
117702
|
let preferredAccessUrl;
|
|
117594
117703
|
let preferredAccessUrls = [];
|
|
117595
117704
|
let preferredAccessSource;
|
|
@@ -117626,7 +117735,7 @@ for await (let x of res.textStream) {
|
|
|
117626
117735
|
}
|
|
117627
117736
|
// Send deployment notification to CodeBuddy IDE
|
|
117628
117737
|
try {
|
|
117629
|
-
const projectName = path_1.default.basename(targetPath);
|
|
117738
|
+
const projectName = targetPath ? path_1.default.basename(targetPath) : input.serverName;
|
|
117630
117739
|
await (0, notification_js_1.sendDeployNotification)(server, {
|
|
117631
117740
|
deployType: 'cloudrun',
|
|
117632
117741
|
url: preferredAccessUrl ?? "",
|
|
@@ -117656,9 +117765,11 @@ for await (let x of res.textStream) {
|
|
|
117656
117765
|
data: {
|
|
117657
117766
|
serviceName: input.serverName,
|
|
117658
117767
|
status: 'deploying',
|
|
117659
|
-
|
|
117768
|
+
deployType: input.imageUrl ? 'image' : 'source',
|
|
117769
|
+
...(targetPath ? { deployPath: targetPath } : {}),
|
|
117770
|
+
...(input.imageUrl ? { imageUrl: input.imageUrl } : {}),
|
|
117660
117771
|
serverType: serverType,
|
|
117661
|
-
cloudbasercGenerated
|
|
117772
|
+
cloudbasercGenerated,
|
|
117662
117773
|
consoleUrl,
|
|
117663
117774
|
...(existingService
|
|
117664
117775
|
? {
|
|
@@ -117681,7 +117792,7 @@ for await (let x of res.textStream) {
|
|
|
117681
117792
|
: {}),
|
|
117682
117793
|
...(warnings.length > 0 ? { warnings } : {}),
|
|
117683
117794
|
},
|
|
117684
|
-
message: `Triggered deployment for ${serverType} service '${input.serverName}' from ${targetPath}. You can follow the progress in ${consoleUrl}.${warningSuffix}`
|
|
117795
|
+
message: `Triggered deployment for ${serverType} service '${input.serverName}' ${input.imageUrl ? `from image ${input.imageUrl}` : `from ${targetPath}`}. You can follow the progress in ${consoleUrl}.${warningSuffix}`
|
|
117685
117796
|
}, null, 2)
|
|
117686
117797
|
}
|
|
117687
117798
|
]
|
|
@@ -121916,7 +122027,8 @@ function registerAppTools(server) {
|
|
|
121916
122027
|
.optional()
|
|
121917
122028
|
.describe("自定义部署命令。通常无需填写,默认自动生成 tcb hosting deploy 命令。" +
|
|
121918
122029
|
"有 buildPath 时远端已 cd 到该目录,默认用 . 作为源码路径;无 buildPath 时默认用 dist。"),
|
|
121919
|
-
ignore: zod_1.z.array(zod_1.z.string()).optional().describe("上传时忽略的文件/目录 glob 模式,例如 **/node_modules
|
|
122030
|
+
ignore: zod_1.z.array(zod_1.z.string()).optional().describe("上传时忽略的文件/目录 glob 模式,例如 **/node_modules/**。\n" +
|
|
122031
|
+
"⚠️ 打包的是项目根目录(filePath)而非 buildPath 产物目录:若项目根含 target/(Rust)、.next/、dist-old/、build/ 等大构建产物,必须加进 ignore(如 **/target/**),否则整个目录被打进上传 zip(实证 54GB target → 34GB zip)。默认已排除 node_modules/.git/.DS_Store/**/target/**/.next/**/.next.bak/**。"),
|
|
121920
122032
|
versionName: zod_1.z
|
|
121921
122033
|
.string()
|
|
121922
122034
|
.optional()
|
|
@@ -121936,6 +122048,17 @@ function registerAppTools(server) {
|
|
|
121936
122048
|
if (!appService) {
|
|
121937
122049
|
throw new Error("当前 manager 未提供 cloudAppService");
|
|
121938
122050
|
}
|
|
122051
|
+
// 默认排除的大目录(2026-08-14 实证:ato 项目 target/ 54GB 被整个打进 zip)
|
|
122052
|
+
// tcb app deploy 打包的是项目根目录(localPath)而非 outputDir,必须排除构建产物
|
|
122053
|
+
const defaultPackIgnore = [
|
|
122054
|
+
"node_modules/**",
|
|
122055
|
+
".git/**",
|
|
122056
|
+
".DS_Store",
|
|
122057
|
+
"**/.DS_Store",
|
|
122058
|
+
"**/target/**",
|
|
122059
|
+
"**/.next/**",
|
|
122060
|
+
"**/.next.bak/**",
|
|
122061
|
+
];
|
|
121939
122062
|
// getUploadUrl — 获取预签名上传 URL(cloud mode 专用)
|
|
121940
122063
|
if (action === "getUploadUrl") {
|
|
121941
122064
|
if (!serviceName) {
|
|
@@ -121946,9 +122069,9 @@ function registerAppTools(server) {
|
|
|
121946
122069
|
serviceName,
|
|
121947
122070
|
});
|
|
121948
122071
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, cosInfoResult);
|
|
121949
|
-
const defaultIgnore =
|
|
122072
|
+
const defaultIgnore = defaultPackIgnore;
|
|
121950
122073
|
// eslint-disable-next-line max-len
|
|
121951
|
-
const zipCmd = "zip -r upload.zip . -x 'node_modules/**' -x '.git/**' -x '.DS_Store' -x '**/.DS_Store'";
|
|
122074
|
+
const zipCmd = "zip -r upload.zip . -x 'node_modules/**' -x '.git/**' -x '.DS_Store' -x '**/.DS_Store' -x '**/target/**' -x '**/.next/**' -x '**/.next.bak/**'";
|
|
121952
122075
|
const followupArgs = {
|
|
121953
122076
|
action: "deployApp",
|
|
121954
122077
|
serviceName,
|
|
@@ -121992,11 +122115,17 @@ function registerAppTools(server) {
|
|
|
121992
122115
|
// 上传代码到 COS(仅本地模式需要,cloud mode 用 cosTimestamp 跳过)
|
|
121993
122116
|
let cosTs = cosTimestamp;
|
|
121994
122117
|
if (filePath && !cosTs) {
|
|
122118
|
+
// 默认排除大目录(2026-08-14 实证:ato 项目 target/ 54GB 被整个打进 zip)
|
|
122119
|
+
// 用户显式传 ignore 时合并,避免覆盖默认值
|
|
122120
|
+
const mergedIgnore = Array.from(new Set([
|
|
122121
|
+
...defaultPackIgnore,
|
|
122122
|
+
...(ignore ?? []),
|
|
122123
|
+
]));
|
|
121995
122124
|
const uploadResult = await appService.uploadCode({
|
|
121996
122125
|
deployType: "static-hosting",
|
|
121997
122126
|
serviceName,
|
|
121998
122127
|
localPath: filePath,
|
|
121999
|
-
ignore,
|
|
122128
|
+
ignore: mergedIgnore,
|
|
122000
122129
|
});
|
|
122001
122130
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, uploadResult);
|
|
122002
122131
|
cosTs = uploadResult.cosTimestamp;
|
|
@@ -123609,16 +123738,19 @@ function isDirectoryUploadTarget(localPath, cloudPath) {
|
|
|
123609
123738
|
return true;
|
|
123610
123739
|
return path_1.default.posix.extname(normalizedCloudPath) === '';
|
|
123611
123740
|
}
|
|
123612
|
-
function
|
|
123613
|
-
if (!staticDomain)
|
|
123614
|
-
return '';
|
|
123741
|
+
function hostingAccessPathname(cloudPath, localPath) {
|
|
123615
123742
|
const normalizedCloudPath = (cloudPath ?? '').trim().replace(/^\/+|\/+$/g, '');
|
|
123616
123743
|
const isDirectory = isDirectoryUploadTarget(localPath, cloudPath);
|
|
123617
123744
|
if (!normalizedCloudPath) {
|
|
123618
|
-
return
|
|
123745
|
+
return '/';
|
|
123619
123746
|
}
|
|
123620
123747
|
const pathname = isDirectory ? `${normalizedCloudPath}/` : normalizedCloudPath;
|
|
123621
|
-
return
|
|
123748
|
+
return `/${pathname}`;
|
|
123749
|
+
}
|
|
123750
|
+
function buildHostingAccessUrl(staticDomain, cloudPath, localPath) {
|
|
123751
|
+
if (!staticDomain)
|
|
123752
|
+
return '';
|
|
123753
|
+
return `https://${staticDomain}${hostingAccessPathname(cloudPath, localPath)}`;
|
|
123622
123754
|
}
|
|
123623
123755
|
function buildUploadErrorMessage(error, localPath) {
|
|
123624
123756
|
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -123799,7 +123931,7 @@ function buildDomainMutationResult(params) {
|
|
|
123799
123931
|
message: `静态托管域名${actionLabel}请求已提交。域名配置、证书校验和边缘侧传播通常需要 30 秒到 10 分钟,请继续调用 queryHosting(action="domainStatus") 确认最终结果。`,
|
|
123800
123932
|
};
|
|
123801
123933
|
}
|
|
123802
|
-
async function getHostingWebsiteConfig(cloudbase, logger) {
|
|
123934
|
+
async function getHostingWebsiteConfig(cloudbase, logger, cloudBaseOptions) {
|
|
123803
123935
|
const websiteConfig = await cloudbase.hosting.getWebsiteConfig();
|
|
123804
123936
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(logger, websiteConfig);
|
|
123805
123937
|
const hostingResult = {
|
|
@@ -123816,6 +123948,27 @@ async function getHostingWebsiteConfig(cloudbase, logger) {
|
|
|
123816
123948
|
catch {
|
|
123817
123949
|
// Ignore enrichment failures and return the website config as-is.
|
|
123818
123950
|
}
|
|
123951
|
+
try {
|
|
123952
|
+
const cdnDomain = typeof hostingResult.CdnDomain === 'string' ? hostingResult.CdnDomain : '';
|
|
123953
|
+
if (cdnDomain && typeof cloudbase.env?.describeHttpServiceRoute === 'function') {
|
|
123954
|
+
const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
123955
|
+
const routeResult = await cloudbase.env.describeHttpServiceRoute({
|
|
123956
|
+
EnvId: envId,
|
|
123957
|
+
Limit: 1000,
|
|
123958
|
+
});
|
|
123959
|
+
const routes = (0, gateway_access_urls_js_1.flattenHttpServiceRoutes)(routeResult);
|
|
123960
|
+
const reachable = (0, gateway_access_urls_js_1.isDomainPathReachableViaGateway)(routes, cdnDomain, '/');
|
|
123961
|
+
hostingResult.staticDomainRouteEnabled = reachable;
|
|
123962
|
+
hostingResult.accessUrlReachable = reachable !== false;
|
|
123963
|
+
if (reachable === false) {
|
|
123964
|
+
hostingResult.routeDisabled = true;
|
|
123965
|
+
hostingResult.disabledAccessUrls = [`https://${cdnDomain}/`];
|
|
123966
|
+
}
|
|
123967
|
+
}
|
|
123968
|
+
}
|
|
123969
|
+
catch {
|
|
123970
|
+
// Gateway route lookup is best-effort enrichment only.
|
|
123971
|
+
}
|
|
123819
123972
|
return hostingResult;
|
|
123820
123973
|
}
|
|
123821
123974
|
async function resolveHostingStaticDomain(cloudbase, logger) {
|
|
@@ -123899,7 +124052,7 @@ function registerHostingTools(server) {
|
|
|
123899
124052
|
const cloudbase = await getManager();
|
|
123900
124053
|
switch (input.action) {
|
|
123901
124054
|
case 'websiteConfig': {
|
|
123902
|
-
const websiteConfig = await getHostingWebsiteConfig(cloudbase, server.logger);
|
|
124055
|
+
const websiteConfig = await getHostingWebsiteConfig(cloudbase, server.logger, cloudBaseOptions);
|
|
123903
124056
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
123904
124057
|
success: true,
|
|
123905
124058
|
data: {
|
|
@@ -124025,7 +124178,7 @@ function registerHostingTools(server) {
|
|
|
124025
124178
|
});
|
|
124026
124179
|
server.registerTool('manageHosting', {
|
|
124027
124180
|
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。',
|
|
124181
|
+
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
124182
|
inputSchema: manageHostingInputSchema,
|
|
124030
124183
|
annotations: {
|
|
124031
124184
|
readOnlyHint: false,
|
|
@@ -124065,7 +124218,24 @@ function registerHostingTools(server) {
|
|
|
124065
124218
|
const cdnFromStore = (store.CdnDomain ?? store.StaticDomain);
|
|
124066
124219
|
const staticDomain = cdnFromStore || await resolveHostingStaticDomain(cloudbase, server.logger);
|
|
124067
124220
|
const fallbackAccessUrl = buildHostingAccessUrl(staticDomain, input.cloudPath, input.localPath);
|
|
124221
|
+
const accessPathname = hostingAccessPathname(input.cloudPath, input.localPath);
|
|
124068
124222
|
const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
|
|
124223
|
+
const getGatewayManager = async () => {
|
|
124224
|
+
const manager = await getManager();
|
|
124225
|
+
if (!manager) {
|
|
124226
|
+
throw new Error("cloudbase manager unavailable");
|
|
124227
|
+
}
|
|
124228
|
+
return manager;
|
|
124229
|
+
};
|
|
124230
|
+
const allGatewayRoutes = await (0, gateway_access_urls_js_1.resolveAllGatewayRoutes)({
|
|
124231
|
+
envId,
|
|
124232
|
+
getManager: getGatewayManager,
|
|
124233
|
+
});
|
|
124234
|
+
const staticDomainRouteEnabled = staticDomain
|
|
124235
|
+
? (0, gateway_access_urls_js_1.isDomainPathReachableViaGateway)(allGatewayRoutes, staticDomain, accessPathname)
|
|
124236
|
+
: null;
|
|
124237
|
+
// null = no matching gateway route info → keep legacy fallback.
|
|
124238
|
+
const fallbackReachable = staticDomainRouteEnabled !== false;
|
|
124069
124239
|
const gatewayCandidates = Array.from(new Set([
|
|
124070
124240
|
store.StaticStoreName,
|
|
124071
124241
|
store.Name,
|
|
@@ -124075,34 +124245,38 @@ function registerHostingTools(server) {
|
|
|
124075
124245
|
]
|
|
124076
124246
|
.map((item) => (typeof item === "string" ? item.trim() : ""))
|
|
124077
124247
|
.filter(Boolean)));
|
|
124078
|
-
let accessUrl = fallbackAccessUrl;
|
|
124248
|
+
let accessUrl = fallbackReachable ? fallbackAccessUrl : "";
|
|
124079
124249
|
let accessUrls = accessUrl ? [accessUrl] : [];
|
|
124080
124250
|
let accessUrlSource = accessUrl
|
|
124081
124251
|
? "hosting.staticDomain"
|
|
124082
124252
|
: undefined;
|
|
124253
|
+
let accessUrlReachable = Boolean(accessUrl);
|
|
124254
|
+
let disabledAccessUrls = [];
|
|
124255
|
+
if (fallbackAccessUrl && !fallbackReachable) {
|
|
124256
|
+
disabledAccessUrls.push(fallbackAccessUrl);
|
|
124257
|
+
}
|
|
124083
124258
|
for (const upstreamName of gatewayCandidates) {
|
|
124084
124259
|
const gateway = await (0, gateway_access_urls_js_1.resolveGatewayAccessUrls)({
|
|
124085
124260
|
envId,
|
|
124086
124261
|
upstreamResourceName: upstreamName,
|
|
124087
124262
|
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
|
-
},
|
|
124263
|
+
getManager: getGatewayManager,
|
|
124095
124264
|
});
|
|
124096
124265
|
const preferred = (0, gateway_access_urls_js_1.preferGatewayOrFallback)({
|
|
124097
124266
|
gateway,
|
|
124098
124267
|
fallbackUrl: fallbackAccessUrl || undefined,
|
|
124099
124268
|
fallbackSource: "hosting.staticDomain",
|
|
124269
|
+
fallbackReachable,
|
|
124100
124270
|
});
|
|
124101
|
-
if (preferred.accessUrl) {
|
|
124102
|
-
accessUrl = preferred.accessUrl;
|
|
124271
|
+
if (preferred.accessUrl || preferred.disabledAccessUrls.length > 0) {
|
|
124272
|
+
accessUrl = preferred.accessUrl ?? "";
|
|
124103
124273
|
accessUrls = preferred.accessUrls;
|
|
124104
124274
|
accessUrlSource = preferred.accessUrlSource;
|
|
124105
|
-
|
|
124275
|
+
accessUrlReachable = preferred.accessUrlReachable;
|
|
124276
|
+
disabledAccessUrls = preferred.disabledAccessUrls;
|
|
124277
|
+
if (preferred.accessUrl) {
|
|
124278
|
+
break;
|
|
124279
|
+
}
|
|
124106
124280
|
}
|
|
124107
124281
|
}
|
|
124108
124282
|
try {
|
|
@@ -124118,18 +124292,31 @@ function registerHostingTools(server) {
|
|
|
124118
124292
|
projectName = path_1.default.basename(input.localPath);
|
|
124119
124293
|
}
|
|
124120
124294
|
}
|
|
124121
|
-
|
|
124122
|
-
|
|
124123
|
-
|
|
124124
|
-
|
|
124125
|
-
|
|
124126
|
-
|
|
124127
|
-
|
|
124295
|
+
// sendDeployNotification requires a concrete url; skip when no reachable accessUrl
|
|
124296
|
+
if (accessUrl) {
|
|
124297
|
+
await (0, notification_js_1.sendDeployNotification)(server, {
|
|
124298
|
+
deployType: 'hosting',
|
|
124299
|
+
url: accessUrl,
|
|
124300
|
+
projectId: envId,
|
|
124301
|
+
projectName,
|
|
124302
|
+
consoleUrl: `https://tcb.cloud.tencent.com/dev?envId=${envId}#/static-hosting`,
|
|
124303
|
+
});
|
|
124304
|
+
}
|
|
124128
124305
|
}
|
|
124129
124306
|
catch {
|
|
124130
124307
|
// Notification failure should not block uploads.
|
|
124131
124308
|
}
|
|
124132
124309
|
const uploadPrefix = input.cloudPath ?? input.files?.[0]?.cloudPath ?? '';
|
|
124310
|
+
const routeDisabled = staticDomainRouteEnabled === false;
|
|
124311
|
+
let message = '静态托管文件上传成功。若需要校验上传结果,请继续调用 queryHosting(action="findFiles") 或 queryHosting(action="listFiles")。';
|
|
124312
|
+
if (routeDisabled && accessUrlReachable) {
|
|
124313
|
+
message =
|
|
124314
|
+
'静态托管文件上传成功。默认静态托管域名的网关路由已禁用(访问会返回 GATEWAY_ROUTE_DISABLED),已改用其他可达域名作为 accessUrl。可用 manageGateway(action="updateRoute", route.enable=true) 重新启用默认域路由。';
|
|
124315
|
+
}
|
|
124316
|
+
else if (routeDisabled && !accessUrlReachable) {
|
|
124317
|
+
message =
|
|
124318
|
+
'静态托管文件上传成功,但默认静态托管域名的网关路由已禁用(GATEWAY_ROUTE_DISABLED),当前没有可达的 accessUrl。请用 manageGateway(action="updateRoute", domain="<静态域名>", path="/", upstreamResourceType="STATIC_STORE", targetName="staticstore", route.enable=true) 启用路由,或改用已启用的自定义域名 / CloudBase Sites 域名访问。';
|
|
124319
|
+
}
|
|
124133
124320
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
124134
124321
|
success: true,
|
|
124135
124322
|
data: {
|
|
@@ -124139,13 +124326,19 @@ function registerHostingTools(server) {
|
|
|
124139
124326
|
files: input.files ?? [],
|
|
124140
124327
|
ignore: input.ignore,
|
|
124141
124328
|
staticDomain,
|
|
124142
|
-
|
|
124329
|
+
staticDomainRouteEnabled,
|
|
124330
|
+
accessUrl: accessUrl || undefined,
|
|
124143
124331
|
accessUrls,
|
|
124144
124332
|
accessUrlSource,
|
|
124333
|
+
accessUrlReachable,
|
|
124334
|
+
...(disabledAccessUrls.length > 0
|
|
124335
|
+
? { disabledAccessUrls }
|
|
124336
|
+
: {}),
|
|
124337
|
+
...(routeDisabled ? { routeDisabled: true } : {}),
|
|
124145
124338
|
result,
|
|
124146
124339
|
nextActions: uploadPrefix ? [buildFindFilesNextStep(uploadPrefix.replace(/^\/+/, ''))] : undefined,
|
|
124147
124340
|
},
|
|
124148
|
-
message
|
|
124341
|
+
message,
|
|
124149
124342
|
});
|
|
124150
124343
|
}
|
|
124151
124344
|
case 'delete': {
|
|
@@ -131266,6 +131459,7 @@ module.exports = setup;
|
|
|
131266
131459
|
"use strict";
|
|
131267
131460
|
|
|
131268
131461
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
131462
|
+
exports.assertTcbCloudRunActionAllowed = assertTcbCloudRunActionAllowed;
|
|
131269
131463
|
exports.removeEmptyStringParams = removeEmptyStringParams;
|
|
131270
131464
|
exports.buildCapiErrorMessage = buildCapiErrorMessage;
|
|
131271
131465
|
exports.registerCapiTools = registerCapiTools;
|
|
@@ -131277,6 +131471,7 @@ const CLOUDBASE_CONTROL_PLANE_DOC_URL = "https://cloud.tencent.com/document/prod
|
|
|
131277
131471
|
const CLOUDBASE_DEPENDENCY_API_DOC_URL = "https://cloud.tencent.com/document/product/876/34808";
|
|
131278
131472
|
const ALLOWED_SERVICES = [
|
|
131279
131473
|
"tcb",
|
|
131474
|
+
"tcbr",
|
|
131280
131475
|
"scf",
|
|
131281
131476
|
"sts",
|
|
131282
131477
|
"cam",
|
|
@@ -131284,6 +131479,31 @@ const ALLOWED_SERVICES = [
|
|
|
131284
131479
|
"cdn",
|
|
131285
131480
|
"vpc",
|
|
131286
131481
|
];
|
|
131482
|
+
/**
|
|
131483
|
+
* tcb 旧版小租户云托管(CloudBase Run)API 族,统一禁用。
|
|
131484
|
+
*
|
|
131485
|
+
* 云托管一律走 tcbr 新逻辑(CreateCloudRunEnv / CreateCloudRunServer)。tcb 的
|
|
131486
|
+
* CreateCloudBaseRunResource 是 2018 旧小租户开通接口:在无大租户记录的环境上调用
|
|
131487
|
+
* 会把服务创建到小租户,产生错误的服务与版本(2026-08-13 用户实测,ATO 调研
|
|
131488
|
+
* efec7cc5 结论)。同族 Describe/Delete 一并拦截,避免 AI 误以为与 tcbr 环境相关。
|
|
131489
|
+
*/
|
|
131490
|
+
const TCB_CLOUDRUN_FORBIDDEN_ACTIONS = [
|
|
131491
|
+
"CreateCloudBaseRunResource",
|
|
131492
|
+
"DescribeCloudBaseRunResource",
|
|
131493
|
+
"DeleteCloudBaseRunResource",
|
|
131494
|
+
];
|
|
131495
|
+
const TCB_CLOUDRUN_FORBIDDEN_HINT = `云托管(CloudBase Run)统一走 tcbr 新逻辑(CreateCloudRunEnv / CreateCloudRunServer),` +
|
|
131496
|
+
`不要使用 tcb 旧小租户接口(${TCB_CLOUDRUN_FORBIDDEN_ACTIONS.join(" / ")})。` +
|
|
131497
|
+
`新环境请先初始化云托管:callCloudApi(service="tcbr", version="2022-02-17", action="CreateCloudRunEnv", params={EnvId:"..."}),` +
|
|
131498
|
+
`再通过 manageCloudRun(action="deploy") 创建服务;查询单个环境基础信息/是否已开通云托管用 callCloudApi(service="tcbr", version="2022-02-17", action="DescribeEnvBaseInfo", params={EnvId:"..."}),查询环境列表/资源信息用 DescribeCloudRunEnvs。`;
|
|
131499
|
+
/**
|
|
131500
|
+
* 拒绝 tcb 旧版小租户云托管 API。命中黑名单时抛出带 tcbr 引导的错误。
|
|
131501
|
+
*/
|
|
131502
|
+
function assertTcbCloudRunActionAllowed(service, action) {
|
|
131503
|
+
if (service === "tcb" && TCB_CLOUDRUN_FORBIDDEN_ACTIONS.includes(action)) {
|
|
131504
|
+
throw new Error(`[${service}/${action}] 已禁用:${TCB_CLOUDRUN_FORBIDDEN_HINT}`);
|
|
131505
|
+
}
|
|
131506
|
+
}
|
|
131287
131507
|
function levenshteinDistance(left, right) {
|
|
131288
131508
|
const rows = left.length + 1;
|
|
131289
131509
|
const cols = right.length + 1;
|
|
@@ -131359,7 +131579,7 @@ function formatTcbParamsTypeHint(action) {
|
|
|
131359
131579
|
return `参数类型参考:\n\`\`\`ts\n${entry.paramsType}\n\`\`\``;
|
|
131360
131580
|
}
|
|
131361
131581
|
function buildCapiDocGuidance(service) {
|
|
131362
|
-
if (service === "tcb" || service === "lowcode" || service === "scf") {
|
|
131582
|
+
if (service === "tcb" || service === "tcbr" || service === "lowcode" || service === "scf") {
|
|
131363
131583
|
return `优先查阅 CloudBase API 概览 ${CLOUDBASE_CONTROL_PLANE_DOC_URL} 与云开发依赖资源接口指引 ${CLOUDBASE_DEPENDENCY_API_DOC_URL}。`;
|
|
131364
131584
|
}
|
|
131365
131585
|
return `请优先核对对应官方云 API 文档;若你的场景其实是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode="openapi"),不要继续猜测管控面 Action。`;
|
|
@@ -131449,15 +131669,21 @@ function registerCapiTools(server) {
|
|
|
131449
131669
|
**云函数**: \`DescribeFunctions\`、\`CreateFunction\`、\`UpdateFunctionCode\`、\`DeleteFunction\`
|
|
131450
131670
|
**数据库**: \`CreateMySQLInstance\`、\`DescribeMySQLInstances\`、\`DestroyMySQLInstance\`
|
|
131451
131671
|
|
|
131672
|
+
⚠️ 云托管(CloudBase Run)统一走 tcbr service(CreateCloudRunEnv / CreateCloudRunServer / DescribeEnvBaseInfo / DescribeCloudRunEnvs,version="2022-02-17"),tcb 旧小租户接口 CreateCloudBaseRunResource 等已被禁用;部署请用 manageCloudRun。查询单个环境基础信息/是否已开通云托管用 DescribeEnvBaseInfo(EnvId 必填),查询环境列表及资源信息用 DescribeCloudRunEnvs(EnvId 可选过滤)。
|
|
131673
|
+
|
|
131452
131674
|
销毁环境时,常见做法是至少带上 \`EnvId\` 和 \`BypassCheck: true\`,如果环境已经处于隔离期再按文档补 \`IsForce: true\`。`,
|
|
131453
131675
|
inputSchema: {
|
|
131454
131676
|
service: zod_1.z
|
|
131455
131677
|
.enum(ALLOWED_SERVICES)
|
|
131456
|
-
.describe("选择要访问的服务。可选:tcb、scf、sts、cam、lowcode、cdn、vpc。对于 tcb / scf / lowcode 等 CloudBase 管控面 Action,请优先查官方文档,不要直接猜测 Action
|
|
131678
|
+
.describe("选择要访问的服务。可选:tcb、tcbr、scf、sts、cam、lowcode、cdn、vpc。对于 tcb / scf / lowcode 等 CloudBase 管控面 Action,请优先查官方文档,不要直接猜测 Action。云托管统一走 tcbr(version 需传 2022-02-17)。"),
|
|
131457
131679
|
action: zod_1.z
|
|
131458
131680
|
.string()
|
|
131459
131681
|
.min(1)
|
|
131460
|
-
.describe("具体 Action 名称,需符合对应服务的官方 API 定义。若不确定正确 Action,请先查官方文档;不要用近义词或历史命名进行猜测。tcb 常用 Action:环境管理 CreateEnv/ModifyEnv/DescribeEnvs/DestroyEnv、用户管理 CreateUser/ModifyUser/DescribeUserList/DeleteUsers、认证配置 EditAuthConfig、云函数 DescribeFunctions/CreateFunction、数据库 CreateMySQLInstance 等。"),
|
|
131682
|
+
.describe("具体 Action 名称,需符合对应服务的官方 API 定义。若不确定正确 Action,请先查官方文档;不要用近义词或历史命名进行猜测。tcb 常用 Action:环境管理 CreateEnv/ModifyEnv/DescribeEnvs/DestroyEnv、用户管理 CreateUser/ModifyUser/DescribeUserList/DeleteUsers、认证配置 EditAuthConfig、云函数 DescribeFunctions/CreateFunction、数据库 CreateMySQLInstance 等。tcbr 常用 Action:CreateCloudRunEnv(初始化云托管)、DescribeEnvBaseInfo(查询单个环境基础信息,EnvId 必填)、DescribeCloudRunEnvs(查询环境列表/资源信息,EnvId 可选过滤)、CreateCloudRunServer/DescribeCloudRunServers。"),
|
|
131683
|
+
version: zod_1.z
|
|
131684
|
+
.string()
|
|
131685
|
+
.optional()
|
|
131686
|
+
.describe("API 版本(可选)。缺省时按 service 使用 SDK 内置默认版本;tcbr 必须传 \"2022-02-17\"(否则请求缺少 X-TC-Version 会失败)。示例:service=\"tcbr\", version=\"2022-02-17\", action=\"CreateCloudRunEnv\", params={EnvId:\"env-xxx\",PackageType:\"Standard\"}。"),
|
|
131461
131687
|
params: zod_1.z
|
|
131462
131688
|
.record(zod_1.z.any())
|
|
131463
131689
|
.optional()
|
|
@@ -131470,10 +131696,11 @@ function registerCapiTools(server) {
|
|
|
131470
131696
|
openWorldHint: true,
|
|
131471
131697
|
category: CATEGORY,
|
|
131472
131698
|
},
|
|
131473
|
-
}, async ({ service, action, params, }) => {
|
|
131699
|
+
}, async ({ service, action, params, version, }) => {
|
|
131474
131700
|
if (!ALLOWED_SERVICES.includes(service)) {
|
|
131475
131701
|
throw new Error(`Service ${service} is not allowed. Allowed services: ${ALLOWED_SERVICES.join(", ")}`);
|
|
131476
131702
|
}
|
|
131703
|
+
assertTcbCloudRunActionAllowed(service, action);
|
|
131477
131704
|
const cloudbase = await getManager();
|
|
131478
131705
|
if (['1', 'true'].includes(process.env.CLOUDBASE_EVALUATE_MODE ?? '')) {
|
|
131479
131706
|
if (service === 'lowcode') {
|
|
@@ -131502,7 +131729,7 @@ function registerCapiTools(server) {
|
|
|
131502
131729
|
let result;
|
|
131503
131730
|
try {
|
|
131504
131731
|
const cleanedParams = params ? removeEmptyStringParams(params) : {};
|
|
131505
|
-
result = await cloudbase.commonService(service).call({
|
|
131732
|
+
result = await cloudbase.commonService(service, version).call({
|
|
131506
131733
|
Action: action,
|
|
131507
131734
|
Param: cleanedParams,
|
|
131508
131735
|
});
|
|
@@ -142128,7 +142355,7 @@ class TelemetryReporter {
|
|
|
142128
142355
|
const arch = os_1.default.arch(); // 系统架构
|
|
142129
142356
|
// Build-time injected version; guard for vitest / non-webpack runtimes.
|
|
142130
142357
|
const mcpVersion = process.env.npm_package_version ||
|
|
142131
|
-
( true ? "2.
|
|
142358
|
+
( true ? "2.27.0" : 0);
|
|
142132
142359
|
return {
|
|
142133
142360
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
142134
142361
|
deviceId: this.deviceId,
|
|
@@ -218225,6 +218452,127 @@ TransportStream.prototype._nop = function _nop() {
|
|
|
218225
218452
|
};
|
|
218226
218453
|
|
|
218227
218454
|
|
|
218455
|
+
/***/ }),
|
|
218456
|
+
|
|
218457
|
+
/***/ 68107:
|
|
218458
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
218459
|
+
|
|
218460
|
+
"use strict";
|
|
218461
|
+
|
|
218462
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
218463
|
+
exports.SITE_IDS = exports.SITE_REGION_MAP = void 0;
|
|
218464
|
+
exports.isSiteId = isSiteId;
|
|
218465
|
+
exports.normalizeSite = normalizeSite;
|
|
218466
|
+
exports.getSite = getSite;
|
|
218467
|
+
exports.resolveSite = resolveSite;
|
|
218468
|
+
exports.resolveSiteAndRegion = resolveSiteAndRegion;
|
|
218469
|
+
const project_config_js_1 = __webpack_require__(68445);
|
|
218470
|
+
exports.SITE_REGION_MAP = {
|
|
218471
|
+
domestic: {
|
|
218472
|
+
id: "domestic",
|
|
218473
|
+
label: "国内站",
|
|
218474
|
+
authHost: "tcb.cloud.tencent.com",
|
|
218475
|
+
consoleHost: "tcb.cloud.tencent.com",
|
|
218476
|
+
defaultRegion: "ap-shanghai",
|
|
218477
|
+
regions: ["ap-shanghai", "ap-guangzhou", "ap-singapore"],
|
|
218478
|
+
capabilities: { noSql: true },
|
|
218479
|
+
},
|
|
218480
|
+
intl: {
|
|
218481
|
+
id: "intl",
|
|
218482
|
+
label: "国际站",
|
|
218483
|
+
authHost: "tcb.tencentcloud.com",
|
|
218484
|
+
consoleHost: "tcb.tencentcloud.com",
|
|
218485
|
+
defaultRegion: "ap-singapore",
|
|
218486
|
+
regions: ["ap-singapore"],
|
|
218487
|
+
capabilities: { noSql: false },
|
|
218488
|
+
},
|
|
218489
|
+
};
|
|
218490
|
+
exports.SITE_IDS = Object.keys(exports.SITE_REGION_MAP);
|
|
218491
|
+
function isSiteId(value) {
|
|
218492
|
+
return value === "domestic" || value === "intl";
|
|
218493
|
+
}
|
|
218494
|
+
function normalizeSite(value) {
|
|
218495
|
+
if (typeof value !== "string") {
|
|
218496
|
+
return undefined;
|
|
218497
|
+
}
|
|
218498
|
+
const normalized = value.trim().toLowerCase();
|
|
218499
|
+
if (normalized === "intl" || normalized === "international") {
|
|
218500
|
+
return "intl";
|
|
218501
|
+
}
|
|
218502
|
+
if (normalized === "domestic" || normalized === "cn") {
|
|
218503
|
+
return "domestic";
|
|
218504
|
+
}
|
|
218505
|
+
return undefined;
|
|
218506
|
+
}
|
|
218507
|
+
/**
|
|
218508
|
+
* 根据 region 与显式 site 解析站点。
|
|
218509
|
+
*
|
|
218510
|
+
* - 显式 site 优先(`domestic`/`intl`,兼容 `cn`/`international` 别名)
|
|
218511
|
+
* - 否则查 SITE_REGION_MAP:region 只属于一个站点时返回该站点
|
|
218512
|
+
* - 当 region 同时存在于多个站点(如 ap-singapore)时返回 `ambiguous`,由上层决定
|
|
218513
|
+
* - 未知 region 或未提供 region 时回退 domestic(全局默认)
|
|
218514
|
+
*/
|
|
218515
|
+
function getSite(region, explicitSite) {
|
|
218516
|
+
const explicit = normalizeSite(explicitSite);
|
|
218517
|
+
if (explicit) {
|
|
218518
|
+
return explicit;
|
|
218519
|
+
}
|
|
218520
|
+
if (!region) {
|
|
218521
|
+
return "domestic";
|
|
218522
|
+
}
|
|
218523
|
+
const matches = exports.SITE_IDS.filter((site) => exports.SITE_REGION_MAP[site].regions.includes(region));
|
|
218524
|
+
if (matches.length === 1) {
|
|
218525
|
+
return matches[0];
|
|
218526
|
+
}
|
|
218527
|
+
if (matches.length > 1) {
|
|
218528
|
+
return "ambiguous";
|
|
218529
|
+
}
|
|
218530
|
+
return "domestic";
|
|
218531
|
+
}
|
|
218532
|
+
/**
|
|
218533
|
+
* 认证/路由场景下解析站点:歧义(如 ap-singapore 未配 site)时默认 intl,保持既有国际站行为。
|
|
218534
|
+
*/
|
|
218535
|
+
function resolveSite(region, explicitSite) {
|
|
218536
|
+
const site = getSite(region, explicitSite);
|
|
218537
|
+
return site === "ambiguous" ? "intl" : site;
|
|
218538
|
+
}
|
|
218539
|
+
/**
|
|
218540
|
+
* 统一 site/region 解析入口(收敛各调用点分散的 region fallback)。
|
|
218541
|
+
*
|
|
218542
|
+
* 优先级:显式参数 > 环境变量(TCB_SITE/TCB_REGION)> 项目配置(.cloudbase/project.json)> 全局默认
|
|
218543
|
+
* (site=domestic, region=ap-shanghai)。
|
|
218544
|
+
*
|
|
218545
|
+
* 当仅指定 region 且该 region 在多个 site 的地域列表中都存在(如 ap-singapore)时,
|
|
218546
|
+
* 返回 site=intl 并标记 ambiguous=true(兼容既有国际站行为),调用方可用 ambiguous 提示用户显式指定 site。
|
|
218547
|
+
*/
|
|
218548
|
+
function resolveSiteAndRegion(opts = {}) {
|
|
218549
|
+
const projectConfig = (0, project_config_js_1.readProjectConfig)();
|
|
218550
|
+
const explicitSite = normalizeSite(opts.site) ??
|
|
218551
|
+
normalizeSite(process.env.TCB_SITE) ??
|
|
218552
|
+
normalizeSite(projectConfig?.site);
|
|
218553
|
+
const explicitRegion = opts.region ?? process.env.TCB_REGION ?? projectConfig?.region;
|
|
218554
|
+
let site = explicitSite;
|
|
218555
|
+
let ambiguous = false;
|
|
218556
|
+
if (!site && explicitRegion) {
|
|
218557
|
+
const inferred = getSite(explicitRegion);
|
|
218558
|
+
if (inferred === "ambiguous") {
|
|
218559
|
+
site = "intl";
|
|
218560
|
+
ambiguous = true;
|
|
218561
|
+
}
|
|
218562
|
+
else if (inferred !== "domestic") {
|
|
218563
|
+
site = inferred;
|
|
218564
|
+
}
|
|
218565
|
+
}
|
|
218566
|
+
const resolvedSite = site ?? "domestic";
|
|
218567
|
+
const region = explicitRegion ?? exports.SITE_REGION_MAP[resolvedSite].defaultRegion;
|
|
218568
|
+
return {
|
|
218569
|
+
site: resolvedSite,
|
|
218570
|
+
region,
|
|
218571
|
+
...(ambiguous ? { ambiguous: true } : {}),
|
|
218572
|
+
};
|
|
218573
|
+
}
|
|
218574
|
+
|
|
218575
|
+
|
|
218228
218576
|
/***/ }),
|
|
218229
218577
|
|
|
218230
218578
|
/***/ 68182:
|
|
@@ -218316,6 +218664,44 @@ function parseEffectsDef(_def, refs) {
|
|
|
218316
218664
|
exports.parseEffectsDef = parseEffectsDef;
|
|
218317
218665
|
|
|
218318
218666
|
|
|
218667
|
+
/***/ }),
|
|
218668
|
+
|
|
218669
|
+
/***/ 68445:
|
|
218670
|
+
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
218671
|
+
|
|
218672
|
+
"use strict";
|
|
218673
|
+
|
|
218674
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
218675
|
+
exports.readProjectConfig = readProjectConfig;
|
|
218676
|
+
const node_fs_1 = __webpack_require__(89971);
|
|
218677
|
+
const node_path_1 = __webpack_require__(26743);
|
|
218678
|
+
/**
|
|
218679
|
+
* 读取项目级配置 `.cloudbase/project.json`。
|
|
218680
|
+
*
|
|
218681
|
+
* 该文件是 site/region/envId 的可选持久化来源,让"打开项目即连对服务"
|
|
218682
|
+
* (对齐 Vercel `.vercel/project.json` 的三级回退)。读取失败/文件不存在
|
|
218683
|
+
* 返回 undefined,不阻塞调用方。
|
|
218684
|
+
*/
|
|
218685
|
+
function readProjectConfig(cwd) {
|
|
218686
|
+
try {
|
|
218687
|
+
const projectRoot = cwd ?? process.env.WORKSPACE_FOLDER_PATHS ?? process.cwd();
|
|
218688
|
+
const configPath = (0, node_path_1.join)(projectRoot, ".cloudbase", "project.json");
|
|
218689
|
+
if (!(0, node_fs_1.existsSync)(configPath)) {
|
|
218690
|
+
return undefined;
|
|
218691
|
+
}
|
|
218692
|
+
const raw = (0, node_fs_1.readFileSync)(configPath, "utf-8");
|
|
218693
|
+
const parsed = JSON.parse(raw);
|
|
218694
|
+
if (!parsed || typeof parsed !== "object") {
|
|
218695
|
+
return undefined;
|
|
218696
|
+
}
|
|
218697
|
+
return parsed;
|
|
218698
|
+
}
|
|
218699
|
+
catch {
|
|
218700
|
+
return undefined;
|
|
218701
|
+
}
|
|
218702
|
+
}
|
|
218703
|
+
|
|
218704
|
+
|
|
218319
218705
|
/***/ }),
|
|
218320
218706
|
|
|
218321
218707
|
/***/ 68599:
|
|
@@ -223544,7 +223930,7 @@ ${envIdSection}
|
|
|
223544
223930
|
## 环境信息
|
|
223545
223931
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
223546
223932
|
- Node.js版本: ${process.version}
|
|
223547
|
-
- MCP 版本:${process.env.npm_package_version || ( true ? "2.
|
|
223933
|
+
- MCP 版本:${process.env.npm_package_version || ( true ? "2.27.0" : 0)}
|
|
223548
223934
|
- 系统架构: ${os_1.default.arch()}
|
|
223549
223935
|
- 时间: ${new Date().toISOString()}
|
|
223550
223936
|
- 请求ID: ${requestId}
|
|
@@ -223625,7 +224011,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
223625
224011
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
223626
224012
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
223627
224013
|
});
|
|
223628
|
-
const mcpVersion = true ? "2.
|
|
224014
|
+
const mcpVersion = true ? "2.27.0" : 0;
|
|
223629
224015
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
223630
224016
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
223631
224017
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -240845,7 +241231,7 @@ function registerSetupTools(server) {
|
|
|
240845
241231
|
server.registerTool("downloadTemplate", {
|
|
240846
241232
|
title: "下载项目模板",
|
|
240847
241233
|
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.
|
|
241234
|
+
**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.27.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
240849
241235
|
inputSchema: {
|
|
240850
241236
|
template: zod_1.z
|
|
240851
241237
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -242259,6 +242645,7 @@ module.exports.sync = path => {
|
|
|
242259
242645
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
242260
242646
|
exports.buildVerificationUriComplete = buildVerificationUriComplete;
|
|
242261
242647
|
exports.buildDeviceAuthChallengePayload = buildDeviceAuthChallengePayload;
|
|
242648
|
+
exports.getCloudBaseApiKeyFromEnv = getCloudBaseApiKeyFromEnv;
|
|
242262
242649
|
exports.getAuthProgressStateSync = getAuthProgressStateSync;
|
|
242263
242650
|
exports.getAuthProgressState = getAuthProgressState;
|
|
242264
242651
|
exports.resolveAuthOptions = resolveAuthOptions;
|
|
@@ -242274,7 +242661,7 @@ exports.getLoginState = getLoginState;
|
|
|
242274
242661
|
exports.logout = logout;
|
|
242275
242662
|
const toolbox_1 = __webpack_require__(25901);
|
|
242276
242663
|
const logger_js_1 = __webpack_require__(13039);
|
|
242277
|
-
const
|
|
242664
|
+
const site_map_js_1 = __webpack_require__(68107);
|
|
242278
242665
|
const auth = toolbox_1.AuthSupervisor.getInstance({});
|
|
242279
242666
|
const authProgressState = {
|
|
242280
242667
|
status: "IDLE",
|
|
@@ -242360,16 +242747,24 @@ function updateAuthProgressState(partial) {
|
|
|
242360
242747
|
});
|
|
242361
242748
|
return getAuthProgressStateSync();
|
|
242362
242749
|
}
|
|
242750
|
+
/**
|
|
242751
|
+
* Resolve CloudBase API Key from env.
|
|
242752
|
+
* Prefer CLOUDBASE_API_KEY; fall back to CLOUDBASE_APIKEY (js-sdk / node-sdk convention).
|
|
242753
|
+
*/
|
|
242754
|
+
function getCloudBaseApiKeyFromEnv() {
|
|
242755
|
+
return process.env.CLOUDBASE_API_KEY || process.env.CLOUDBASE_APIKEY || undefined;
|
|
242756
|
+
}
|
|
242363
242757
|
function normalizeLoginStateFromEnvVars(options) {
|
|
242364
242758
|
if (options?.ignoreEnvVars) {
|
|
242365
242759
|
return null;
|
|
242366
242760
|
}
|
|
242367
|
-
//
|
|
242368
|
-
const
|
|
242369
|
-
|
|
242761
|
+
// Prefer CloudBase API Key over TENCENTCLOUD_*
|
|
242762
|
+
const apiKey = getCloudBaseApiKeyFromEnv();
|
|
242763
|
+
const CLOUDBASE_ENV = process.env.CLOUDBASE_ENV_ID;
|
|
242764
|
+
if (apiKey && CLOUDBASE_ENV) {
|
|
242370
242765
|
return {
|
|
242371
242766
|
_type: 'api_key',
|
|
242372
|
-
apiKey
|
|
242767
|
+
apiKey,
|
|
242373
242768
|
envId: CLOUDBASE_ENV,
|
|
242374
242769
|
};
|
|
242375
242770
|
}
|
|
@@ -242515,6 +242910,98 @@ function resetAuthProgressState() {
|
|
|
242515
242910
|
lastError: undefined,
|
|
242516
242911
|
});
|
|
242517
242912
|
}
|
|
242913
|
+
const LEGACY_SITE = "domestic";
|
|
242914
|
+
function isSlottedCredential(value) {
|
|
242915
|
+
if (!value || typeof value !== "object") {
|
|
242916
|
+
return false;
|
|
242917
|
+
}
|
|
242918
|
+
return "domestic" in value || "intl" in value;
|
|
242919
|
+
}
|
|
242920
|
+
async function readStoredCredentialRaw() {
|
|
242921
|
+
return toolbox_1.authStore.get("credential");
|
|
242922
|
+
}
|
|
242923
|
+
/**
|
|
242924
|
+
* 读取指定 site 槽位的原始凭证;旧格式(单槽 flat)视为 domestic 槽位。
|
|
242925
|
+
*/
|
|
242926
|
+
async function readSiteCredentialRaw(site) {
|
|
242927
|
+
const raw = await readStoredCredentialRaw();
|
|
242928
|
+
if (!raw) {
|
|
242929
|
+
return undefined;
|
|
242930
|
+
}
|
|
242931
|
+
if (isSlottedCredential(raw)) {
|
|
242932
|
+
return raw[site];
|
|
242933
|
+
}
|
|
242934
|
+
return site === LEGACY_SITE ? raw : undefined;
|
|
242935
|
+
}
|
|
242936
|
+
/**
|
|
242937
|
+
* 将凭证写入指定 site 槽位,保留其他槽位数据。
|
|
242938
|
+
* base 缺省时读取当前存储;旧单槽数据会被迁移为 domestic 槽位(写回时升级分槽)。
|
|
242939
|
+
*/
|
|
242940
|
+
async function writeSiteCredential(site, credential, base) {
|
|
242941
|
+
const current = base ?? (await readStoredCredentialRaw());
|
|
242942
|
+
const slotted = current && isSlottedCredential(current)
|
|
242943
|
+
? { ...current }
|
|
242944
|
+
: { domestic: current ?? {} };
|
|
242945
|
+
slotted[site] = credential ?? {};
|
|
242946
|
+
await toolbox_1.authStore.set("credential", slotted);
|
|
242947
|
+
}
|
|
242948
|
+
/**
|
|
242949
|
+
* 确保存储已为分槽格式并返回槽位快照。
|
|
242950
|
+
* 用于登录前迁移旧单槽数据,避免 @cloudbase/toolbox 内部误命中旧凭证而跳过新站点登录。
|
|
242951
|
+
*/
|
|
242952
|
+
async function ensureSlottedCredential() {
|
|
242953
|
+
const raw = await readStoredCredentialRaw();
|
|
242954
|
+
if (raw && isSlottedCredential(raw)) {
|
|
242955
|
+
return { ...raw };
|
|
242956
|
+
}
|
|
242957
|
+
const slotted = { domestic: raw ?? {} };
|
|
242958
|
+
await toolbox_1.authStore.set("credential", slotted);
|
|
242959
|
+
return slotted;
|
|
242960
|
+
}
|
|
242961
|
+
function isSlotTokenExpired(credential, gap = 120) {
|
|
242962
|
+
return (!!credential.accessTokenExpired &&
|
|
242963
|
+
Number(credential.accessTokenExpired) < Date.now() + gap * 1000);
|
|
242964
|
+
}
|
|
242965
|
+
/**
|
|
242966
|
+
* 读取并解析指定 site 的登录态,必要时走 refreshToken 续期(镜像 @cloudbase/toolbox 行为)。
|
|
242967
|
+
*/
|
|
242968
|
+
async function resolveSiteLoginState(site) {
|
|
242969
|
+
const raw = await readSiteCredentialRaw(site);
|
|
242970
|
+
if (!raw) {
|
|
242971
|
+
return null;
|
|
242972
|
+
}
|
|
242973
|
+
const credential = (0, toolbox_1.resolveCredential)(raw);
|
|
242974
|
+
if (!credential?.secretId || !credential?.secretKey) {
|
|
242975
|
+
return null;
|
|
242976
|
+
}
|
|
242977
|
+
if (credential.refreshToken) {
|
|
242978
|
+
if (!isSlotTokenExpired(credential)) {
|
|
242979
|
+
return credential;
|
|
242980
|
+
}
|
|
242981
|
+
if (Date.now() < Number(credential.expired)) {
|
|
242982
|
+
try {
|
|
242983
|
+
const refreshed = await (0, toolbox_1.refreshTmpToken)(credential);
|
|
242984
|
+
await writeSiteCredential(site, refreshed ?? {});
|
|
242985
|
+
const resolved = (0, toolbox_1.resolveCredential)(refreshed ?? {});
|
|
242986
|
+
return resolved?.secretId ? resolved : null;
|
|
242987
|
+
}
|
|
242988
|
+
catch (e) {
|
|
242989
|
+
const code = e?.code;
|
|
242990
|
+
if (code === "AUTH_FAIL" || code === "InternalError.GetRoleError") {
|
|
242991
|
+
return null;
|
|
242992
|
+
}
|
|
242993
|
+
throw e;
|
|
242994
|
+
}
|
|
242995
|
+
}
|
|
242996
|
+
return null;
|
|
242997
|
+
}
|
|
242998
|
+
return credential;
|
|
242999
|
+
}
|
|
243000
|
+
function isUsableCredential(value) {
|
|
243001
|
+
return (!!value &&
|
|
243002
|
+
typeof value === "object" &&
|
|
243003
|
+
Object.keys(value).length > 0);
|
|
243004
|
+
}
|
|
242518
243005
|
async function peekLoginState(options) {
|
|
242519
243006
|
const envVarLoginState = normalizeLoginStateFromEnvVars(options);
|
|
242520
243007
|
if (envVarLoginState) {
|
|
@@ -242536,14 +243023,25 @@ async function peekLoginState(options) {
|
|
|
242536
243023
|
(0, logger_js_1.debug)("loginByApiSecret");
|
|
242537
243024
|
return envVarLoginState;
|
|
242538
243025
|
}
|
|
242539
|
-
//
|
|
242540
|
-
|
|
243026
|
+
// 按 site 读取分槽凭证(缺省按 TCB_SITE/region 解析站点,默认 domestic)
|
|
243027
|
+
const site = (0, site_map_js_1.normalizeSite)(options?.site) ?? (0, site_map_js_1.resolveSite)(options?.region, process.env.TCB_SITE);
|
|
243028
|
+
const slotLoginState = await resolveSiteLoginState(site);
|
|
243029
|
+
if (slotLoginState) {
|
|
243030
|
+
return slotLoginState;
|
|
243031
|
+
}
|
|
243032
|
+
// 兼容旧单槽数据(视为 domestic),走 toolbox 读取/续期
|
|
243033
|
+
if (site === LEGACY_SITE) {
|
|
243034
|
+
return auth.getLoginState();
|
|
243035
|
+
}
|
|
243036
|
+
return null;
|
|
242541
243037
|
}
|
|
242542
243038
|
async function ensureLogin(options) {
|
|
242543
243039
|
(0, logger_js_1.debug)("TENCENTCLOUD_SECRETID", { hasSecretId: !!process.env.TENCENTCLOUD_SECRETID });
|
|
242544
|
-
(0, logger_js_1.debug)("CLOUDBASE_API_KEY", { hasApiKey: !!
|
|
243040
|
+
(0, logger_js_1.debug)("CLOUDBASE_API_KEY", { hasApiKey: !!getCloudBaseApiKeyFromEnv() });
|
|
242545
243041
|
const loginState = await peekLoginState({
|
|
242546
243042
|
ignoreEnvVars: options?.ignoreEnvVars,
|
|
243043
|
+
site: options?.site,
|
|
243044
|
+
region: options?.region,
|
|
242547
243045
|
});
|
|
242548
243046
|
if (!loginState) {
|
|
242549
243047
|
const resolvedAuthOptions = resolveAuthOptions({
|
|
@@ -242559,21 +243057,31 @@ async function ensureLogin(options) {
|
|
|
242559
243057
|
throw new Error(validationError);
|
|
242560
243058
|
}
|
|
242561
243059
|
const mode = resolvedAuthOptions.authMode;
|
|
243060
|
+
// 按显式 site > TCB_SITE > region 映射表解析站点(ap-singapore 歧义默认 intl,兼容既有国际站行为)
|
|
243061
|
+
const resolvedSite = (0, site_map_js_1.resolveSite)(options?.region, options?.site ?? process.env.TCB_SITE);
|
|
242562
243062
|
const loginOptions = { flow: mode };
|
|
242563
243063
|
if (mode === "web") {
|
|
242564
243064
|
loginOptions.getAuthUrl =
|
|
242565
|
-
options?.fromCloudBaseLoginPage &&
|
|
243065
|
+
options?.fromCloudBaseLoginPage && resolvedSite === "domestic"
|
|
242566
243066
|
? (url) => {
|
|
242567
243067
|
const separator = url.includes("?") ? "&" : "?";
|
|
242568
243068
|
const urlWithParam = `${url}${separator}allowNoEnv=true`;
|
|
242569
|
-
return `https
|
|
243069
|
+
return `https://${site_map_js_1.SITE_REGION_MAP.domestic.authHost}/login?_redirect_uri=${encodeURIComponent(urlWithParam)}`;
|
|
242570
243070
|
}
|
|
242571
243071
|
: (url) => {
|
|
242572
|
-
|
|
242573
|
-
|
|
243072
|
+
let finalUrl = url;
|
|
243073
|
+
if (resolvedSite === "intl") {
|
|
243074
|
+
try {
|
|
243075
|
+
const parsed = new URL(url);
|
|
243076
|
+
parsed.host = site_map_js_1.SITE_REGION_MAP.intl.authHost;
|
|
243077
|
+
finalUrl = parsed.toString();
|
|
243078
|
+
}
|
|
243079
|
+
catch {
|
|
243080
|
+
finalUrl = url.replace("cloud.tencent.com", "tencentcloud.com");
|
|
243081
|
+
}
|
|
242574
243082
|
}
|
|
242575
|
-
const separator =
|
|
242576
|
-
return `${
|
|
243083
|
+
const separator = finalUrl.includes("?") ? "&" : "?";
|
|
243084
|
+
return `${finalUrl}${separator}allowNoEnv=true`;
|
|
242577
243085
|
};
|
|
242578
243086
|
}
|
|
242579
243087
|
else {
|
|
@@ -242594,8 +243102,23 @@ async function ensureLogin(options) {
|
|
|
242594
243102
|
}
|
|
242595
243103
|
}
|
|
242596
243104
|
(0, logger_js_1.debug)("beforeloginByWebAuth", { loginOptions });
|
|
243105
|
+
// 登录前迁移旧单槽数据为分槽格式,避免 toolbox 内部命中旧凭证而跳过新站点登录
|
|
243106
|
+
const slotsBefore = await ensureSlottedCredential();
|
|
242597
243107
|
try {
|
|
242598
|
-
await auth.loginByWebAuth(loginOptions);
|
|
243108
|
+
const loginResult = await auth.loginByWebAuth(loginOptions);
|
|
243109
|
+
// toolbox 将新凭证写入 flat 'credential',将其并入对应 site 槽位,保留其他槽位
|
|
243110
|
+
const newRaw = await readStoredCredentialRaw();
|
|
243111
|
+
let credentialToSlot = loginResult;
|
|
243112
|
+
if (isSlottedCredential(newRaw)) {
|
|
243113
|
+
credentialToSlot = newRaw[resolvedSite] ?? newRaw.domestic;
|
|
243114
|
+
}
|
|
243115
|
+
else if (newRaw) {
|
|
243116
|
+
credentialToSlot = newRaw;
|
|
243117
|
+
}
|
|
243118
|
+
if (isUsableCredential(credentialToSlot)) {
|
|
243119
|
+
const merged = { ...slotsBefore, [resolvedSite]: credentialToSlot };
|
|
243120
|
+
await toolbox_1.authStore.set("credential", merged);
|
|
243121
|
+
}
|
|
242599
243122
|
resolveAuthProgressState();
|
|
242600
243123
|
}
|
|
242601
243124
|
catch (error) {
|
|
@@ -242604,6 +243127,8 @@ async function ensureLogin(options) {
|
|
|
242604
243127
|
}
|
|
242605
243128
|
const loginState = await peekLoginState({
|
|
242606
243129
|
ignoreEnvVars: options?.ignoreEnvVars,
|
|
243130
|
+
site: options?.site,
|
|
243131
|
+
region: options?.region,
|
|
242607
243132
|
});
|
|
242608
243133
|
(0, logger_js_1.debug)("loginByWebAuth", { mode, hasLoginState: !!loginState });
|
|
242609
243134
|
return loginState;
|
|
@@ -242616,11 +243141,19 @@ async function ensureLogin(options) {
|
|
|
242616
243141
|
async function getLoginState(options) {
|
|
242617
243142
|
return ensureLogin(options);
|
|
242618
243143
|
}
|
|
242619
|
-
async function logout() {
|
|
242620
|
-
const
|
|
242621
|
-
const
|
|
243144
|
+
async function logout(options) {
|
|
243145
|
+
const site = (0, site_map_js_1.normalizeSite)(options?.site) ?? "domestic";
|
|
243146
|
+
const raw = await readStoredCredentialRaw();
|
|
243147
|
+
if (raw && isSlottedCredential(raw)) {
|
|
243148
|
+
const slotted = { ...raw };
|
|
243149
|
+
delete slotted[site];
|
|
243150
|
+
await toolbox_1.authStore.set("credential", slotted);
|
|
243151
|
+
}
|
|
243152
|
+
else if (site === LEGACY_SITE) {
|
|
243153
|
+
const cwd = process.env.WORKSPACE_FOLDER_PATHS || process.cwd();
|
|
243154
|
+
await auth.logout({ cwd });
|
|
243155
|
+
}
|
|
242622
243156
|
resetAuthProgressState();
|
|
242623
|
-
return result;
|
|
242624
243157
|
}
|
|
242625
243158
|
|
|
242626
243159
|
|
|
@@ -263813,6 +264346,8 @@ const MANAGE_GATEWAY_ACTIONS = [
|
|
|
263813
264346
|
"createRoute",
|
|
263814
264347
|
"updateRoute",
|
|
263815
264348
|
"deleteRoute",
|
|
264349
|
+
"enableRoute",
|
|
264350
|
+
"disableRoute",
|
|
263816
264351
|
"bindCustomDomain",
|
|
263817
264352
|
"deleteCustomDomain",
|
|
263818
264353
|
"enableService",
|
|
@@ -263885,6 +264420,7 @@ function registerGatewayTools(server) {
|
|
|
263885
264420
|
Domain: route.Domain,
|
|
263886
264421
|
Path: route.Path,
|
|
263887
264422
|
IsDefault: route.IsDefault,
|
|
264423
|
+
Enable: typeof route.Enable === "boolean" ? route.Enable : undefined,
|
|
263888
264424
|
UpstreamResourceType: route.UpstreamResourceType,
|
|
263889
264425
|
UpstreamResourceName: route.UpstreamResourceName,
|
|
263890
264426
|
}));
|
|
@@ -263895,16 +264431,26 @@ function registerGatewayTools(server) {
|
|
|
263895
264431
|
Domain: route.Domain,
|
|
263896
264432
|
Path: route.Path,
|
|
263897
264433
|
IsDefault: route.IsDefault,
|
|
264434
|
+
Enable: typeof route.Enable === "boolean" ? route.Enable : undefined,
|
|
263898
264435
|
UpstreamResourceType: route.UpstreamResourceType,
|
|
263899
264436
|
UpstreamResourceName: route.UpstreamResourceName,
|
|
263900
264437
|
}));
|
|
263901
|
-
const
|
|
264438
|
+
const disabledAccessUrls = (0, gateway_access_urls_js_1.rankGatewayAccessUrls)(candidates, {
|
|
264439
|
+
includeDisabled: true,
|
|
264440
|
+
})
|
|
264441
|
+
.filter((item) => !item.enabled)
|
|
264442
|
+
.map((item) => item.url);
|
|
264443
|
+
const envelope = (0, gateway_access_urls_js_1.toAccessUrlEnvelope)((0, gateway_access_urls_js_1.rankGatewayAccessUrls)(candidates), disabledAccessUrls);
|
|
263902
264444
|
return {
|
|
263903
264445
|
...(envelope.accessUrl ? { accessUrl: envelope.accessUrl } : {}),
|
|
263904
264446
|
accessUrls: envelope.accessUrls,
|
|
263905
264447
|
...(envelope.accessUrlSource
|
|
263906
264448
|
? { accessUrlSource: envelope.accessUrlSource }
|
|
263907
264449
|
: {}),
|
|
264450
|
+
accessUrlReachable: Boolean(envelope.accessUrl),
|
|
264451
|
+
...(disabledAccessUrls.length > 0
|
|
264452
|
+
? { disabledAccessUrls, routeDisabled: true }
|
|
264453
|
+
: {}),
|
|
263908
264454
|
};
|
|
263909
264455
|
};
|
|
263910
264456
|
const pickUsableDefaultDomain = (domains) => {
|
|
@@ -263994,6 +264540,15 @@ function registerGatewayTools(server) {
|
|
|
263994
264540
|
: input.enablePathTransmission !== undefined
|
|
263995
264541
|
? input.enablePathTransmission
|
|
263996
264542
|
: undefined;
|
|
264543
|
+
// Route enable: prefer route.enable. For updateRoute only, top-level
|
|
264544
|
+
// enable also maps to Route.Enable (enableService/authSwitch use the
|
|
264545
|
+
// same field name but different actions).
|
|
264546
|
+
const routeEnable = input.route?.enable !== undefined
|
|
264547
|
+
? input.route.enable
|
|
264548
|
+
: input.action === "updateRoute" &&
|
|
264549
|
+
typeof input.enable === "boolean"
|
|
264550
|
+
? input.enable
|
|
264551
|
+
: undefined;
|
|
263997
264552
|
const route = {
|
|
263998
264553
|
Path: path,
|
|
263999
264554
|
UpstreamResourceType: upstreamResourceType,
|
|
@@ -264003,6 +264558,9 @@ function registerGatewayTools(server) {
|
|
|
264003
264558
|
if (enablePathTransmission !== undefined) {
|
|
264004
264559
|
route.EnablePathTransmission = enablePathTransmission;
|
|
264005
264560
|
}
|
|
264561
|
+
if (routeEnable !== undefined) {
|
|
264562
|
+
route.Enable = routeEnable;
|
|
264563
|
+
}
|
|
264006
264564
|
return {
|
|
264007
264565
|
EnvId: await resolveEnvId(),
|
|
264008
264566
|
Domain: {
|
|
@@ -264016,9 +264574,46 @@ function registerGatewayTools(server) {
|
|
|
264016
264574
|
upstreamResourceName,
|
|
264017
264575
|
enableAuth,
|
|
264018
264576
|
enablePathTransmission,
|
|
264577
|
+
enable: routeEnable,
|
|
264019
264578
|
},
|
|
264020
264579
|
};
|
|
264021
264580
|
};
|
|
264581
|
+
const resolveExistingRouteForToggle = async (input) => {
|
|
264582
|
+
const routePath = input.routePath ?? input.path;
|
|
264583
|
+
if (!routePath) {
|
|
264584
|
+
throw new Error("action=enableRoute/disableRoute 时必须提供 path 或 route.path(例如 \"/\" 或 \"/api\")");
|
|
264585
|
+
}
|
|
264586
|
+
const normalizedPath = normalizeAccessPath(routePath);
|
|
264587
|
+
const preferredDomain = input.domain;
|
|
264588
|
+
const result = await listHttpServiceRoutes(preferredDomain ? { domain: preferredDomain } : undefined);
|
|
264589
|
+
const upstreamName = input.routeServiceName ?? input.targetName;
|
|
264590
|
+
const matches = flattenRoutes(result).filter((item) => {
|
|
264591
|
+
if (preferredDomain &&
|
|
264592
|
+
item.Domain !== preferredDomain) {
|
|
264593
|
+
return false;
|
|
264594
|
+
}
|
|
264595
|
+
if (normalizeAccessPath(String(item.Path ?? "")) !== normalizedPath) {
|
|
264596
|
+
return false;
|
|
264597
|
+
}
|
|
264598
|
+
if (upstreamName && item.UpstreamResourceName !== upstreamName) {
|
|
264599
|
+
return false;
|
|
264600
|
+
}
|
|
264601
|
+
return true;
|
|
264602
|
+
});
|
|
264603
|
+
if (matches.length === 0) {
|
|
264604
|
+
throw new Error(`未找到路径 ${normalizedPath} 的路由` +
|
|
264605
|
+
(preferredDomain ? `(域名 ${preferredDomain})` : "") +
|
|
264606
|
+
(upstreamName ? `(上游 ${upstreamName})` : "") +
|
|
264607
|
+
"。请先用 queryGateway(action=\"listRoutes\") 确认 Domain / Path,再调用 enableRoute/disableRoute。" +
|
|
264608
|
+
"关闭静态托管默认域名(*.tcloudbaseapp.com)时,请显式传 domain=该 STATIC_STORE IsDefault 域名,并通常 path=\"/\"。");
|
|
264609
|
+
}
|
|
264610
|
+
if (matches.length > 1) {
|
|
264611
|
+
const domains = [...new Set(matches.map((item) => item.Domain))];
|
|
264612
|
+
throw new Error(`路径 ${normalizedPath} 匹配到 ${matches.length} 条路由(域名:${domains.join(", ")})。` +
|
|
264613
|
+
"请补充 domain(必要时再加 targetName/route.serviceName)精确定位后再 enableRoute/disableRoute。");
|
|
264614
|
+
}
|
|
264615
|
+
return matches[0];
|
|
264616
|
+
};
|
|
264022
264617
|
const routeMutationNextActions = (targetName) => [
|
|
264023
264618
|
{
|
|
264024
264619
|
tool: "queryGateway",
|
|
@@ -264222,6 +264817,10 @@ function registerGatewayTools(server) {
|
|
|
264222
264817
|
Domain: payload.Domain,
|
|
264223
264818
|
});
|
|
264224
264819
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
264820
|
+
const routeEnabled = payload.resolved.enable !== false;
|
|
264821
|
+
const accessUrl = routeEnabled
|
|
264822
|
+
? `https://${payload.resolved.domain}${payload.resolved.path}`
|
|
264823
|
+
: undefined;
|
|
264225
264824
|
return buildEnvelope({
|
|
264226
264825
|
action: input.action,
|
|
264227
264826
|
model: "httpServiceRoute",
|
|
@@ -264231,13 +264830,31 @@ function registerGatewayTools(server) {
|
|
|
264231
264830
|
upstreamResourceName: payload.resolved.upstreamResourceName,
|
|
264232
264831
|
auth: payload.resolved.enableAuth ?? null,
|
|
264233
264832
|
enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
|
|
264234
|
-
|
|
264235
|
-
|
|
264833
|
+
enable: payload.resolved.enable ?? null,
|
|
264834
|
+
...(accessUrl
|
|
264835
|
+
? {
|
|
264836
|
+
accessUrl,
|
|
264837
|
+
accessUrls: [accessUrl],
|
|
264838
|
+
accessUrlReachable: true,
|
|
264839
|
+
}
|
|
264840
|
+
: {
|
|
264841
|
+
accessUrls: [],
|
|
264842
|
+
accessUrlReachable: false,
|
|
264843
|
+
routeDisabled: true,
|
|
264844
|
+
disabledAccessUrls: [
|
|
264845
|
+
`https://${payload.resolved.domain}${payload.resolved.path}`,
|
|
264846
|
+
],
|
|
264847
|
+
}),
|
|
264236
264848
|
accessUrlSource: input.domain && input.domain === payload.resolved.domain
|
|
264237
264849
|
? "gateway.custom"
|
|
264238
264850
|
: "gateway.default",
|
|
264239
264851
|
raw: result,
|
|
264240
264852
|
}, `HTTP 路由更新成功(${payload.resolved.domain}${payload.resolved.path}` +
|
|
264853
|
+
(payload.resolved.enable === false
|
|
264854
|
+
? ",路由已禁用 Enable=false"
|
|
264855
|
+
: payload.resolved.enable === true
|
|
264856
|
+
? ",路由已启用 Enable=true"
|
|
264857
|
+
: "") +
|
|
264241
264858
|
(payload.resolved.enablePathTransmission === true
|
|
264242
264859
|
? ",路径透传=开启"
|
|
264243
264860
|
: payload.resolved.enablePathTransmission === false
|
|
@@ -264245,6 +264862,102 @@ function registerGatewayTools(server) {
|
|
|
264245
264862
|
: "") +
|
|
264246
264863
|
`)`, routeMutationNextActions(payload.resolved.upstreamResourceName));
|
|
264247
264864
|
}
|
|
264865
|
+
case "enableRoute":
|
|
264866
|
+
case "disableRoute": {
|
|
264867
|
+
const enable = input.action === "enableRoute";
|
|
264868
|
+
const existing = await resolveExistingRouteForToggle({
|
|
264869
|
+
path: input.path,
|
|
264870
|
+
routePath: input.route?.path,
|
|
264871
|
+
domain: input.domain,
|
|
264872
|
+
targetName: input.targetName,
|
|
264873
|
+
routeServiceName: input.route?.serviceName,
|
|
264874
|
+
});
|
|
264875
|
+
const upstreamResourceType = resolveUpstreamResourceType({
|
|
264876
|
+
upstreamResourceType: input.upstreamResourceType ??
|
|
264877
|
+
existing.UpstreamResourceType,
|
|
264878
|
+
routeUpstreamResourceType: input.route?.upstreamResourceType,
|
|
264879
|
+
});
|
|
264880
|
+
const upstreamResourceName = input.route?.serviceName ??
|
|
264881
|
+
input.targetName ??
|
|
264882
|
+
String(existing.UpstreamResourceName ?? "");
|
|
264883
|
+
if (!upstreamResourceName) {
|
|
264884
|
+
throw new Error(`action=${input.action} 无法解析上游资源名;请用 queryGateway(listRoutes) 确认后传入 targetName 或 route.serviceName。`);
|
|
264885
|
+
}
|
|
264886
|
+
const normalizedPath = normalizeAccessPath(String(existing.Path ?? input.route?.path ?? input.path ?? "/"));
|
|
264887
|
+
const domain = existing.Domain;
|
|
264888
|
+
const enableAuth = input.route?.auth !== undefined
|
|
264889
|
+
? input.route.auth
|
|
264890
|
+
: input.auth !== undefined
|
|
264891
|
+
? input.auth
|
|
264892
|
+
: existing.EnableAuth;
|
|
264893
|
+
const enablePathTransmission = input.route?.enablePathTransmission !== undefined
|
|
264894
|
+
? input.route.enablePathTransmission
|
|
264895
|
+
: input.enablePathTransmission !== undefined
|
|
264896
|
+
? input.enablePathTransmission
|
|
264897
|
+
: existing.EnablePathTransmission;
|
|
264898
|
+
const route = {
|
|
264899
|
+
Path: normalizedPath,
|
|
264900
|
+
UpstreamResourceType: upstreamResourceType,
|
|
264901
|
+
UpstreamResourceName: upstreamResourceName,
|
|
264902
|
+
Enable: enable,
|
|
264903
|
+
};
|
|
264904
|
+
if (enableAuth !== undefined) {
|
|
264905
|
+
route.EnableAuth = enableAuth;
|
|
264906
|
+
}
|
|
264907
|
+
if (enablePathTransmission !== undefined) {
|
|
264908
|
+
route.EnablePathTransmission = enablePathTransmission;
|
|
264909
|
+
}
|
|
264910
|
+
const cloudbase = await getManager();
|
|
264911
|
+
const result = await cloudbase.env.modifyHttpServiceRoute({
|
|
264912
|
+
EnvId: await resolveEnvId(),
|
|
264913
|
+
Domain: {
|
|
264914
|
+
Domain: domain,
|
|
264915
|
+
Routes: [route],
|
|
264916
|
+
},
|
|
264917
|
+
});
|
|
264918
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
264919
|
+
const verb = enable ? "启用" : "禁用";
|
|
264920
|
+
const accessUrl = enable
|
|
264921
|
+
? `https://${domain}${normalizedPath}`
|
|
264922
|
+
: undefined;
|
|
264923
|
+
return buildEnvelope({
|
|
264924
|
+
action: input.action,
|
|
264925
|
+
model: "httpServiceRoute",
|
|
264926
|
+
domain,
|
|
264927
|
+
path: normalizedPath,
|
|
264928
|
+
upstreamResourceType,
|
|
264929
|
+
upstreamResourceName,
|
|
264930
|
+
enable,
|
|
264931
|
+
auth: enableAuth ?? null,
|
|
264932
|
+
enablePathTransmission: enablePathTransmission ?? null,
|
|
264933
|
+
...(accessUrl
|
|
264934
|
+
? {
|
|
264935
|
+
accessUrl,
|
|
264936
|
+
accessUrls: [accessUrl],
|
|
264937
|
+
accessUrlReachable: true,
|
|
264938
|
+
}
|
|
264939
|
+
: {
|
|
264940
|
+
accessUrls: [],
|
|
264941
|
+
accessUrlReachable: false,
|
|
264942
|
+
routeDisabled: true,
|
|
264943
|
+
disabledAccessUrls: [`https://${domain}${normalizedPath}`],
|
|
264944
|
+
}),
|
|
264945
|
+
accessUrlSource: existing.IsDefault
|
|
264946
|
+
? "gateway.default"
|
|
264947
|
+
: "gateway.custom",
|
|
264948
|
+
raw: result,
|
|
264949
|
+
}, `HTTP 路由已${verb}(${domain}${normalizedPath},Enable=${enable})。` +
|
|
264950
|
+
(enable
|
|
264951
|
+
? "启用后通常数秒到约 30 秒内可访问;请用 queryGateway(getRoute) 或探测 accessUrl 确认。"
|
|
264952
|
+
: "禁用后该路径将不可公网访问(GATEWAY_ROUTE_DISABLED);关闭静态托管默认域名(*.tcloudbaseapp.com)时,请确认 DomainType=STATIC_STORE 且 IsDefault=true。") +
|
|
264953
|
+
" 底层对应 tcb ModifyHTTPServiceRoute(不是 ModifyGatewayRoute)。", [
|
|
264954
|
+
{
|
|
264955
|
+
tool: "queryGateway",
|
|
264956
|
+
action: "getRoute",
|
|
264957
|
+
reason: `复核路由 Enable=${enable} 是否已生效`,
|
|
264958
|
+
},
|
|
264959
|
+
]);
|
|
264960
|
+
}
|
|
264248
264961
|
case "deleteRoute": {
|
|
264249
264962
|
const routePath = input.route?.path ?? input.path;
|
|
264250
264963
|
if (!routePath) {
|
|
@@ -264452,8 +265165,11 @@ function registerGatewayTools(server) {
|
|
|
264452
265165
|
}, async (input) => withEnvelope(() => handleQueryGateway(input)));
|
|
264453
265166
|
server.registerTool?.("manageGateway", {
|
|
264454
265167
|
title: "管理 CloudBase 网关",
|
|
264455
|
-
description: "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path
|
|
265168
|
+
description: "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path 转到上游;" +
|
|
265169
|
+
"enableRoute/disableRoute 启用或禁用已有路由(底层 ModifyHTTPServiceRoute 的 Routes[].Enable,不是 ModifyGatewayRoute)。" +
|
|
265170
|
+
"未传 domain 时用 DomainType=HTTPSERVICE 的 IsDefault 默认 HTTP 域名(形如 *.{region}.app.tcloudbase.com),不会使用静态托管 CDN 域名(*.tcloudbaseapp.com,DomainType=STATIC_STORE)。" +
|
|
264456
265171
|
"这是网关默认域上的路径路由,不是 STATIC_STORE 上游绑定;STATIC_STORE 上游必须显式传 upstreamResourceType=STATIC_STORE。" +
|
|
265172
|
+
"关闭静态托管默认域名(*.tcloudbaseapp.com):先 queryGateway(listRoutes) 找到 DomainType=STATIC_STORE 且 IsDefault=true 的 domain,再 manageGateway(action=\"disableRoute\", domain=该域名, path=\"/\");勿用 manageHosting。" +
|
|
264457
265173
|
"创建后可用 queryGateway(action=\"listRoutes\") 核对 Domain / DomainType / Path / UpstreamResourceType。" +
|
|
264458
265174
|
"上游类型只用一个参数 upstreamResourceType(也可写在 route.upstreamResourceType,route 优先):" +
|
|
264459
265175
|
"WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器;" +
|
|
@@ -264466,9 +265182,12 @@ function registerGatewayTools(server) {
|
|
|
264466
265182
|
inputSchema: {
|
|
264467
265183
|
action: zod_1.z
|
|
264468
265184
|
.enum(MANAGE_GATEWAY_ACTIONS)
|
|
264469
|
-
.describe("写操作:createRoute/updateRoute/deleteRoute 管理路由;
|
|
265185
|
+
.describe("写操作:createRoute/updateRoute/deleteRoute 管理路由;enableRoute/disableRoute 启用或禁用已有路由(需 path,建议显式传 domain);" +
|
|
265186
|
+
"bindCustomDomain/deleteCustomDomain 管理自定义域名;" +
|
|
264470
265187
|
"enableService/authSwitch 开关 HTTP 网关总开关与访问鉴权(需配合 enable 参数)。" +
|
|
264471
|
-
"createRoute/updateRoute 必须提供 upstreamResourceType
|
|
265188
|
+
"createRoute/updateRoute 必须提供 upstreamResourceType;enableRoute/disableRoute 会先 listRoutes 定位已有路由,通常不必重填上游。" +
|
|
265189
|
+
"updateRoute 也可传 enable/route.enable 直接改 Routes[].Enable。" +
|
|
265190
|
+
"关闭 *.tcloudbaseapp.com 默认静态托管域:disableRoute + domain=该 STATIC_STORE IsDefault 域名 + path=\"/\"。" +
|
|
264472
265191
|
"已有自定义域名时优先 createRoute(domain=已有域名) 实现访问,不必再次 bindCustomDomain / 传入 certificateId;" +
|
|
264473
265192
|
"bindCustomDomain 仅用于首次绑定新域名(需 certificateId;可选 accessType=DIRECT|CDN|CUSTOM,CUSTOM 需 customCname;普通场景用默认 DIRECT)。" +
|
|
264474
265193
|
"接入说明:https://docs.cloudbase.net/service/custom-domain"),
|
|
@@ -264517,16 +265236,25 @@ function registerGatewayTools(server) {
|
|
|
264517
265236
|
.boolean()
|
|
264518
265237
|
.optional()
|
|
264519
265238
|
.describe("同顶层 enablePathTransmission。route 内设置时优先于顶层。"),
|
|
265239
|
+
enable: zod_1.z
|
|
265240
|
+
.boolean()
|
|
265241
|
+
.optional()
|
|
265242
|
+
.describe("路由级开关(Routes[].Enable / Route.Enable)。createRoute/updateRoute 可用:" +
|
|
265243
|
+
"enable=false 禁用该 Domain+Path(访问返回 GATEWAY_ROUTE_DISABLED);" +
|
|
265244
|
+
"enable=true 重新启用。updateRoute 也可用顶层 enable 表达同一语义;" +
|
|
265245
|
+
"也可用专用 action enableRoute/disableRoute。route.enable 优先于顶层 enable。"),
|
|
264520
265246
|
})
|
|
264521
265247
|
.optional()
|
|
264522
265248
|
.describe("路由对象(可选写法)。例:云函数 {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"};" +
|
|
264523
265249
|
"云托管 {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"};" +
|
|
264524
|
-
"静态托管 {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"}
|
|
265250
|
+
"静态托管 {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"};" +
|
|
265251
|
+
"禁用路由 {path:\"/\",enable:false}(配合 updateRoute,或直接用 disableRoute)。"),
|
|
264525
265252
|
domain: zod_1.z
|
|
264526
265253
|
.string()
|
|
264527
265254
|
.optional()
|
|
264528
265255
|
.describe("域名。省略时自动使用环境 DomainType=HTTPSERVICE 的 IsDefault 默认 HTTP 域名(*.{region}.app.tcloudbase.com),不会回退到静态托管 CDN 域名(*.tcloudbaseapp.com,DomainType=STATIC_STORE);也不是 STATIC_STORE 上游绑定。" +
|
|
264529
265256
|
"可用 queryGateway(action=\"listRoutes\") 核对实际 Domain / DomainType。" +
|
|
265257
|
+
"enableRoute/disableRoute 操作 *.tcloudbaseapp.com 时必须显式传入该域名。" +
|
|
264530
265258
|
"已有自定义域名时请显式传入该域名并 createRoute/updateRoute/deleteRoute,即可实现自定义域名访问且无需证书 ID;" +
|
|
264531
265259
|
"仅 bindCustomDomain 时表示要新绑定的域名。"),
|
|
264532
265260
|
certificateId: zod_1.z
|
|
@@ -264549,9 +265277,11 @@ function registerGatewayTools(server) {
|
|
|
264549
265277
|
enable: zod_1.z
|
|
264550
265278
|
.boolean()
|
|
264551
265279
|
.optional()
|
|
264552
|
-
.describe("
|
|
264553
|
-
"bindCustomDomain 可选:enable=false
|
|
264554
|
-
"
|
|
265280
|
+
.describe("开关目标状态:enableService / authSwitch 必填(true 开启 / false 关闭);" +
|
|
265281
|
+
"bindCustomDomain 可选:enable=false 表示绑定后禁用域名(默认启用);" +
|
|
265282
|
+
"updateRoute 可选:映射到 Routes[].Enable(也可用 route.enable,route 优先;" +
|
|
265283
|
+
"也可用专用 action enableRoute/disableRoute)。" +
|
|
265284
|
+
"省略或非布尔值在 enableService/authSwitch 会返回参数错误。"),
|
|
264555
265285
|
},
|
|
264556
265286
|
annotations: {
|
|
264557
265287
|
readOnlyHint: false,
|
|
@@ -274552,7 +275282,6 @@ function shouldRegisterTool(toolName) {
|
|
|
274552
275282
|
'createFunction', // also involves local files
|
|
274553
275283
|
// Download tools - local file downloads
|
|
274554
275284
|
'downloadTemplate',
|
|
274555
|
-
'downloadRemoteFile',
|
|
274556
275285
|
// Setup tools - local config file operations
|
|
274557
275286
|
'setupEnvironmentId',
|
|
274558
275287
|
// CloudRun tools - local file operations
|