@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/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
- ? `当前为环境级 API Key 登录(单环境权限)。只能访问已绑定的 envId${pinnedEnvId ? ` ${pinnedEnvId}` : ""},看不到账号下其他环境或其他地域。这是凭据权限边界,不是环境不存在。`
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. 先调用 auth(action=\"status\") 检查当前登录状态");
1228
- suggestions.push("2. 如果未登录,调用 auth(action=\"start_auth\", authMode=\"device\") 完成登录");
1229
- suggestions.push("3. 登录完成后再次调用 queryEnv(action=\"list\")");
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 || `https://${process.env.CLOUDBASE_ENV_ID}.ap-shanghai.tcb-api.tencentcloudapi.com`;
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
- const envIdFromEnv = !cloudBaseOptions?.requestFn && process.env.CLOUDBASE_ENV_ID;
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() || (!region && !alias && !envId)));
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.3" : 0)}
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.3" : 0;
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 KnowledgeBaseEnum = zod_1.z.enum(["cloudbase", "scf", "miniprogram"]);
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: `云开发知识库智能检索工具,支持向量查询 (vector)、固定技能文档 (skill)、OpenAPI 文档 (openapi) 和 CloudBase 官方文档 (docs) 查询。
8912
+ description: `云开发知识库检索工具,支持 CloudBase 官方文档 (docs)、固定技能文档 (skill) 和 OpenAPI 文档 (openapi) 查询。
8850
8913
 
8851
- 强烈推荐始终优先使用固定技能文档 (skill)、OpenAPI 文档 (openapi) 或 CloudBase 官方文档 (docs) 模式进行检索,仅当固定文档无法覆盖你的问题时,再使用向量查询 (vector) 模式。
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 ({ id, content, options, limit = 5, threshold = 0.5, mode, skillName, apiName, action, moduleName, input, docPath, query, }) => {
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
- // 向量检索模式下必须提供 id 和 content,避免后端报「知识库名称不能为空」
9000
- const vectorKnowledgeBaseId = id ?? "cloudbase";
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
- service: result,
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.3" : 0);
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
- const VPC_SCHEMA = zod_1.z.object({
14359
- vpcId: zod_1.z
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
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
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: result,
14776
+ functionDetail,
14734
14777
  layers: normalizeFunctionLayers(result.Layers),
14735
14778
  triggers: result.Triggers || [],
14736
14779
  requestId: result.RequestId,
14737
- raw: result,
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: result.Triggers || [],
14960
- requestId: result.RequestId,
14961
- raw: result,
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: limitedSql.trim().slice(0, 500),
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.3" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
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
- if (!Array.isArray(files))
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}\` 查询静态托管文件,共 ${Array.isArray(normalizedFiles) ? normalizedFiles.length : 0} 个。`,
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
- const credential = await auth.loginByApiKey(envVarLoginState.apiKey, envVarLoginState.envId, { cwd: projectCwd });
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
- * 认证/路由场景下解析站点:歧义(如 ap-singapore 未配 site)时默认 intl,保持既有国际站行为。
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;