@cloudbase/cloudbase-mcp 2.32.3 → 2.32.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/cli.cjs +3 -3
- package/dist/index.cjs +284 -168
- package/dist/index.d.ts +8 -0
- package/dist/index.js +284 -168
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/index.js
CHANGED
|
@@ -548,15 +548,20 @@ async function fetchAccountEnvCandidates(cloudBaseOptions) {
|
|
|
548
548
|
function isApiKeyCredentialMode() {
|
|
549
549
|
return Boolean((0, auth_js_1.getCloudBaseApiKeyFromEnv)() && process.env.CLOUDBASE_ENV_ID);
|
|
550
550
|
}
|
|
551
|
-
function getCredentialScope() {
|
|
551
|
+
function getCredentialScope(cloudBaseOptions) {
|
|
552
|
+
// 只认宿主的显式声明 credentialScope: 'env'(如 tcb-bff hosted OAuth 签发的
|
|
553
|
+
// 环境级 federated STS)。账号级 DescribeEnvs(不带 EnvId)会被后端拒绝(invalid token)。
|
|
554
|
+
// 注意:不能用 token 字段的有无推断范围——sessionToken 本身不携带权限范围语义。
|
|
555
|
+
if (cloudBaseOptions?.credentialScope === "env")
|
|
556
|
+
return "single_env";
|
|
552
557
|
return isApiKeyCredentialMode() ? "single_env" : "account";
|
|
553
558
|
}
|
|
554
559
|
function buildCredentialBoundaryPayload(cloudBaseOptions) {
|
|
555
|
-
const credentialScope = getCredentialScope();
|
|
560
|
+
const credentialScope = getCredentialScope(cloudBaseOptions);
|
|
556
561
|
const currentRegion = (0, site_map_js_1.resolveSiteAndRegion)(cloudBaseOptions ?? {}).region;
|
|
557
562
|
const pinnedEnvId = process.env.CLOUDBASE_ENV_ID || cloudBaseOptions?.envId || null;
|
|
558
563
|
const scopeNote = credentialScope === "single_env"
|
|
559
|
-
?
|
|
564
|
+
? `当前为环境级凭证登录(单环境权限,API Key 或托管授权 token)。只能访问已绑定的 envId${pinnedEnvId ? ` ${pinnedEnvId}` : ""},看不到账号下其他环境或其他地域。这是凭据权限边界,不是环境不存在。queryEnv(action="list") 会自动降级为仅返回绑定环境的信息。`
|
|
560
565
|
: `当前为账号级登录。DescribeEnvs 按地域查询;未传 region 时使用当前地域 ${currentRegion}。其他地域请用 queryEnv(action="list", region="ap-singapore"),或 CLI: tcb env list -r ap-singapore。`;
|
|
561
566
|
return {
|
|
562
567
|
credential_scope: credentialScope,
|
|
@@ -675,6 +680,24 @@ function buildAuthEnvSetupPayload(preparation) {
|
|
|
675
680
|
...buildEnvCandidatePayload(preparation.envCandidates),
|
|
676
681
|
};
|
|
677
682
|
}
|
|
683
|
+
// API Key 登录态 CAM 能力受限时的用户提示(实测:部分 API Key 换出的 STS 不带 CAM 策略)
|
|
684
|
+
const API_KEY_CAM_LIMITATION_WARNING = "\n\n⚠️ 注意:该 API Key 换取的临时凭据无法调用管理面 API(CAM 鉴权不通过),管理类工具(queryEnv、queryAppAuth、manageAppAuth 等)将不可用。如需完整能力,请改用长期密钥 TENCENTCLOUD_SECRETID / TENCENTCLOUD_SECRETKEY 认证。";
|
|
685
|
+
/**
|
|
686
|
+
* API Key 登录态 AUTH_READY 出口统一追加 CAM 能力探测警告。
|
|
687
|
+
* 仅在探测明确返回 limited(CAM 拒绝)时追加;capable/unknown 静默通过。
|
|
688
|
+
*/
|
|
689
|
+
async function appendApiKeyCamWarningIfNeeded(loginState, message) {
|
|
690
|
+
try {
|
|
691
|
+
const probe = await (0, cloudbase_manager_js_1.probeApiKeyCamCapability)(loginState);
|
|
692
|
+
return probe === "limited" ? message + API_KEY_CAM_LIMITATION_WARNING : message;
|
|
693
|
+
}
|
|
694
|
+
catch (e) {
|
|
695
|
+
(0, logger_js_1.debug)("appendApiKeyCamWarningIfNeeded: probe threw", {
|
|
696
|
+
error: e instanceof Error ? e.message : String(e),
|
|
697
|
+
});
|
|
698
|
+
return message;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
678
701
|
async function prepareAuthEnvironment(params) {
|
|
679
702
|
const { server, cloudBaseOptions, loginState } = params;
|
|
680
703
|
const currentEnvId = (0, cloudbase_manager_js_1.getCachedEnvId)() ||
|
|
@@ -1223,10 +1246,11 @@ function buildEnvQueryErrorMessage(error, action) {
|
|
|
1223
1246
|
const hasEnvNotFoundError = /env|environment|环境.*不存在|not found/i.test(baseMessage);
|
|
1224
1247
|
const suggestions = [];
|
|
1225
1248
|
if (hasInvalidParameterError) {
|
|
1226
|
-
suggestions.push("
|
|
1227
|
-
suggestions.push("1.
|
|
1228
|
-
suggestions.push("2.
|
|
1229
|
-
suggestions.push("3.
|
|
1249
|
+
suggestions.push("参数错误:请求未通过服务端的参数校验,请检查本次调用的入参:");
|
|
1250
|
+
suggestions.push("1. 各参数取值是否在允许范围内(枚举值、时间粒度、数量上限等)");
|
|
1251
|
+
suggestions.push("2. 各参数格式是否正确,需要成对传入的参数是否齐全");
|
|
1252
|
+
suggestions.push("3. 必填参数是否都已提供,参数名与类型是否正确");
|
|
1253
|
+
suggestions.push(`4. 修正参数后重新调用 queryEnv(action=\"${action}\")`);
|
|
1230
1254
|
}
|
|
1231
1255
|
if (hasAuthError) {
|
|
1232
1256
|
suggestions.push("认证错误:当前未登录或认证已过期。");
|
|
@@ -1762,7 +1786,7 @@ function registerEnvTools(server) {
|
|
|
1762
1786
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
1763
1787
|
ok: true,
|
|
1764
1788
|
code: "AUTH_READY",
|
|
1765
|
-
message: "当前使用 API Key 认证模式,已自动完成登录,无需手动授权。",
|
|
1789
|
+
message: await appendApiKeyCamWarningIfNeeded(existingLoginState, "当前使用 API Key 认证模式,已自动完成登录,无需手动授权。"),
|
|
1766
1790
|
auth_mode: "api_key",
|
|
1767
1791
|
envId: process.env.CLOUDBASE_ENV_ID,
|
|
1768
1792
|
});
|
|
@@ -1773,16 +1797,21 @@ function registerEnvTools(server) {
|
|
|
1773
1797
|
(0, logger_js_1.debug)("start_auth: peekLoginState threw", { error: e instanceof Error ? e.message : String(e) });
|
|
1774
1798
|
}
|
|
1775
1799
|
// API Key exchange failed: return diagnostic details
|
|
1800
|
+
const exchangeRegion = (0, site_map_js_1.resolveApiKeyExchangeRegion)();
|
|
1776
1801
|
let diagMessage = "当前配置了 API Key 认证模式,但换取临时密钥失败。";
|
|
1777
|
-
const endpoint = process.env.CLOUDBASE_API_ENDPOINT ||
|
|
1802
|
+
const endpoint = process.env.CLOUDBASE_API_ENDPOINT ||
|
|
1803
|
+
`https://${process.env.CLOUDBASE_ENV_ID}.${exchangeRegion ?? "ap-shanghai"}.tcb-api.tencentcloudapi.com`;
|
|
1778
1804
|
diagMessage += `\n\n诊断信息:`;
|
|
1779
1805
|
diagMessage += `\n- CLOUDBASE_ENV_ID: ${process.env.CLOUDBASE_ENV_ID}`;
|
|
1780
1806
|
diagMessage += `\n- CLOUDBASE_API_KEY: ${apiKeyFromEnv.slice(0, 20)}...(已截断)`;
|
|
1807
|
+
diagMessage += `\n- TCB_SITE: ${process.env.TCB_SITE || "(未设置)"}`;
|
|
1808
|
+
diagMessage += `\n- 换取网关地域: ${exchangeRegion ?? "ap-shanghai(国内站默认,多地域环境均经其路由)"}`;
|
|
1781
1809
|
diagMessage += `\n- Endpoint: ${endpoint}`;
|
|
1782
1810
|
diagMessage += `\n\n可能原因:`;
|
|
1783
1811
|
diagMessage += `\n1. API Key 已过期或被删除`;
|
|
1784
1812
|
diagMessage += `\n2. Endpoint 不可达(网络/DNS 问题)`;
|
|
1785
1813
|
diagMessage += `\n3. CLOUDBASE_ENV_ID 与 API Key 所属环境不匹配`;
|
|
1814
|
+
diagMessage += `\n4. API Key 与站点不匹配:国际站环境的 Key 需配置 TCB_SITE=intl(走 ap-singapore 网关);国内站环境(含 ap-guangzhou/ap-singapore 地域)不要配置 intl`;
|
|
1786
1815
|
diagMessage += `\n\n建议:检查 MCP 配置中的 CLOUDBASE_API_KEY(或兼容的 CLOUDBASE_APIKEY)和 CLOUDBASE_ENV_ID 环境变量是否正确。`;
|
|
1787
1816
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
1788
1817
|
ok: false,
|
|
@@ -1993,7 +2022,7 @@ function registerEnvTools(server) {
|
|
|
1993
2022
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
1994
2023
|
ok: true,
|
|
1995
2024
|
code: "AUTH_READY",
|
|
1996
|
-
message: "API Key 认证成功,已获取临时密钥。",
|
|
2025
|
+
message: await appendApiKeyCamWarningIfNeeded(loginState, "API Key 认证成功,已获取临时密钥。"),
|
|
1997
2026
|
auth_mode: "api_key",
|
|
1998
2027
|
...buildAuthEnvSetupPayload(envPreparation),
|
|
1999
2028
|
next_step: envPreparation.nextStep,
|
|
@@ -2006,10 +2035,12 @@ function registerEnvTools(server) {
|
|
|
2006
2035
|
diagMessage += `\n\n诊断信息:`;
|
|
2007
2036
|
diagMessage += `\n- CLOUDBASE_ENV_ID: ${toolApiKeyEnvId}`;
|
|
2008
2037
|
diagMessage += `\n- CLOUDBASE_API_KEY: ${toolApiKey.slice(0, 20)}...(已截断)`;
|
|
2038
|
+
diagMessage += `\n- TCB_SITE: ${process.env.TCB_SITE || "(未设置)"}`;
|
|
2009
2039
|
diagMessage += `\n\n可能原因:`;
|
|
2010
2040
|
diagMessage += `\n1. API Key 已过期或被删除`;
|
|
2011
2041
|
diagMessage += `\n2. CLOUDBASE_ENV_ID 与 API Key 所属环境不匹配`;
|
|
2012
|
-
diagMessage += `\n3.
|
|
2042
|
+
diagMessage += `\n3. API Key 与站点不匹配:国际站环境的 Key 需配置 TCB_SITE=intl(走 ap-singapore 网关);国内站环境(含 ap-guangzhou/ap-singapore 地域)不要配置 intl`;
|
|
2043
|
+
diagMessage += `\n4. 网络连接问题`;
|
|
2013
2044
|
diagMessage += `\n\n建议:请检查 API Key 和环境 ID 是否正确。`;
|
|
2014
2045
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
2015
2046
|
ok: false,
|
|
@@ -2233,9 +2264,19 @@ function registerEnvTools(server) {
|
|
|
2233
2264
|
// API Key / env-var pin: skip DescribeEnvs (STS often cannot list).
|
|
2234
2265
|
// Account-level sessions that only pinned CLOUDBASE_ENV_ID via set_env
|
|
2235
2266
|
// can still pass region/alias/envId to list other environments.
|
|
2236
|
-
|
|
2267
|
+
// Hosted OAuth: 宿主(tcb-bff)显式声明 credentialScope: 'env',签发的
|
|
2268
|
+
// 环境级 federated STS 调账号级 DescribeEnvs 会被拒("invalid token"),
|
|
2269
|
+
// 同样 pin 到绑定 envId 降级为 describeEnvInfo。
|
|
2270
|
+
const isEnvScopedCredential = cloudBaseOptions?.credentialScope === "env" &&
|
|
2271
|
+
typeof cloudBaseOptions?.envId === "string" &&
|
|
2272
|
+
cloudBaseOptions.envId.length > 0;
|
|
2273
|
+
const envIdFromEnv = !cloudBaseOptions?.requestFn &&
|
|
2274
|
+
(process.env.CLOUDBASE_ENV_ID ||
|
|
2275
|
+
(isEnvScopedCredential ? cloudBaseOptions.envId : undefined));
|
|
2237
2276
|
const shouldPinToEnvVar = Boolean(envIdFromEnv &&
|
|
2238
|
-
(isApiKeyCredentialMode() ||
|
|
2277
|
+
(isApiKeyCredentialMode() ||
|
|
2278
|
+
isEnvScopedCredential ||
|
|
2279
|
+
(!region && !alias && !envId)));
|
|
2239
2280
|
if (shouldPinToEnvVar && envIdFromEnv) {
|
|
2240
2281
|
try {
|
|
2241
2282
|
const envInfo = await cloudbaseList.env.describeEnvInfo({ EnvId: envIdFromEnv });
|
|
@@ -2258,6 +2299,13 @@ function registerEnvTools(server) {
|
|
|
2258
2299
|
(0, logger_js_1.debug)("DescribeEnvInfo 失败,返回基础环境信息:", envInfoError instanceof Error ? envInfoError : new Error(String(envInfoError)));
|
|
2259
2300
|
result = { EnvList: [{ EnvId: envIdFromEnv }] };
|
|
2260
2301
|
}
|
|
2302
|
+
// 给调用方 AI 说明降级原因,避免误判为"账号只有一个环境"
|
|
2303
|
+
if (isEnvScopedCredential) {
|
|
2304
|
+
result = {
|
|
2305
|
+
...result,
|
|
2306
|
+
scope_note: `当前凭证为环境级(托管授权凭证绑定 ${envIdFromEnv}),账号级环境列表接口无权限,已降级为仅返回绑定环境的信息。如需查看账号下全部环境,请用有账号级权限的凭证登录。`,
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2261
2309
|
}
|
|
2262
2310
|
else {
|
|
2263
2311
|
// Use commonService to call DescribeEnvs with filter parameters
|
|
@@ -3605,7 +3653,7 @@ ${envIdSection}
|
|
|
3605
3653
|
## 环境信息
|
|
3606
3654
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
3607
3655
|
- Node.js版本: ${process.version}
|
|
3608
|
-
- MCP 版本:${process.env.npm_package_version || ( true ? "2.32.
|
|
3656
|
+
- MCP 版本:${process.env.npm_package_version || ( true ? "2.32.5" : 0)}
|
|
3609
3657
|
- 系统架构: ${os_1.default.arch()}
|
|
3610
3658
|
- 时间: ${new Date().toISOString()}
|
|
3611
3659
|
- 请求ID: ${requestId}
|
|
@@ -3690,7 +3738,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
3690
3738
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
3691
3739
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
3692
3740
|
});
|
|
3693
|
-
const mcpVersion = true ? "2.32.
|
|
3741
|
+
const mcpVersion = true ? "2.32.5" : 0;
|
|
3694
3742
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
3695
3743
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
3696
3744
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -5550,6 +5598,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5550
5598
|
};
|
|
5551
5599
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
5552
5600
|
exports.envManager = void 0;
|
|
5601
|
+
exports.probeApiKeyCamCapability = probeApiKeyCamCapability;
|
|
5553
5602
|
exports.listAvailableEnvCandidates = listAvailableEnvCandidates;
|
|
5554
5603
|
exports.resolveEnvCandidateByEnvId = resolveEnvCandidateByEnvId;
|
|
5555
5604
|
exports.getEnvId = getEnvId;
|
|
@@ -5594,6 +5643,53 @@ function createManagerFromLoginState(loginState, region) {
|
|
|
5594
5643
|
region: region ?? (0, site_map_js_1.resolveSiteAndRegion)().region,
|
|
5595
5644
|
});
|
|
5596
5645
|
}
|
|
5646
|
+
// 仅缓存确定性结果(capable/limited),unknown 不缓存以便下次重试
|
|
5647
|
+
const apiKeyCamProbeCache = new Map();
|
|
5648
|
+
const CAM_PROBE_TIMEOUT_MS = 8000;
|
|
5649
|
+
/**
|
|
5650
|
+
* 轻量探测 API Key 登录态能否调用管理面(CAM)API。
|
|
5651
|
+
* 背景:部分 API Key(如"AI 开发套件"形态的 JWT key)只能完成 tcb-api 网关的登录态换取,
|
|
5652
|
+
* 换出的 STS 凭据不带 CAM 策略,queryEnv/queryAppAuth 等管理类工具会全部失败。
|
|
5653
|
+
* 实测凭据:2026-09-01 国际站认证排查(specs/intl-auth-investigation)。
|
|
5654
|
+
*/
|
|
5655
|
+
async function probeApiKeyCamCapability(loginState) {
|
|
5656
|
+
if (!loginState.secretId || !loginState.secretKey || !loginState.envId) {
|
|
5657
|
+
return "unknown";
|
|
5658
|
+
}
|
|
5659
|
+
const cacheKey = `${loginState.secretId}:${loginState.envId}`;
|
|
5660
|
+
const cached = apiKeyCamProbeCache.get(cacheKey);
|
|
5661
|
+
if (cached !== undefined) {
|
|
5662
|
+
return cached ? "capable" : "limited";
|
|
5663
|
+
}
|
|
5664
|
+
try {
|
|
5665
|
+
const manager = createManagerFromLoginState(loginState);
|
|
5666
|
+
// 查询环境详情(DescribeEnvInfo):单环境、入参仅 EnvId,比 DescribeEnvs 更贴合
|
|
5667
|
+
// "登录后查环境" 的首个真实调用;Action 名已对照 CAM 资源级策略文档确认
|
|
5668
|
+
const probeCall = manager.commonService("tcb").call({
|
|
5669
|
+
Action: "DescribeEnvInfo",
|
|
5670
|
+
Param: { EnvId: loginState.envId },
|
|
5671
|
+
});
|
|
5672
|
+
const timeout = new Promise((_, reject) => {
|
|
5673
|
+
const timer = setTimeout(() => reject(new Error("probe timeout")), CAM_PROBE_TIMEOUT_MS);
|
|
5674
|
+
// 不阻塞进程退出
|
|
5675
|
+
timer.unref?.();
|
|
5676
|
+
});
|
|
5677
|
+
await Promise.race([probeCall, timeout]);
|
|
5678
|
+
apiKeyCamProbeCache.set(cacheKey, true);
|
|
5679
|
+
return "capable";
|
|
5680
|
+
}
|
|
5681
|
+
catch (e) {
|
|
5682
|
+
const code = e?.code ?? (e instanceof Error ? e.message : String(e));
|
|
5683
|
+
// 仅把 CAM 明确拒绝判定为 limited;超时/网络等 inconclusive 失败归为 unknown
|
|
5684
|
+
if (code.includes("UnauthorizedOperation") || code.includes("invalid token")) {
|
|
5685
|
+
(0, logger_js_1.debug)("probeApiKeyCamCapability: CAM rejected", { code });
|
|
5686
|
+
apiKeyCamProbeCache.set(cacheKey, false);
|
|
5687
|
+
return "limited";
|
|
5688
|
+
}
|
|
5689
|
+
(0, logger_js_1.debug)("probeApiKeyCamCapability: inconclusive failure", { code });
|
|
5690
|
+
return "unknown";
|
|
5691
|
+
}
|
|
5692
|
+
}
|
|
5597
5693
|
async function listAvailableEnvCandidates(options) {
|
|
5598
5694
|
const { cloudBaseOptions, loginState: providedLoginState, region: regionOverride, ignorePinnedEnvId, } = options ?? {};
|
|
5599
5695
|
if (!ignorePinnedEnvId) {
|
|
@@ -8421,8 +8517,7 @@ const cloudbase_manager_js_1 = __webpack_require__(3431);
|
|
|
8421
8517
|
const json_content_js_1 = __webpack_require__(4365);
|
|
8422
8518
|
const logger_js_1 = __webpack_require__(3039);
|
|
8423
8519
|
// 1. 枚举定义
|
|
8424
|
-
const
|
|
8425
|
-
const SearchKnowledgeModeEnum = zod_1.z.enum(["vector", "skill", "openapi", "docs"]);
|
|
8520
|
+
const SearchKnowledgeModeEnum = zod_1.z.enum(["skill", "openapi", "docs"]);
|
|
8426
8521
|
const CloudBaseDocsActionEnum = zod_1.z.enum([
|
|
8427
8522
|
"listModules",
|
|
8428
8523
|
"listModuleDocs",
|
|
@@ -8430,12 +8525,6 @@ const CloudBaseDocsActionEnum = zod_1.z.enum([
|
|
|
8430
8525
|
"readDoc",
|
|
8431
8526
|
"searchDocs",
|
|
8432
8527
|
]);
|
|
8433
|
-
// 2. 枚举到后端 id 的映射
|
|
8434
|
-
const KnowledgeBaseIdMap = {
|
|
8435
|
-
cloudbase: "ykfzskv4_ad28",
|
|
8436
|
-
scf: "scfsczskzyws_4bdc",
|
|
8437
|
-
miniprogram: "xcxzskws_25d8",
|
|
8438
|
-
};
|
|
8439
8528
|
// ============ 缓存配置 ============
|
|
8440
8529
|
const CACHE_BASE_DIR = path.join(os.homedir(), ".cloudbase-mcp");
|
|
8441
8530
|
const CACHE_META_FILE = path.join(CACHE_BASE_DIR, "cache-meta.json");
|
|
@@ -8559,32 +8648,6 @@ async function updateCache() {
|
|
|
8559
8648
|
await fs.mkdir(CACHE_BASE_DIR, { recursive: true });
|
|
8560
8649
|
await fs.writeFile(CACHE_META_FILE, JSON.stringify({ timestamp: Date.now() }, null, 2), "utf8");
|
|
8561
8650
|
}
|
|
8562
|
-
// 安全 JSON.parse
|
|
8563
|
-
function safeParse(str) {
|
|
8564
|
-
try {
|
|
8565
|
-
return JSON.parse(str);
|
|
8566
|
-
}
|
|
8567
|
-
catch (e) {
|
|
8568
|
-
return {};
|
|
8569
|
-
}
|
|
8570
|
-
}
|
|
8571
|
-
// 安全 JSON.stringify,处理循环引用
|
|
8572
|
-
function safeStringify(obj) {
|
|
8573
|
-
const seen = new WeakSet();
|
|
8574
|
-
try {
|
|
8575
|
-
return JSON.stringify(obj, function (key, value) {
|
|
8576
|
-
if (typeof value === "object" && value !== null) {
|
|
8577
|
-
if (seen.has(value))
|
|
8578
|
-
return;
|
|
8579
|
-
seen.add(value);
|
|
8580
|
-
}
|
|
8581
|
-
return value;
|
|
8582
|
-
});
|
|
8583
|
-
}
|
|
8584
|
-
catch (e) {
|
|
8585
|
-
return "";
|
|
8586
|
-
}
|
|
8587
|
-
}
|
|
8588
8651
|
function buildDocsEnvelope(action, data, message) {
|
|
8589
8652
|
return {
|
|
8590
8653
|
success: true,
|
|
@@ -8846,9 +8909,13 @@ async function registerRagTools(server) {
|
|
|
8846
8909
|
?.docs;
|
|
8847
8910
|
server.registerTool?.("searchKnowledgeBase", {
|
|
8848
8911
|
title: "云开发知识库检索",
|
|
8849
|
-
description:
|
|
8912
|
+
description: `云开发知识库检索工具,支持 CloudBase 官方文档 (docs)、固定技能文档 (skill) 和 OpenAPI 文档 (openapi) 查询。
|
|
8850
8913
|
|
|
8851
|
-
|
|
8914
|
+
按场景选择 mode:
|
|
8915
|
+
- 不确定答案在哪、需要对官方文档做全文检索时:mode=docs + action=searchDocs(传 query 关键词)
|
|
8916
|
+
- 已知文档标题、层级路径或 URL 时:mode=docs + action=findByName(传 input)或 action=readDoc(传 docPath)
|
|
8917
|
+
- 需要某个场景的落地指南 / 最佳实践时:mode=skill + skillName
|
|
8918
|
+
- 需要 HTTP API 的接口定义时:mode=openapi + apiName
|
|
8852
8919
|
|
|
8853
8920
|
⚠️ 重要:当 CloudBase skills 处于禁用状态或当前 IDE 不支持 skill 文件读取时,必须使用 searchKnowledgeBase(mode=skill, skillName=...) 来获取 CloudBase 技能文档内容,而不是尝试直接读取 skill 文件。直接读取可能返回 400 错误。示例:
|
|
8854
8921
|
- 需要最小 Web+数据库 Demo 路径时:searchKnowledgeBase(mode=skill, skillName=minimal-web-baas-demo)
|
|
@@ -8886,37 +8953,13 @@ async function registerRagTools(server) {
|
|
|
8886
8953
|
.string()
|
|
8887
8954
|
.optional()
|
|
8888
8955
|
.describe("mode=docs 且 action=searchDocs 时指定。全文检索关键词。"),
|
|
8889
|
-
threshold: zod_1.z
|
|
8890
|
-
.number()
|
|
8891
|
-
.default(0.5)
|
|
8892
|
-
.optional()
|
|
8893
|
-
.describe("mode=vector 时指定。相似性检索阈值"),
|
|
8894
|
-
id: KnowledgeBaseEnum.default("cloudbase").optional().describe("mode=vector 时指定。知识库范围,默认 cloudbase。cloudbase=云开发全量知识,scf=云开发的云函数知识, miniprogram=小程序知识(不包含云开发与云函数知识)"),
|
|
8895
|
-
content: zod_1.z.string().describe("mode=vector 时指定。检索内容").optional(),
|
|
8896
|
-
options: zod_1.z
|
|
8897
|
-
.object({
|
|
8898
|
-
chunkExpand: zod_1.z
|
|
8899
|
-
.array(zod_1.z.number())
|
|
8900
|
-
.min(2)
|
|
8901
|
-
.max(2)
|
|
8902
|
-
.default([3, 3])
|
|
8903
|
-
.describe("指定返回的文档内容的展开长度,例如 [3,3]代表前后展开长度"),
|
|
8904
|
-
})
|
|
8905
|
-
.optional()
|
|
8906
|
-
.describe("mode=vector 时指定。其他选项"),
|
|
8907
|
-
limit: zod_1.z
|
|
8908
|
-
.number()
|
|
8909
|
-
.default(5)
|
|
8910
|
-
.optional()
|
|
8911
|
-
.describe("mode=vector 时指定。指定返回最相似的 Top K 的 K 的值"),
|
|
8912
8956
|
},
|
|
8913
8957
|
annotations: {
|
|
8914
8958
|
readOnlyHint: true,
|
|
8915
8959
|
openWorldHint: true,
|
|
8916
8960
|
category: "rag",
|
|
8917
8961
|
},
|
|
8918
|
-
}, async ({
|
|
8919
|
-
const chunkExpand = options?.chunkExpand ?? [3, 3];
|
|
8962
|
+
}, async ({ mode, skillName, apiName, action, moduleName, input, docPath, query, }) => {
|
|
8920
8963
|
if (mode === "docs") {
|
|
8921
8964
|
try {
|
|
8922
8965
|
const resolvedAction = action;
|
|
@@ -8996,76 +9039,8 @@ async function registerRagTools(server) {
|
|
|
8996
9039
|
],
|
|
8997
9040
|
};
|
|
8998
9041
|
}
|
|
8999
|
-
//
|
|
9000
|
-
|
|
9001
|
-
if (mode === "vector") {
|
|
9002
|
-
if (!content || !content.trim()) {
|
|
9003
|
-
throw new Error("检索内容不能为空: please provide non-empty `content` when mode=vector.");
|
|
9004
|
-
}
|
|
9005
|
-
}
|
|
9006
|
-
// 枚举到后端 id 映射
|
|
9007
|
-
const backendId = KnowledgeBaseIdMap[vectorKnowledgeBaseId] ||
|
|
9008
|
-
vectorKnowledgeBaseId;
|
|
9009
|
-
const signInRes = await fetch("https://tcb-advanced-a656fc.api.tcloudbasegateway.com/auth/v1/signin/anonymously", {
|
|
9010
|
-
method: "POST",
|
|
9011
|
-
headers: {
|
|
9012
|
-
"Content-Type": "application/json",
|
|
9013
|
-
Accept: "application/json",
|
|
9014
|
-
"x-device-id": "cloudbase-ai-toolkit",
|
|
9015
|
-
},
|
|
9016
|
-
body: safeStringify({
|
|
9017
|
-
collectionView: backendId,
|
|
9018
|
-
options: {
|
|
9019
|
-
chunkExpand,
|
|
9020
|
-
},
|
|
9021
|
-
search: {
|
|
9022
|
-
content: content,
|
|
9023
|
-
limit,
|
|
9024
|
-
},
|
|
9025
|
-
}),
|
|
9026
|
-
});
|
|
9027
|
-
const token = (await signInRes.json()).access_token;
|
|
9028
|
-
const res = await fetch(`https://tcb-advanced-a656fc.api.tcloudbasegateway.com/v1/knowledge/search`, {
|
|
9029
|
-
method: "POST",
|
|
9030
|
-
headers: {
|
|
9031
|
-
"Content-Type": "application/json",
|
|
9032
|
-
Authorization: `Bearer ${token}`,
|
|
9033
|
-
},
|
|
9034
|
-
body: safeStringify({
|
|
9035
|
-
collectionView: backendId,
|
|
9036
|
-
options: {
|
|
9037
|
-
chunkExpand,
|
|
9038
|
-
},
|
|
9039
|
-
search: {
|
|
9040
|
-
content: content,
|
|
9041
|
-
limit,
|
|
9042
|
-
},
|
|
9043
|
-
}),
|
|
9044
|
-
});
|
|
9045
|
-
const result = await res.json();
|
|
9046
|
-
if (result.code) {
|
|
9047
|
-
throw new Error(result.message);
|
|
9048
|
-
}
|
|
9049
|
-
return {
|
|
9050
|
-
content: [
|
|
9051
|
-
{
|
|
9052
|
-
type: "text",
|
|
9053
|
-
text: safeStringify(result.data.documents
|
|
9054
|
-
.filter((item) => item.score >= threshold)
|
|
9055
|
-
.map((item) => {
|
|
9056
|
-
return {
|
|
9057
|
-
score: item.score,
|
|
9058
|
-
fileTile: item.documentSet.fileTitle,
|
|
9059
|
-
url: safeParse(item.documentSet.fileMetaData).url,
|
|
9060
|
-
paragraphTitle: item.data.paragraphTitle,
|
|
9061
|
-
text: `${item.data.pre?.join("\n") || ""}
|
|
9062
|
-
${item.data.text}
|
|
9063
|
-
${item.data.next?.join("\n") || ""}`,
|
|
9064
|
-
};
|
|
9065
|
-
})),
|
|
9066
|
-
},
|
|
9067
|
-
],
|
|
9068
|
-
};
|
|
9042
|
+
// mode 是枚举,docs / skill / openapi 三个分支已在上面全部返回,这里不可达
|
|
9043
|
+
throw new Error(`unsupported mode: ${String(mode)}`);
|
|
9069
9044
|
});
|
|
9070
9045
|
}
|
|
9071
9046
|
function extractDescriptionFromFrontMatter(content) {
|
|
@@ -10837,7 +10812,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10837
10812
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10838
10813
|
};
|
|
10839
10814
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
10840
|
-
exports.CLOUDRUN_ENV_TYPE = exports.CLOUDRUN_DEPLOY_REGISTRATION_INTERVAL_MS = exports.CLOUDRUN_DEPLOY_REGISTRATION_MAX_WAIT_MS = exports.CLOUDRUN_PACKAGE_TYPES = exports.CLOUDRUN_ACCESS_TYPES = exports.CLOUDRUN_SERVICE_TYPES = void 0;
|
|
10815
|
+
exports.CLOUDRUN_ENV_TYPE = exports.CLOUDRUN_DEPLOY_REGISTRATION_INTERVAL_MS = exports.CLOUDRUN_DEPLOY_REGISTRATION_MAX_WAIT_MS = exports.MASKED_CLOUDRUN_ENV_VALUE = exports.CLOUDRUN_PACKAGE_TYPES = exports.CLOUDRUN_ACCESS_TYPES = exports.CLOUDRUN_SERVICE_TYPES = void 0;
|
|
10816
|
+
exports.maskCloudRunDetailEnvParams = maskCloudRunDetailEnvParams;
|
|
10841
10817
|
exports.buildManageCloudRunErrorMessage = buildManageCloudRunErrorMessage;
|
|
10842
10818
|
exports.isValidCloudRunBuildId = isValidCloudRunBuildId;
|
|
10843
10819
|
exports.isValidCloudRunRunId = isValidCloudRunRunId;
|
|
@@ -10871,6 +10847,40 @@ exports.CLOUDRUN_SERVICE_TYPES = ['function', 'container'];
|
|
|
10871
10847
|
exports.CLOUDRUN_ACCESS_TYPES = ['OA', 'PUBLIC', 'MINIAPP', 'VPC'];
|
|
10872
10848
|
// CloudRun env package types (CreateCloudRunEnv.PackageType)
|
|
10873
10849
|
exports.CLOUDRUN_PACKAGE_TYPES = ['Trial', 'Standard', 'Professional', 'Enterprise'];
|
|
10850
|
+
/** 环境变量脱敏后的占位值(不保留任何明文片段)。 */
|
|
10851
|
+
exports.MASKED_CLOUDRUN_ENV_VALUE = "***";
|
|
10852
|
+
/**
|
|
10853
|
+
* 对云托管 detail 返回的 ServerConfig.EnvParams(JSON 字符串)做脱敏:
|
|
10854
|
+
* 解析后把所有 value 置为占位符、保留 key,再序列化回 JSON 字符串。
|
|
10855
|
+
* 解析失败(非 JSON)时整串替换为占位符,避免任何明文片段外泄。
|
|
10856
|
+
* 用于默认工具返回,避免明文进入模型上下文。
|
|
10857
|
+
*/
|
|
10858
|
+
function maskCloudRunDetailEnvParams(detail) {
|
|
10859
|
+
const envParams = detail?.ServerConfig?.EnvParams;
|
|
10860
|
+
if (typeof envParams !== "string" || envParams === "") {
|
|
10861
|
+
return detail;
|
|
10862
|
+
}
|
|
10863
|
+
let masked;
|
|
10864
|
+
try {
|
|
10865
|
+
const parsed = JSON.parse(envParams);
|
|
10866
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
10867
|
+
masked = JSON.stringify(Object.fromEntries(Object.entries(parsed).map(([key]) => [key, exports.MASKED_CLOUDRUN_ENV_VALUE])));
|
|
10868
|
+
}
|
|
10869
|
+
else {
|
|
10870
|
+
masked = exports.MASKED_CLOUDRUN_ENV_VALUE;
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
catch {
|
|
10874
|
+
masked = exports.MASKED_CLOUDRUN_ENV_VALUE;
|
|
10875
|
+
}
|
|
10876
|
+
return {
|
|
10877
|
+
...detail,
|
|
10878
|
+
ServerConfig: {
|
|
10879
|
+
...detail.ServerConfig,
|
|
10880
|
+
EnvParams: masked,
|
|
10881
|
+
},
|
|
10882
|
+
};
|
|
10883
|
+
}
|
|
10874
10884
|
// Input schema for queryCloudRun tool
|
|
10875
10885
|
const queryCloudRunInputSchema = {
|
|
10876
10886
|
action: zod_1.z.enum(['list', 'detail', 'templates', 'getDeployLog', 'getProcessLog', 'getDeployRecords', 'envStatus']).describe('查询操作类型:list=获取云托管服务列表(支持分页和筛选),detail=查询指定服务的详细信息(包含服务配置和最新部署状态),templates=获取可用的项目模板列表(用于初始化新项目),getDeployLog=获取构建日志(仅云端源码构建有意义,走 CODING/DescribeCloudRunBuildLog;已有镜像部署无构建过程;未登录 CODING 的账号会报错),getProcessLog=获取运行日志(部署阶段步骤+容器启动/运行日志,走 tcbr/DescribeCloudRunProcessLog;镜像部署与源码构建均可用,不依赖 CODING;RunId 来自 detail/getDeployRecords 的 latestDeploy.RunId),getDeployRecords=获取指定服务的部署记录列表(按部署时间倒序,含 BuildId/RunId/FlowRatio/Status 等字段,用于查看历史发布与回滚上下文),envStatus=查询当前环境云托管是否已开通及开通状态(Status=creating开通中/normal已开通),用于initEnv之后轮询进度或deploy之前确认环境是否就绪'),
|
|
@@ -10884,6 +10894,7 @@ const queryCloudRunInputSchema = {
|
|
|
10884
10894
|
detailServerName: zod_1.z.string().optional().describe('要查询详细信息、部署记录、构建日志或运行日志的服务名称。当action为detail、getDeployLog、getProcessLog或getDeployRecords时建议提供,必须是已存在的服务名称。可通过list操作获取可用的服务名称列表'),
|
|
10885
10895
|
buildId: zod_1.z.number().optional().describe('构建ID,仅在action=getDeployLog时使用(构建日志,仅云端源码构建)。不传时默认返回最近一次部署的构建日志'),
|
|
10886
10896
|
runId: zod_1.z.string().optional().describe('运行ID(RunId),仅在action=getProcessLog时使用。不传时默认取该服务最近一次部署记录的 RunId(与 detail/getDeployRecords 的 latestDeploy.RunId 同源)。镜像部署与源码构建均可查询运行日志'),
|
|
10897
|
+
revealEnvParams: zod_1.z.boolean().optional().default(false).describe('是否返回服务环境变量(ServerConfig.EnvParams)明文值(仅 action=detail 时生效)。默认 false,值脱敏为 "***"(保留 key,足够排查配置了哪些变量、变更是否生效);true 时返回明文,敏感变量(如带密码的 DATABASE_URL)可能暴露给模型上下文,谨慎使用'),
|
|
10887
10898
|
};
|
|
10888
10899
|
// Input schema for manageCloudRun tool
|
|
10889
10900
|
const ManageCloudRunInputSchema = {
|
|
@@ -11749,7 +11760,8 @@ function registerCloudRunTools(server) {
|
|
|
11749
11760
|
text: JSON.stringify({
|
|
11750
11761
|
success: true,
|
|
11751
11762
|
data: {
|
|
11752
|
-
|
|
11763
|
+
// 默认脱敏 ServerConfig.EnvParams 的值(保留 key);revealEnvParams=true 时返回明文
|
|
11764
|
+
service: input.revealEnvParams === true ? result : maskCloudRunDetailEnvParams(result),
|
|
11753
11765
|
latestDeploy,
|
|
11754
11766
|
// 若最新部署记录带镜像信息(镜像部署),透出便于展示
|
|
11755
11767
|
...(extractCloudRunImageInfo(result, latestDeploy)
|
|
@@ -13182,8 +13194,9 @@ const SUPABASE_LIKE_SDK_HINTS = {
|
|
|
13182
13194
|
emailOtp: "auth.signInWithOtp({ email })",
|
|
13183
13195
|
password: "auth.signInWithPassword({ username|email|phone, password })",
|
|
13184
13196
|
signup: "auth.signUp({ phone|email, ... })",
|
|
13185
|
-
verifyOtp: "verifyOtp({ token })",
|
|
13197
|
+
verifyOtp: "const { data, error } = await auth.signInWithOtp({ email })\nconst { data: loginData, error: loginError } = await data.verifyOtp({ token })",
|
|
13186
13198
|
anonymous: "auth.signInAnonymously()",
|
|
13199
|
+
caution: "verifyOtp is a callback on the signInWithOtp / signUp result: call data.verifyOtp({ token }) with only the token. Do NOT call a standalone auth.verifyOtp({ token }) — it requires an extra messageId and fails with 'messageId is required' when the messageId is missing. Error 'messageId is required' means you used the standalone call instead of the callback.",
|
|
13187
13200
|
};
|
|
13188
13201
|
function buildErrorEnvelope(error) {
|
|
13189
13202
|
return {
|
|
@@ -13893,7 +13906,7 @@ class TelemetryReporter {
|
|
|
13893
13906
|
const arch = os_1.default.arch(); // 系统架构
|
|
13894
13907
|
// Build-time injected version; guard for vitest / non-webpack runtimes.
|
|
13895
13908
|
const mcpVersion = process.env.npm_package_version ||
|
|
13896
|
-
( true ? "2.32.
|
|
13909
|
+
( true ? "2.32.5" : 0);
|
|
13897
13910
|
return {
|
|
13898
13911
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
13899
13912
|
deviceId: this.deviceId,
|
|
@@ -14227,10 +14240,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14227
14240
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14228
14241
|
};
|
|
14229
14242
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
14230
|
-
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;
|
|
14243
|
+
exports.CUSTOM_IMAGE_RUNTIME = exports.MASKED_ENV_VALUE = 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;
|
|
14231
14244
|
exports.formatRuntimeList = formatRuntimeList;
|
|
14232
14245
|
exports.layerNameIncludesEnvId = layerNameIncludesEnvId;
|
|
14233
14246
|
exports.buildCreateLayerNameWarning = buildCreateLayerNameWarning;
|
|
14247
|
+
exports.maskFunctionDetailEnvValues = maskFunctionDetailEnvValues;
|
|
14234
14248
|
exports.validateTimerCron = validateTimerCron;
|
|
14235
14249
|
exports.pickManageFunctionName = pickManageFunctionName;
|
|
14236
14250
|
exports.shouldInstallDependencyForFunction = shouldInstallDependencyForFunction;
|
|
@@ -14355,8 +14369,33 @@ function buildCreateLayerNameWarning(layerName, envId) {
|
|
|
14355
14369
|
}
|
|
14356
14370
|
return exports.LAYER_SOFT_WARN.createNameFormat(envId);
|
|
14357
14371
|
}
|
|
14358
|
-
|
|
14359
|
-
|
|
14372
|
+
/** 环境变量脱敏后的占位值(不保留任何明文片段)。 */
|
|
14373
|
+
exports.MASKED_ENV_VALUE = "***";
|
|
14374
|
+
/**
|
|
14375
|
+
* 对 getFunctionDetail 返回的 Environment.Variables 做脱敏:
|
|
14376
|
+
* 保留 Key 与原始值长度(ValueLength),Value 置为占位符。
|
|
14377
|
+
* 用于默认工具返回与日志落盘,避免明文进入模型上下文 / 持久化日志。
|
|
14378
|
+
*/
|
|
14379
|
+
function maskFunctionDetailEnvValues(detail) {
|
|
14380
|
+
const variables = detail?.Environment?.Variables;
|
|
14381
|
+
if (!Array.isArray(variables) || variables.length === 0) {
|
|
14382
|
+
return detail;
|
|
14383
|
+
}
|
|
14384
|
+
return {
|
|
14385
|
+
...detail,
|
|
14386
|
+
Environment: {
|
|
14387
|
+
...detail.Environment,
|
|
14388
|
+
Variables: variables.map((item) => ({
|
|
14389
|
+
...item,
|
|
14390
|
+
Value: exports.MASKED_ENV_VALUE,
|
|
14391
|
+
...(typeof item.Value === "string"
|
|
14392
|
+
? { ValueLength: item.Value.length }
|
|
14393
|
+
: {}),
|
|
14394
|
+
})),
|
|
14395
|
+
},
|
|
14396
|
+
};
|
|
14397
|
+
}
|
|
14398
|
+
const VPC_SCHEMA = zod_1.z.object({ vpcId: zod_1.z
|
|
14360
14399
|
.string()
|
|
14361
14400
|
.describe("VPC ID from the real database/network console (e.g. vpc-xxxxxxxx). Required for non-native TCP DB access. Do NOT invent or use placeholders."),
|
|
14362
14401
|
subnetId: zod_1.z
|
|
@@ -14726,15 +14765,19 @@ function registerFunctionTools(server) {
|
|
|
14726
14765
|
}
|
|
14727
14766
|
const cloudbase = await getManager();
|
|
14728
14767
|
const result = await cloudbase.functions.getFunctionDetail(input.functionName, input.codeSecret);
|
|
14729
|
-
|
|
14768
|
+
// 日志侧一律脱敏(持久化,不提供明文口子);工具返回侧按 revealEnvValues 决定
|
|
14769
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, maskFunctionDetailEnvValues(result));
|
|
14770
|
+
const functionDetail = input.revealEnvValues === true
|
|
14771
|
+
? result
|
|
14772
|
+
: maskFunctionDetailEnvValues(result);
|
|
14730
14773
|
return buildEnvelope({
|
|
14731
14774
|
action: input.action,
|
|
14732
14775
|
functionName: input.functionName,
|
|
14733
|
-
functionDetail
|
|
14776
|
+
functionDetail,
|
|
14734
14777
|
layers: normalizeFunctionLayers(result.Layers),
|
|
14735
14778
|
triggers: result.Triggers || [],
|
|
14736
14779
|
requestId: result.RequestId,
|
|
14737
|
-
raw:
|
|
14780
|
+
raw: functionDetail,
|
|
14738
14781
|
}, `已获取函数 ${input.functionName} 的详情`, [
|
|
14739
14782
|
{
|
|
14740
14783
|
tool: "queryFunctions",
|
|
@@ -14952,13 +14995,16 @@ function registerFunctionTools(server) {
|
|
|
14952
14995
|
}
|
|
14953
14996
|
const cloudbase = await getManager();
|
|
14954
14997
|
const result = await cloudbase.functions.getFunctionDetail(input.functionName, input.codeSecret);
|
|
14955
|
-
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
14998
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, maskFunctionDetailEnvValues(result));
|
|
14999
|
+
const detail = input.revealEnvValues === true
|
|
15000
|
+
? result
|
|
15001
|
+
: maskFunctionDetailEnvValues(result);
|
|
14956
15002
|
return buildEnvelope({
|
|
14957
15003
|
action: input.action,
|
|
14958
15004
|
functionName: input.functionName,
|
|
14959
|
-
triggers:
|
|
14960
|
-
requestId:
|
|
14961
|
-
raw:
|
|
15005
|
+
triggers: detail.Triggers || [],
|
|
15006
|
+
requestId: detail.RequestId,
|
|
15007
|
+
raw: detail,
|
|
14962
15008
|
}, `已获取函数 ${input.functionName} 的触发器列表`, [
|
|
14963
15009
|
{
|
|
14964
15010
|
tool: "manageFunctions",
|
|
@@ -15712,6 +15758,10 @@ function registerFunctionTools(server) {
|
|
|
15712
15758
|
limit: zod_1.z.number().optional().describe("分页数量(limit)。列表类 action 可选,默认值由后端决定"),
|
|
15713
15759
|
offset: zod_1.z.number().optional().describe("分页偏移(offset)。列表类 action 可选,默认 0"),
|
|
15714
15760
|
codeSecret: zod_1.z.string().optional().describe("代码保护密钥,用于解密函数代码"),
|
|
15761
|
+
revealEnvValues: zod_1.z
|
|
15762
|
+
.boolean()
|
|
15763
|
+
.optional()
|
|
15764
|
+
.describe("getFunctionDetail / listFunctionTriggers 时是否返回环境变量明文值。默认 false:Value 脱敏为 ***,仅保留 Key 与 ValueLength,足以确认配置了哪些变量及变更是否生效;true 时返回明文,敏感变量会进入模型上下文,谨慎使用。如需查看明文,建议优先使用控制台或 CLI"),
|
|
15715
15765
|
startTime: zod_1.z
|
|
15716
15766
|
.string()
|
|
15717
15767
|
.optional()
|
|
@@ -17269,7 +17319,7 @@ async function handleReadOnlySql(args, context, deps) {
|
|
|
17269
17319
|
message: `PostgreSQL read-only SQL execution failed: ${reason}`,
|
|
17270
17320
|
data: {
|
|
17271
17321
|
role: context.role,
|
|
17272
|
-
sqlPreview:
|
|
17322
|
+
sqlPreview: args.sql.trim().slice(0, 500),
|
|
17273
17323
|
},
|
|
17274
17324
|
});
|
|
17275
17325
|
}
|
|
@@ -19812,7 +19862,7 @@ function registerSetupTools(server) {
|
|
|
19812
19862
|
server.registerTool("downloadTemplate", {
|
|
19813
19863
|
title: "下载项目模板",
|
|
19814
19864
|
description: `自动下载并部署CloudBase项目模板。
|
|
19815
|
-
**Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.32.
|
|
19865
|
+
**Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.32.5" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
19816
19866
|
inputSchema: {
|
|
19817
19867
|
template: zod_1.z
|
|
19818
19868
|
.enum(["react", "vue", "miniprogram", "uniapp", "rules"])
|
|
@@ -21428,10 +21478,33 @@ function buildFailureResult(action, error) {
|
|
|
21428
21478
|
message: enrichRateLimitMessage(error instanceof Error ? error.message : String(error)),
|
|
21429
21479
|
});
|
|
21430
21480
|
}
|
|
21481
|
+
function extractFileList(files) {
|
|
21482
|
+
// hosting.listFiles 直接返回文件数组
|
|
21483
|
+
if (Array.isArray(files))
|
|
21484
|
+
return files;
|
|
21485
|
+
// hosting.findFiles 返回 COS 风格对象,文件列表在 Contents 里
|
|
21486
|
+
if (typeof files === 'object' && files !== null) {
|
|
21487
|
+
const record = files;
|
|
21488
|
+
if (Array.isArray(record.Contents))
|
|
21489
|
+
return record.Contents;
|
|
21490
|
+
if (Array.isArray(record.contents))
|
|
21491
|
+
return record.contents;
|
|
21492
|
+
}
|
|
21493
|
+
return [];
|
|
21494
|
+
}
|
|
21495
|
+
function extractFilePagination(result) {
|
|
21496
|
+
if (typeof result !== 'object' || result === null)
|
|
21497
|
+
return {};
|
|
21498
|
+
const record = result;
|
|
21499
|
+
const isTruncated = record.IsTruncated ?? record.isTruncated;
|
|
21500
|
+
const nextMarker = record.NextMarker ?? record.nextMarker;
|
|
21501
|
+
return {
|
|
21502
|
+
isTruncated: typeof isTruncated === 'boolean' ? isTruncated : undefined,
|
|
21503
|
+
nextMarker: typeof nextMarker === 'string' && nextMarker.length > 0 ? nextMarker : undefined,
|
|
21504
|
+
};
|
|
21505
|
+
}
|
|
21431
21506
|
function normalizeFileFields(files) {
|
|
21432
|
-
|
|
21433
|
-
return [];
|
|
21434
|
-
return files.map(file => {
|
|
21507
|
+
return extractFileList(files).map((file) => {
|
|
21435
21508
|
if (typeof file !== 'object' || file === null)
|
|
21436
21509
|
return file;
|
|
21437
21510
|
const record = file;
|
|
@@ -21520,6 +21593,8 @@ function registerHostingTools(server) {
|
|
|
21520
21593
|
});
|
|
21521
21594
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
21522
21595
|
const normalizedFiles = normalizeFileFields(result);
|
|
21596
|
+
// findFiles 的 marker 是 COS 游标字符串,与 listFiles 的数字 offset 语义不同,此处只透传不做解析
|
|
21597
|
+
const { isTruncated, nextMarker } = extractFilePagination(result);
|
|
21523
21598
|
return (0, tool_result_js_1.buildJsonToolResult)({
|
|
21524
21599
|
success: true,
|
|
21525
21600
|
data: {
|
|
@@ -21528,9 +21603,11 @@ function registerHostingTools(server) {
|
|
|
21528
21603
|
marker: input.marker,
|
|
21529
21604
|
maxKeys: input.maxKeys,
|
|
21530
21605
|
files: normalizedFiles,
|
|
21606
|
+
nextMarker,
|
|
21607
|
+
isTruncated,
|
|
21531
21608
|
result,
|
|
21532
21609
|
},
|
|
21533
|
-
message: `已按前缀 \`${input.prefix}\` 查询静态托管文件,共 ${
|
|
21610
|
+
message: `已按前缀 \`${input.prefix}\` 查询静态托管文件,共 ${normalizedFiles.length} 个。${nextMarker ? ' 还有更多文件,可使用 nextMarker 继续查询。' : ''}`,
|
|
21534
21611
|
});
|
|
21535
21612
|
}
|
|
21536
21613
|
case 'listFiles': {
|
|
@@ -22367,7 +22444,13 @@ async function peekLoginState(options) {
|
|
|
22367
22444
|
try {
|
|
22368
22445
|
// 优先使用 IDE 注入的工作目录,其次 process.cwd()
|
|
22369
22446
|
const projectCwd = process.env.WORKSPACE_FOLDER_PATHS || process.cwd();
|
|
22370
|
-
|
|
22447
|
+
// 换取网关按站点选型:显式 intl → ap-singapore;domestic/歧义 → toolbox 默认
|
|
22448
|
+
// ap-shanghai(国内站多地域环境均经其全局路由,详见 resolveApiKeyExchangeRegion)
|
|
22449
|
+
const exchangeRegion = (0, site_map_js_1.resolveApiKeyExchangeRegion)({
|
|
22450
|
+
site: options?.site,
|
|
22451
|
+
region: options?.region,
|
|
22452
|
+
});
|
|
22453
|
+
const credential = await auth.loginByApiKey(envVarLoginState.apiKey, envVarLoginState.envId, { cwd: projectCwd, ...(exchangeRegion ? { region: exchangeRegion } : {}) });
|
|
22371
22454
|
return credential;
|
|
22372
22455
|
}
|
|
22373
22456
|
catch (e) {
|
|
@@ -22462,6 +22545,18 @@ async function ensureLogin(options) {
|
|
|
22462
22545
|
if (resolvedAuthOptions.oauthEndpoint) {
|
|
22463
22546
|
loginOptions.getOAuthEndpoint = () => resolvedAuthOptions.oauthEndpoint;
|
|
22464
22547
|
}
|
|
22548
|
+
else if (resolvedSite === "intl") {
|
|
22549
|
+
// 国际站 OAuth 后端独立部署(2026-09-01 实测 device/code+token 可用,注册表与国内站隔离);
|
|
22550
|
+
// 不覆写时 toolbox 默认打国内站端点,国际站账号无法完成授权
|
|
22551
|
+
loginOptions.getOAuthEndpoint = () => site_map_js_1.SITE_REGION_MAP.intl.oauthEndpoint;
|
|
22552
|
+
}
|
|
22553
|
+
if (resolvedSite === "intl") {
|
|
22554
|
+
// device 模式的 verification_uri 标准模式下由客户端拼接,指向国内站授权页;
|
|
22555
|
+
// 与 web 模式同款 host 改写,把授权页切到国际站
|
|
22556
|
+
loginOptions.getAuthUrl = (url) => url
|
|
22557
|
+
.replace(`${site_map_js_1.SITE_REGION_MAP.domestic.authHost}/dev`, `${site_map_js_1.SITE_REGION_MAP.intl.authHost}/dev`)
|
|
22558
|
+
.replace(site_map_js_1.SITE_REGION_MAP.domestic.authHost, site_map_js_1.SITE_REGION_MAP.intl.authHost);
|
|
22559
|
+
}
|
|
22465
22560
|
if (resolvedAuthOptions.oauthCustom) {
|
|
22466
22561
|
loginOptions.custom = true;
|
|
22467
22562
|
}
|
|
@@ -23560,6 +23655,7 @@ exports.TCB_QUERY_REGIONS = exports.SITE_IDS = exports.SITE_REGION_MAP = void 0;
|
|
|
23560
23655
|
exports.isSiteId = isSiteId;
|
|
23561
23656
|
exports.normalizeSite = normalizeSite;
|
|
23562
23657
|
exports.getSite = getSite;
|
|
23658
|
+
exports.resolveApiKeyExchangeRegion = resolveApiKeyExchangeRegion;
|
|
23563
23659
|
exports.resolveSite = resolveSite;
|
|
23564
23660
|
exports.resolveSiteAndRegion = resolveSiteAndRegion;
|
|
23565
23661
|
const project_config_js_1 = __webpack_require__(8445);
|
|
@@ -23569,6 +23665,7 @@ exports.SITE_REGION_MAP = {
|
|
|
23569
23665
|
label: "国内站",
|
|
23570
23666
|
authHost: "tcb.cloud.tencent.com",
|
|
23571
23667
|
consoleHost: "tcb.cloud.tencent.com",
|
|
23668
|
+
oauthEndpoint: "https://tcb-api.cloud.tencent.com/qcloud-tcb/v1/oauth",
|
|
23572
23669
|
defaultRegion: "ap-shanghai",
|
|
23573
23670
|
regions: ["ap-shanghai", "ap-guangzhou", "ap-singapore"],
|
|
23574
23671
|
capabilities: { noSql: true },
|
|
@@ -23578,6 +23675,9 @@ exports.SITE_REGION_MAP = {
|
|
|
23578
23675
|
label: "国际站",
|
|
23579
23676
|
authHost: "tcb.tencentcloud.com",
|
|
23580
23677
|
consoleHost: "tcb.tencentcloud.com",
|
|
23678
|
+
// 2026-09-01 实测:device/code + token 端点可用,且 device-code 注册表与国内站隔离
|
|
23679
|
+
// (国内站有效期内的 code 在该端点轮询返回 expired_token)
|
|
23680
|
+
oauthEndpoint: "https://tcb-api.tencentcloud.com/qcloud-tcb/v1/oauth",
|
|
23581
23681
|
defaultRegion: "ap-singapore",
|
|
23582
23682
|
regions: ["ap-singapore"],
|
|
23583
23683
|
capabilities: { noSql: false },
|
|
@@ -23632,8 +23732,24 @@ function getSite(region, explicitSite) {
|
|
|
23632
23732
|
return "domestic";
|
|
23633
23733
|
}
|
|
23634
23734
|
/**
|
|
23635
|
-
*
|
|
23735
|
+
* API Key 换取网关(POST /capi/credential 的 host)地域选型。
|
|
23736
|
+
*
|
|
23737
|
+
* 2026-09-01 同一把国内站 key 三地域网关实测:
|
|
23738
|
+
* - ap-shanghai / ap-guangzhou host 均换取成功(国内站集群共享 key 注册表,
|
|
23739
|
+
* URL 中的 region 段在国内站内部等价,互换可用)
|
|
23740
|
+
* - ap-singapore host 拒绝(SIGN_PARAM_INVALID:sg 网关为国际站独立部署,
|
|
23741
|
+
* key 注册表与国内站隔离)
|
|
23742
|
+
*
|
|
23743
|
+
* 因此换取 host 按**站点**选择,而非按环境所属地域:
|
|
23744
|
+
* - 显式 intl 站点(如 TCB_SITE=intl)→ 返回 intl 地域(ap-singapore)
|
|
23745
|
+
* - domestic / 歧义(如仅设 TCB_REGION=ap-singapore)/ 未配置 → 返回 undefined,
|
|
23746
|
+
* toolbox 回落默认 ap-shanghai;国内站多地域环境(ap-guangzhou / ap-singapore)
|
|
23747
|
+
* 经默认域名均可换取,盲目传环境地域反而会把国内站 key 打到 sg 网关导致换取失败
|
|
23636
23748
|
*/
|
|
23749
|
+
function resolveApiKeyExchangeRegion(opts = {}) {
|
|
23750
|
+
const resolved = resolveSiteAndRegion(opts);
|
|
23751
|
+
return resolved.site === "intl" && !resolved.ambiguous ? resolved.region : undefined;
|
|
23752
|
+
}
|
|
23637
23753
|
function resolveSite(region, explicitSite) {
|
|
23638
23754
|
const site = getSite(region, explicitSite);
|
|
23639
23755
|
return site === "ambiguous" ? "intl" : site;
|