@cloudbase/cloudbase-mcp 2.18.0 → 2.19.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.cjs CHANGED
@@ -1892,12 +1892,71 @@ function simplifyEnvDomains(domains) {
1892
1892
  }
1893
1893
  const source = domain;
1894
1894
  return {
1895
+ ...(source.Id !== undefined ? { Id: source.Id } : {}),
1895
1896
  ...(source.Domain !== undefined ? { Domain: source.Domain } : {}),
1897
+ ...(source.CreateTime !== undefined ? { CreateTime: source.CreateTime } : {}),
1898
+ ...(source.UpdateTime !== undefined ? { UpdateTime: source.UpdateTime } : {}),
1896
1899
  ...(source.Status !== undefined ? { Status: source.Status } : {}),
1897
1900
  ...(source.Type !== undefined ? { Type: source.Type } : {}),
1898
1901
  };
1899
1902
  });
1900
1903
  }
1904
+ function buildEnvDomainManagementResult(params) {
1905
+ const { action, domains, result } = params;
1906
+ const rawResult = result && typeof result === "object" && !Array.isArray(result)
1907
+ ? result
1908
+ : { result };
1909
+ if (action === "create") {
1910
+ return {
1911
+ ...rawResult,
1912
+ ok: true,
1913
+ code: "DOMAIN_UPDATE_PENDING",
1914
+ operation: action,
1915
+ targetDomains: domains,
1916
+ asyncState: "PENDING",
1917
+ message: '安全域名已提交添加请求。该变更通常需要约 10 分钟传播,请继续轮询 envQuery(action="domains"),直到目标域名状态为 ENABLE。',
1918
+ propagation: {
1919
+ requiresPolling: true,
1920
+ pollTool: "envQuery",
1921
+ pollAction: "domains",
1922
+ pollIntervalSuggestionSeconds: 10,
1923
+ timeoutSuggestionSeconds: 600,
1924
+ successCondition: '目标域名出现在 envQuery(action="domains") 返回中,且 Status 为 ENABLE。',
1925
+ },
1926
+ next_step: {
1927
+ tool: "envQuery",
1928
+ action: "domains",
1929
+ suggested_args: {
1930
+ action: "domains",
1931
+ },
1932
+ },
1933
+ };
1934
+ }
1935
+ return {
1936
+ ...rawResult,
1937
+ ok: true,
1938
+ code: "DOMAIN_DELETE_PENDING",
1939
+ operation: action,
1940
+ targetDomains: domains,
1941
+ asyncState: "PENDING",
1942
+ message: '安全域名已提交删除请求。该变更可能需要数分钟传播,请继续轮询 envQuery(action="domains"),直到目标域名不再出现。',
1943
+ propagation: {
1944
+ requiresPolling: true,
1945
+ pollTool: "envQuery",
1946
+ pollAction: "domains",
1947
+ pollIntervalSuggestionSeconds: 10,
1948
+ timeoutSuggestionSeconds: 600,
1949
+ successCondition: '目标域名不再出现在 envQuery(action="domains") 返回中。',
1950
+ },
1951
+ next_step: {
1952
+ tool: "envQuery",
1953
+ action: "domains",
1954
+ suggested_args: {
1955
+ action: "domains",
1956
+ },
1957
+ },
1958
+ };
1959
+ }
1901
1960
  function formatDeviceAuthHint(deviceAuthInfo) {
1902
1961
  if (!deviceAuthInfo) {
1903
1962
  return "";
@@ -2234,6 +2293,49 @@ function normalizeOptionalToolString(value) {
2234
2293
  ? value.trim()
2235
2294
  : undefined;
2236
2295
  }
2296
+ /**
2297
+ * Build enhanced error message for envQuery tool errors
2298
+ * Provides actionable guidance based on error patterns
2299
+ */
2300
+ function buildEnvQueryErrorMessage(error, action) {
2301
+ const baseMessage = error instanceof Error ? error.message : String(error);
2302
+ // Check for common error patterns and provide specific guidance
2303
+ const hasInvalidParameterError = /400|invalid parameter|invalid argument|parameter value/i.test(baseMessage);
2304
+ const hasAuthError = /未登录|auth required|unauthorized|authentication|credential|token|secret/i.test(baseMessage);
2305
+ const hasNetworkError = /ECONNRESET|socket hang up|ETIMEDOUT|ENOTFOUND|timeout/i.test(baseMessage);
2306
+ const hasPermissionError = /permission|denied|forbidden|无权|拒绝/i.test(baseMessage);
2307
+ const hasEnvNotFoundError = /env|environment|环境.*不存在|not found/i.test(baseMessage);
2308
+ const suggestions = [];
2309
+ if (hasInvalidParameterError) {
2310
+ suggestions.push("参数错误:可能是认证信息无效或已过期,请尝试以下步骤:");
2311
+ suggestions.push("1. 先调用 auth(action=\"status\") 检查当前登录状态");
2312
+ suggestions.push("2. 如果未登录,调用 auth(action=\"start_auth\", authMode=\"device\") 完成登录");
2313
+ suggestions.push("3. 登录完成后再次调用 envQuery(action=\"list\")");
2314
+ }
2315
+ if (hasAuthError) {
2316
+ suggestions.push("认证错误:当前未登录或认证已过期。");
2317
+ suggestions.push("建议先执行 auth(action=\"status\") 查看状态,然后按提示完成登录。");
2318
+ }
2319
+ if (hasPermissionError) {
2320
+ suggestions.push("权限错误:当前账号可能没有访问该资源的权限。");
2321
+ suggestions.push("请确认:1) 已选择正确的环境 2) 账号有对应权限");
2322
+ }
2323
+ if (hasEnvNotFoundError) {
2324
+ suggestions.push("环境错误:指定的环境不存在或无法访问。");
2325
+ suggestions.push("请使用 envQuery(action=\"list\") 查看可用的环境列表。");
2326
+ }
2327
+ if (hasNetworkError) {
2328
+ suggestions.push("网络错误:请检查网络连接,稍后重试。");
2329
+ }
2330
+ // If no specific pattern matched, provide general guidance
2331
+ if (suggestions.length === 0) {
2332
+ suggestions.push("查询环境信息时出错,建议:");
2333
+ suggestions.push("1. 先调用 auth(action=\"status\") 确认登录状态");
2334
+ suggestions.push("2. 如未登录,执行 auth(action=\"start_auth\") 完成认证");
2335
+ suggestions.push("3. 确认环境 ID 正确且可访问");
2336
+ }
2337
+ return `[envQuery/${action}] 调用失败: ${baseMessage}\n\n解决建议:\n${suggestions.join("\n")}`;
2338
+ }
2237
2339
  function normalizeOptionalToolBoolean(value) {
2238
2340
  return typeof value === "boolean" ? value : undefined;
2239
2341
  }
@@ -2698,17 +2800,17 @@ function registerEnvTools(server) {
2698
2800
  });
2699
2801
  }
2700
2802
  });
2701
- // envQuery - 环境查询(合并 listEnvs + getEnvInfo + getEnvAuthDomains + getWebsiteConfig)
2803
+ // envQuery - 环境查询(合并 listEnvs + getEnvInfo + getEnvAuthDomains)
2702
2804
  server.registerTool?.("envQuery", {
2703
2805
  title: "环境查询",
2704
- description: "查询云开发环境相关信息,支持查询环境列表、当前环境信息、安全域名和静态网站托管配置。(原工具名:listEnvs/getEnvInfo/getEnvAuthDomains/getWebsiteConfig,为兼容旧AI规则可继续使用这些名称)当 action=list 时,标准返回字段为 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,并支持通过 fields 白名单裁剪这些字段;aliasExact=true 时会按别名精确筛选,避免把前缀相近的环境误当作候选;即使传入 envId,action=list 也只返回摘要,不会返回完整资源明细或 expiry。如需查询某个已知环境的详细信息,请使用 action=info。action=info 会在可用时补充 BillingInfo(如 ExpireTime、PayMode、IsAutoRenew 等计费字段)。",
2806
+ description: "查询云开发环境相关信息,支持查询环境列表、指定环境详情和安全域名。(原工具名:listEnvs/getEnvInfo/getEnvAuthDomains,为兼容旧AI规则可继续使用这些名称)当 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 等计费字段)。",
2705
2807
  inputSchema: {
2706
2808
  action: zod_1.z
2707
- .enum(["list", "info", "domains", "hosting"])
2708
- .describe("查询类型:list=环境列表/摘要筛选(即使传 envId 也只返回 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,不支持 expiry),info=当前环境详细信息(详情中可查看更完整资源字段),domains=安全域名列表,hosting=静态网站托管配置"),
2809
+ .enum(["list", "info", "domains"])
2810
+ .describe("查询类型:list=环境列表/摘要筛选(按 DescribeEnvs 语义筛选,支持通过 envId 筛选,返回 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,不支持 expiry),info=指定环境的详细信息(必须传入 envId,返回资源字段和计费信息),domains=安全域名列表"),
2709
2811
  alias: zod_1.z.string().optional().describe("按环境别名筛选。action=list 时可选"),
2710
2812
  aliasExact: zod_1.z.boolean().optional().describe("按环境别名精确筛选。action=list 时可选;与 alias 配合使用"),
2711
- envId: zod_1.z.string().optional().describe("按环境 ID 精确筛选。action=list 时可选;注意 list + envId 仍只返回摘要,如需该环境详情请改用 action=info"),
2813
+ envId: zod_1.z.string().optional().describe("按环境 ID 筛选。action=list 时可选(仅按 DescribeEnvs 语义做筛选,仍返回摘要);action=info 时必填(返回该环境的详细信息,包含资源字段和计费信息)。如果任务已经给出了明确的 EnvId 并要求查询详情,请直接使用 action=info + envId,而不是 action=list"),
2712
2814
  limit: zod_1.z.number().int().positive().optional().describe("返回数量上限。action=list 时可选"),
2713
2815
  offset: zod_1.z.number().int().min(0).optional().describe("分页偏移。action=list 时可选"),
2714
2816
  fields: zod_1.z
@@ -2775,14 +2877,12 @@ function registerEnvTools(server) {
2775
2877
  return toolPayloadResult;
2776
2878
  }
2777
2879
  (0, logger_js_1.debug)("降级到 listEnvs() 也失败:", fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)));
2880
+ const enhancedMessage = buildEnvQueryErrorMessage(fallbackError, "list");
2778
2881
  return {
2779
2882
  content: [
2780
2883
  {
2781
2884
  type: "text",
2782
- text: "获取环境列表时出错: " +
2783
- (fallbackError instanceof Error
2784
- ? fallbackError.message
2785
- : String(fallbackError)),
2885
+ text: enhancedMessage,
2786
2886
  },
2787
2887
  ],
2788
2888
  };
@@ -2847,29 +2947,6 @@ function registerEnvTools(server) {
2847
2947
  };
2848
2948
  }
2849
2949
  break;
2850
- case "hosting": {
2851
- const cloudbaseHosting = await getManager();
2852
- const websiteConfig = await cloudbaseHosting.hosting.getWebsiteConfig();
2853
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, websiteConfig);
2854
- const hostingResult = {
2855
- ...websiteConfig,
2856
- CdnDomain: websiteConfig.CdnDomain ?? null,
2857
- Bucket: websiteConfig.Bucket ?? null,
2858
- };
2859
- try {
2860
- const envInfo = await cloudbaseHosting.env.getEnvInfo();
2861
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, envInfo);
2862
- hostingResult.CdnDomain = envInfo.EnvInfo?.StaticStorages?.[0]?.StaticDomain ?? hostingResult.CdnDomain;
2863
- hostingResult.Bucket = envInfo.EnvInfo?.StaticStorages?.[0]?.Bucket ?? hostingResult.Bucket;
2864
- }
2865
- catch (hostingInfoError) {
2866
- (0, logger_js_1.debug)("Failed to enrich hosting envQuery result with env info", {
2867
- error: hostingInfoError,
2868
- });
2869
- }
2870
- result = hostingResult;
2871
- break;
2872
- }
2873
2950
  default:
2874
2951
  throw new Error(`不支持的查询类型: ${action}`);
2875
2952
  }
@@ -2888,11 +2965,12 @@ function registerEnvTools(server) {
2888
2965
  if (toolPayloadResult) {
2889
2966
  return toolPayloadResult;
2890
2967
  }
2968
+ const enhancedMessage = buildEnvQueryErrorMessage(error, action);
2891
2969
  return {
2892
2970
  content: [
2893
2971
  {
2894
2972
  type: "text",
2895
- text: `环境查询失败: ${error instanceof Error ? error.message : String(error)}`,
2973
+ text: enhancedMessage,
2896
2974
  },
2897
2975
  ],
2898
2976
  };
@@ -2900,13 +2978,13 @@ function registerEnvTools(server) {
2900
2978
  });
2901
2979
  // envDomainManagement - 环境域名管理(合并 createEnvDomain + deleteEnvDomain)
2902
2980
  server.registerTool?.("envDomainManagement", {
2903
- title: "环境域名管理",
2904
- description: "管理云开发环境的安全域名,支持添加和删除操作。(原工具名:createEnvDomain/deleteEnvDomain,为兼容旧AI规则可继续使用这些名称)当浏览器 Web 应用需要从本地 Vite / dev server 或自定义域名直接访问 CloudBase 资源时,应先用 envQuery(action=domains) 检查当前实际浏览器 origin 对应的 host:port 是否已在白名单中,再按该实际值添加。",
2981
+ title: "环境域名管理(安全域名 / CORS 白名单)",
2982
+ description: "管理云开发环境的安全域名(安全域名 / CORS 白名单),支持添加和删除操作。(原工具名:createEnvDomain/deleteEnvDomain,为兼容旧AI规则可继续使用这些名称)当浏览器 Web 应用需要从本地 Vite / dev server 直接访问 CloudBase 资源时,应先用 envQuery(action=domains) 检查当前实际浏览器 origin 对应的 host:port 是否已在白名单中,再按该实际值添加。新增或删除后通常需要继续轮询 envQuery(action=domains) 确认状态收敛;安全域名一般约 10 分钟生效。⚠️ 重要:此工具仅用于 CORS/请求来源验证,不涉及 SSL 证书。如需绑定自定义域名供公网 HTTPS 访问,请使用 manageGateway(action=\"bindCustomDomain\")。",
2905
2983
  inputSchema: {
2906
2984
  action: zod_1.z
2907
2985
  .enum(["create", "delete"])
2908
- .describe("操作类型:create=添加域名,delete=删除域名"),
2909
- domains: zod_1.z.array(zod_1.z.string()).describe("安全域名数组"),
2986
+ .describe("操作类型:create=添加安全域名,delete=删除安全域名"),
2987
+ domains: zod_1.z.array(zod_1.z.string()).describe("安全域名数组(格式:host:port,例如 localhost:5173 或 127.0.0.1:4173)。注意:不是自定义域名,不需要证书。"),
2910
2988
  },
2911
2989
  annotations: {
2912
2990
  readOnlyHint: false,
@@ -2931,14 +3009,11 @@ function registerEnvTools(server) {
2931
3009
  default:
2932
3010
  throw new Error(`不支持的操作类型: ${action}`);
2933
3011
  }
2934
- return {
2935
- content: [
2936
- {
2937
- type: "text",
2938
- text: `${JSON.stringify(result, null, 2)}\n\n请注意安全域名需要10分钟才能生效,用户也应该了解这一点。`,
2939
- },
2940
- ],
2941
- };
3012
+ return (0, tool_result_js_1.buildJsonToolResult)(buildEnvDomainManagementResult({
3013
+ action,
3014
+ domains,
3015
+ result,
3016
+ }));
2942
3017
  }
2943
3018
  catch (error) {
2944
3019
  const toolPayloadResult = (0, tool_result_js_1.toolPayloadErrorToResult)(error);
@@ -24905,20 +24980,9 @@ async function getCloudBaseManager(options = {}) {
24905
24980
  }
24906
24981
  // envId priority: explicit option > envManager cache > loginState.envId
24907
24982
  const resolvedEnvId = finalEnvId || loginEnvId;
24908
- let region = fallbackRegion;
24909
- if (resolvedEnvId && !cloudBaseOptions?.region) {
24910
- try {
24911
- const envCandidates = await listAvailableEnvCandidates({ loginState });
24912
- const matchedEnv = envCandidates.find((candidate) => candidate.envId === resolvedEnvId);
24913
- if (matchedEnv?.region) {
24914
- region = matchedEnv.region;
24915
- (0, logger_js_1.debug)('使用环境实际 region:', { envId: resolvedEnvId, region });
24916
- }
24917
- }
24918
- catch {
24919
- (0, logger_js_1.debug)('无法获取环境列表,使用 fallback region');
24920
- }
24921
- }
24983
+ // region 直接使用 fallbackRegion(来自 cloudBaseOptions?.region ?? TCB_REGION ?? 'ap-shanghai')
24984
+ // 不再通过 DescribeEnvs 查询 region,以兼容 STS 临时密钥场景
24985
+ const region = fallbackRegion;
24922
24986
  const manager = new manager_node_1.default({
24923
24987
  secretId,
24924
24988
  secretKey,
@@ -28385,7 +28449,7 @@ function registerAppAuthTools(server) {
28385
28449
  };
28386
28450
  server.registerTool?.("queryAppAuth", {
28387
28451
  title: "查询应用认证配置",
28388
- description: "应用侧认证配置只读入口。用于查询登录方式、provider、publishable key、API key、client 配置和静态域名等认证准备状态。若业务要接受普通用户名样式标识符,先查询 action=getLoginConfig;若 usernamePassword=false,下一步应立即调用 manageAppAuth(action=patchLoginStrategy, patch={ usernamePassword: true }),不要直接写 email 登录 API。",
28452
+ description: "应用侧认证配置只读入口。用于查询登录方式、provider、publishable key、API key、client 配置和静态域名等认证准备状态。⚠️ 本工具为管理端配置查询工具,不执行用户登录。当任务要求编写客户端登录代码时(例如「用 JS SDK 登录」),应先通过本工具确认配置状态,再在项目代码中编写 @cloudbase/js-sdk 客户端登录代码(如 auth.signInWithPassword()),而非使用本工具完成登录。若业务要接受普通用户名样式标识符,先查询 action=getLoginConfig;若 usernamePassword=false,下一步应立即调用 manageAppAuth(action=patchLoginStrategy, patch={ usernamePassword: true }),不要直接写 email 登录 API。",
28389
28453
  inputSchema: {
28390
28454
  action: zod_1.z.enum(QUERY_APP_AUTH_ACTIONS),
28391
28455
  providerId: zod_1.z.string().optional().describe("provider 标识,如 email、google"),
@@ -28496,7 +28560,7 @@ function registerAppAuthTools(server) {
28496
28560
  }));
28497
28561
  server.registerTool?.("manageAppAuth", {
28498
28562
  title: "管理应用认证配置",
28499
- description: "应用侧认证配置写入口。用于修改登录方式、provider、client 配置,确保 publishable key,以及创建或删除 API key、自定义登录密钥。若前端要接受普通用户名样式标识符,应先执行 action=patchLoginStrategy 并传入 patch={ usernamePassword: true },再实现对应前端登录逻辑。",
28563
+ description: "应用侧认证配置写入口。用于修改登录方式、provider、client 配置,确保 publishable key,以及创建或删除 API key、自定义登录密钥。⚠️ 本工具为管理端配置工具,不执行用户登录。当任务要求编写客户端登录代码时(例如「用 JS SDK 登录」),应先通过本工具完成配置(如启用 usernamePassword、获取 publishable key),再在项目代码中编写 @cloudbase/js-sdk 客户端登录代码(如 auth.signInWithPassword()),而非使用本工具完成登录。若前端要接受普通用户名样式标识符,应先执行 action=patchLoginStrategy 并传入 patch={ usernamePassword: true },再实现对应前端登录逻辑。",
28500
28564
  inputSchema: {
28501
28565
  action: zod_1.z.enum(MANAGE_APP_AUTH_ACTIONS),
28502
28566
  patch: zod_1.z
@@ -37082,15 +37146,14 @@ const fs = __importStar(__webpack_require__(29021));
37082
37146
  const fsPromises = __importStar(__webpack_require__(79748));
37083
37147
  const http = __importStar(__webpack_require__(81630));
37084
37148
  const https = __importStar(__webpack_require__(55591));
37085
- const net = __importStar(__webpack_require__(91645));
37086
37149
  const os = __importStar(__webpack_require__(21820));
37087
37150
  const path = __importStar(__webpack_require__(39902));
37088
37151
  const url_1 = __webpack_require__(79551);
37089
37152
  const zod_1 = __webpack_require__(21614);
37090
- const dns = __importStar(__webpack_require__(14985));
37153
+ const remote_url_safety_js_1 = __webpack_require__(89192);
37091
37154
  // 常量定义
37092
37155
  const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
37093
- const ALLOWED_PROTOCOLS = ["http:", "https:"];
37156
+ const MAX_REDIRECT_HOPS = 5;
37094
37157
  const ALLOWED_CONTENT_TYPES = [
37095
37158
  "text/",
37096
37159
  "image/",
@@ -37139,65 +37202,6 @@ function calculateDownloadPath(relativePath) {
37139
37202
  }
37140
37203
  return finalPath;
37141
37204
  }
37142
- // 检查是否为内网 IP
37143
- function isPrivateIP(ip) {
37144
- // 如果不是有效的 IP 地址,返回 true(保守处理)
37145
- if (!net.isIP(ip)) {
37146
- return true;
37147
- }
37148
- // 检查特殊地址
37149
- if (ip === '127.0.0.1' ||
37150
- ip === 'localhost' ||
37151
- ip === '::1' || // IPv6 本地回环
37152
- ip.startsWith('169.254.') || // 链路本地地址
37153
- ip.startsWith('0.')) { // 特殊用途地址
37154
- return true;
37155
- }
37156
- // 转换 IP 地址为长整数进行范围检查
37157
- const ipv4Parts = ip.split('.').map(part => parseInt(part, 10));
37158
- if (ipv4Parts.length === 4) {
37159
- const ipNum = ((ipv4Parts[0] << 24) >>> 0) + (ipv4Parts[1] << 16) + (ipv4Parts[2] << 8) + ipv4Parts[3];
37160
- // 检查私有 IP 范围
37161
- // 10.0.0.0 - 10.255.255.255
37162
- if (ipNum >= 167772160 && ipNum <= 184549375)
37163
- return true;
37164
- // 172.16.0.0 - 172.31.255.255
37165
- if (ipNum >= 2886729728 && ipNum <= 2887778303)
37166
- return true;
37167
- // 192.168.0.0 - 192.168.255.255
37168
- if (ipNum >= 3232235520 && ipNum <= 3232301055)
37169
- return true;
37170
- }
37171
- // 检查 IPv6 私有地址
37172
- if (net.isIPv6(ip)) {
37173
- const normalizedIP = ip.toLowerCase();
37174
- if (normalizedIP.startsWith('fc00:') || // 唯一本地地址
37175
- normalizedIP.startsWith('fe80:') || // 链路本地地址
37176
- normalizedIP.startsWith('fec0:') || // 站点本地地址
37177
- normalizedIP.startsWith('::1')) { // 本地回环
37178
- return true;
37179
- }
37180
- }
37181
- return false;
37182
- }
37183
- // 检查域名是否解析到内网 IP
37184
- async function doesDomainResolveToPrivateIP(hostname) {
37185
- try {
37186
- const addresses = await new Promise((resolve, reject) => {
37187
- dns.resolve(hostname, (err, addresses) => {
37188
- if (err)
37189
- reject(err);
37190
- else
37191
- resolve(addresses);
37192
- });
37193
- });
37194
- return addresses.some(ip => isPrivateIP(ip));
37195
- }
37196
- catch (error) {
37197
- // 如果解析失败,为安全起见返回 true
37198
- return true;
37199
- }
37200
- }
37201
37205
  // 生成随机文件名
37202
37206
  function generateRandomFileName(extension = '') {
37203
37207
  const randomBytes = crypto.randomBytes(16);
@@ -37248,45 +37252,52 @@ function getFileExtension(url, contentType, contentDisposition) {
37248
37252
  }
37249
37253
  return extension;
37250
37254
  }
37251
- // 验证 URL 和内容类型是否安全
37252
- async function isUrlAndContentTypeSafe(url, contentType) {
37253
- try {
37254
- const parsedUrl = new url_1.URL(url);
37255
- // 检查协议
37256
- if (!ALLOWED_PROTOCOLS.includes(parsedUrl.protocol)) {
37257
- return false;
37258
- }
37259
- // 检查主机名是否为 IP 地址
37260
- const hostname = parsedUrl.hostname;
37261
- if (net.isIP(hostname) && isPrivateIP(hostname)) {
37262
- return false;
37263
- }
37264
- // 如果是域名,检查它是否解析到内网 IP
37265
- if (!net.isIP(hostname) && await doesDomainResolveToPrivateIP(hostname)) {
37266
- return false;
37267
- }
37268
- // 检查内容类型
37269
- return ALLOWED_CONTENT_TYPES.some(allowedType => contentType.startsWith(allowedType));
37270
- }
37271
- catch {
37272
- return false;
37273
- }
37255
+ function isAllowedContentType(contentType) {
37256
+ return ALLOWED_CONTENT_TYPES.some((allowedType) => contentType.startsWith(allowedType));
37257
+ }
37258
+ // 检查是否为可重试的网络错误
37259
+ function isRetryableError(error) {
37260
+ const retryableCodes = [
37261
+ 'ECONNRESET',
37262
+ 'ECONNREFUSED',
37263
+ 'ETIMEDOUT',
37264
+ 'ESOCKETTIMEDOUT',
37265
+ 'ENOTFOUND',
37266
+ 'EHOSTUNREACH',
37267
+ 'ENETUNREACH',
37268
+ 'ECONNABORTED'
37269
+ ];
37270
+ return retryableCodes.includes(error.code || '');
37274
37271
  }
37275
- // 下载文件到指定路径
37276
- function downloadFileToPath(url, targetPath) {
37272
+ // 下载文件到指定路径(带重试逻辑)
37273
+ async function downloadFileToPath(url, targetPath, retryCount = 0, redirectCount = 0) {
37274
+ const MAX_RETRIES = 3;
37275
+ const BASE_DELAY_MS = 1000; // 1秒基础延迟
37276
+ if (redirectCount > MAX_REDIRECT_HOPS) {
37277
+ throw new Error(`重定向次数超过 ${MAX_REDIRECT_HOPS} 次限制`);
37278
+ }
37279
+ const { requestUrl, requestOptions } = await (0, remote_url_safety_js_1.prepareSafeRemoteRequest)(url);
37280
+ const client = requestUrl.protocol === "https:" ? https : http;
37277
37281
  return new Promise((resolve, reject) => {
37278
- const client = url.startsWith('https:') ? https : http;
37279
- client.get(url, async (res) => {
37282
+ const request = client.get(requestUrl, requestOptions ?? {}, async (res) => {
37283
+ // 处理重定向
37284
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
37285
+ try {
37286
+ const redirectUrl = new url_1.URL(res.headers.location, requestUrl).toString();
37287
+ return resolve(await downloadFileToPath(redirectUrl, targetPath, retryCount, redirectCount + 1));
37288
+ }
37289
+ catch (error) {
37290
+ return reject(error);
37291
+ }
37292
+ }
37280
37293
  if (res.statusCode !== 200) {
37281
37294
  reject(new Error(`HTTP Error: ${res.statusCode}`));
37282
37295
  return;
37283
37296
  }
37284
- const contentType = res.headers['content-type'] || '';
37285
- const contentLength = parseInt(res.headers['content-length'] || '0', 10);
37286
- const contentDisposition = res.headers['content-disposition'];
37287
- // 安全检查
37288
- if (!await isUrlAndContentTypeSafe(url, contentType)) {
37289
- reject(new Error('不安全的 URL 或内容类型,或者目标为内网地址'));
37297
+ const contentType = res.headers["content-type"] || "";
37298
+ const contentLength = parseInt(res.headers["content-length"] || "0", 10);
37299
+ if (!isAllowedContentType(contentType)) {
37300
+ reject(new Error(`不允许的内容类型: ${contentType || "unknown"}`));
37290
37301
  return;
37291
37302
  }
37292
37303
  // 文件大小检查
@@ -37300,13 +37311,13 @@ function downloadFileToPath(url, targetPath) {
37300
37311
  await fsPromises.mkdir(targetDir, { recursive: true });
37301
37312
  }
37302
37313
  catch (error) {
37303
- reject(new Error(`无法创建目标目录: ${error instanceof Error ? error.message : '未知错误'}`));
37314
+ reject(new Error(`无法创建目标目录: ${error instanceof Error ? error.message : "未知错误"}`));
37304
37315
  return;
37305
37316
  }
37306
37317
  // 创建写入流
37307
37318
  const fileStream = fs.createWriteStream(targetPath);
37308
37319
  let downloadedSize = 0;
37309
- res.on('data', (chunk) => {
37320
+ res.on("data", (chunk) => {
37310
37321
  downloadedSize += chunk.length;
37311
37322
  if (downloadedSize > MAX_FILE_SIZE) {
37312
37323
  fileStream.destroy();
@@ -37315,37 +37326,65 @@ function downloadFileToPath(url, targetPath) {
37315
37326
  }
37316
37327
  });
37317
37328
  res.pipe(fileStream);
37318
- fileStream.on('finish', () => {
37329
+ fileStream.on("finish", () => {
37319
37330
  resolve({
37320
37331
  filePath: targetPath,
37321
37332
  contentType,
37322
- fileSize: downloadedSize
37333
+ fileSize: downloadedSize,
37323
37334
  });
37324
37335
  });
37325
- fileStream.on('error', (error) => {
37336
+ fileStream.on("error", (error) => {
37326
37337
  fsPromises.unlink(targetPath).catch(() => { });
37327
37338
  reject(error);
37328
37339
  });
37329
- }).on('error', (error) => {
37340
+ });
37341
+ request.on("error", async (error) => {
37342
+ // 检查是否可重试
37343
+ if (isRetryableError(error) && retryCount < MAX_RETRIES) {
37344
+ const delay = BASE_DELAY_MS * Math.pow(2, retryCount); // 指数退避
37345
+ console.warn(`下载失败 (${error.code}),${delay}ms 后重试 (${retryCount + 1}/${MAX_RETRIES})...`);
37346
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, delay));
37347
+ try {
37348
+ return resolve(await downloadFileToPath(url, targetPath, retryCount + 1, redirectCount));
37349
+ }
37350
+ catch (retryError) {
37351
+ return reject(retryError);
37352
+ }
37353
+ }
37330
37354
  reject(error);
37331
37355
  });
37332
37356
  });
37333
37357
  }
37334
- // 下载文件到临时目录(保持向后兼容)
37335
- function downloadFile(url) {
37358
+ // 下载文件到临时目录(保持向后兼容,带重试逻辑)
37359
+ async function downloadFile(url, retryCount = 0, redirectCount = 0) {
37360
+ const MAX_RETRIES = 3;
37361
+ const BASE_DELAY_MS = 1000; // 1秒基础延迟
37362
+ if (redirectCount > MAX_REDIRECT_HOPS) {
37363
+ throw new Error(`重定向次数超过 ${MAX_REDIRECT_HOPS} 次限制`);
37364
+ }
37365
+ const { requestUrl, requestOptions } = await (0, remote_url_safety_js_1.prepareSafeRemoteRequest)(url);
37366
+ const client = requestUrl.protocol === "https:" ? https : http;
37336
37367
  return new Promise((resolve, reject) => {
37337
- const client = url.startsWith('https:') ? https : http;
37338
- client.get(url, async (res) => {
37368
+ const request = client.get(requestUrl, requestOptions ?? {}, async (res) => {
37369
+ // 处理重定向
37370
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
37371
+ try {
37372
+ const redirectUrl = new url_1.URL(res.headers.location, requestUrl).toString();
37373
+ return resolve(await downloadFile(redirectUrl, retryCount, redirectCount + 1));
37374
+ }
37375
+ catch (error) {
37376
+ return reject(error);
37377
+ }
37378
+ }
37339
37379
  if (res.statusCode !== 200) {
37340
37380
  reject(new Error(`HTTP Error: ${res.statusCode}`));
37341
37381
  return;
37342
37382
  }
37343
- const contentType = res.headers['content-type'] || '';
37344
- const contentLength = parseInt(res.headers['content-length'] || '0', 10);
37345
- const contentDisposition = res.headers['content-disposition'];
37346
- // 安全检查
37347
- if (!await isUrlAndContentTypeSafe(url, contentType)) {
37348
- reject(new Error('不安全的 URL 或内容类型,或者目标为内网地址'));
37383
+ const contentType = res.headers["content-type"] || "";
37384
+ const contentLength = parseInt(res.headers["content-length"] || "0", 10);
37385
+ const contentDisposition = res.headers["content-disposition"];
37386
+ if (!isAllowedContentType(contentType)) {
37387
+ reject(new Error(`不允许的内容类型: ${contentType || "unknown"}`));
37349
37388
  return;
37350
37389
  }
37351
37390
  // 文件大小检查
@@ -37354,13 +37393,13 @@ function downloadFile(url) {
37354
37393
  return;
37355
37394
  }
37356
37395
  // 生成临时文件路径
37357
- const extension = getFileExtension(url, contentType, contentDisposition);
37396
+ const extension = getFileExtension(requestUrl.toString(), contentType, contentDisposition);
37358
37397
  const fileName = generateRandomFileName(extension);
37359
37398
  const filePath = getSafeTempFilePath(fileName);
37360
37399
  // 创建写入流
37361
37400
  const fileStream = fs.createWriteStream(filePath);
37362
37401
  let downloadedSize = 0;
37363
- res.on('data', (chunk) => {
37402
+ res.on("data", (chunk) => {
37364
37403
  downloadedSize += chunk.length;
37365
37404
  if (downloadedSize > MAX_FILE_SIZE) {
37366
37405
  fileStream.destroy();
@@ -37369,18 +37408,31 @@ function downloadFile(url) {
37369
37408
  }
37370
37409
  });
37371
37410
  res.pipe(fileStream);
37372
- fileStream.on('finish', () => {
37411
+ fileStream.on("finish", () => {
37373
37412
  resolve({
37374
37413
  filePath,
37375
37414
  contentType,
37376
- fileSize: downloadedSize
37415
+ fileSize: downloadedSize,
37377
37416
  });
37378
37417
  });
37379
- fileStream.on('error', (error) => {
37418
+ fileStream.on("error", (error) => {
37380
37419
  fsPromises.unlink(filePath).catch(() => { });
37381
37420
  reject(error);
37382
37421
  });
37383
- }).on('error', (error) => {
37422
+ });
37423
+ request.on("error", async (error) => {
37424
+ // 检查是否可重试
37425
+ if (isRetryableError(error) && retryCount < MAX_RETRIES) {
37426
+ const delay = BASE_DELAY_MS * Math.pow(2, retryCount); // 指数退避
37427
+ console.warn(`下载失败 (${error.code}),${delay}ms 后重试 (${retryCount + 1}/${MAX_RETRIES})...`);
37428
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, delay));
37429
+ try {
37430
+ return resolve(await downloadFile(url, retryCount + 1, redirectCount));
37431
+ }
37432
+ catch (retryError) {
37433
+ return reject(retryError);
37434
+ }
37435
+ }
37384
37436
  reject(error);
37385
37437
  });
37386
37438
  });
@@ -37393,7 +37445,8 @@ function buildDownloadRemoteFileErrorMessage(url, error) {
37393
37445
  suggestions.push("请先在浏览器中确认该链接可访问,再重新执行下载。");
37394
37446
  }
37395
37447
  if (/ENOTFOUND|ECONNRESET|ETIMEDOUT|socket hang up/i.test(baseMessage)) {
37396
- suggestions.push("下载过程中出现网络异常,建议检查本地网络或代理后重试。");
37448
+ suggestions.push("下载过程中出现网络异常,系统已自动重试 3 次但均失败。");
37449
+ suggestions.push("建议:1) 检查本地网络或代理设置;2) 该网站可能限频,请尝试更换其他图片源(如 Unsplash、Pexels);3) 稍后重试。");
37397
37450
  }
37398
37451
  if (suggestions.length === 0) {
37399
37452
  suggestions.push("请检查 URL 是否可访问,以及目标资源是否允许直接下载。");
@@ -88421,6 +88474,7 @@ exports.MANAGE_FUNCTION_ACTIONS = [
88421
88474
  "updateFunctionCode",
88422
88475
  "updateFunctionConfig",
88423
88476
  "invokeFunction",
88477
+ "deleteFunction",
88424
88478
  "createFunctionTrigger",
88425
88479
  "deleteFunctionTrigger",
88426
88480
  "createLayerVersion",
@@ -88564,14 +88618,32 @@ function buildFunctionOperationErrorMessage(operation, functionName, functionRoo
88564
88618
  if (/路径不存在/i.test(baseMessage) && expectedFunctionPath) {
88565
88619
  suggestions.push(`当前工具会从 \`functionRootPath + 函数名\` 查找代码目录,期望目录是 \`${expectedFunctionPath}\`。`);
88566
88620
  suggestions.push("如果你传入的已经是函数目录本身,请改为传它的父目录。");
88621
+ if (functionRootPath) {
88622
+ const lastDir = path_1.default.basename(path_1.default.normalize(functionRootPath));
88623
+ if (lastDir !== "cloudfunctions" && lastDir !== "functions") {
88624
+ suggestions.push(`functionRootPath 应该是直接包含函数文件夹的目录(如 cloudfunctions 或 functions),而不是项目根目录。` +
88625
+ `请将 functionRootPath 改为 \`${path_1.default.join(path_1.default.normalize(functionRootPath), "cloudfunctions")}\` ` +
88626
+ `或 \`${path_1.default.join(path_1.default.normalize(functionRootPath), "functions")}\`。`);
88627
+ }
88628
+ }
88567
88629
  }
88568
88630
  if (/paths\[0\].*undefined/i.test(baseMessage)) {
88569
- suggestions.push("HTTP 函数创建时需要提供 functionRootPath(指向 cloudfunctions 父目录)或 zipFile,否则 SDK 无法定位函数目录。");
88631
+ suggestions.push("HTTP 函数创建时需要提供 functionRootPath(指向 cloudfunctions 或 functions 目录的绝对路径,不是项目根目录)或 zipFile,否则 SDK 无法定位函数目录。");
88570
88632
  }
88571
88633
  if (/依赖安装失败|package\.json/i.test(baseMessage)) {
88572
88634
  suggestions.push("如果 HTTP 函数只使用原生 Node.js API 且没有第三方依赖,可以保留函数目录中的 index.js 和 scf_bootstrap,工具会跳过依赖安装。");
88573
88635
  suggestions.push("如果你确实依赖 npm 包,请在函数目录下补充 package.json 后重试。");
88574
88636
  }
88637
+ // Handle invalid parameter value errors from CloudBase API
88638
+ if (/invalid parameter value/i.test(baseMessage)) {
88639
+ suggestions.push("检测到参数值格式错误。请重点检查以下配置项:");
88640
+ suggestions.push("1. runtime: 请使用支持的运行时版本,如 Nodejs18.15、Nodejs16.13、Nodejs20.19 等(区分大小写,不要加空格)");
88641
+ suggestions.push("2. handler: Event 函数默认使用 index.main,HTTP 函数默认使用 app.handler 或 scf_bootstrap 启动");
88642
+ suggestions.push("3. functionName: 函数名称只能包含字母、数字、下划线、连字符,不能以数字开头");
88643
+ suggestions.push("4. timeout: 超时时间需为整数,单位为秒,范围 1-900");
88644
+ suggestions.push("5. envVariables: 环境变量键值对不能为空字符串");
88645
+ suggestions.push("6. type: 函数类型只能是 Event 或 HTTP(区分大小写)");
88646
+ }
88575
88647
  if (suggestions.length === 0) {
88576
88648
  suggestions.push("请检查函数名、目录结构和环境中的函数状态后重试。");
88577
88649
  }
@@ -88628,10 +88700,17 @@ function registerFunctionTools(server) {
88628
88700
  throw new Error("createLayerVersion 在 cloud mode 下不支持 contentPath,本地文件内容请改为 base64Content 或改用本地模式执行。");
88629
88701
  }
88630
88702
  };
88703
+ const TIME_FORMAT_REGEX = /^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$/;
88631
88704
  const validateLogRange = (startTime, endTime, offset, limit) => {
88632
88705
  if ((offset || 0) + (limit || 0) > 10000) {
88633
88706
  throw new Error("offset+limit 不能大于 10000");
88634
88707
  }
88708
+ if (startTime && !TIME_FORMAT_REGEX.test(startTime)) {
88709
+ throw new Error(`startTime 格式错误: "${startTime}"。必须使用 YYYY-MM-DD HH:mm:ss 格式(如 2024-01-01 00:00:00)`);
88710
+ }
88711
+ if (endTime && !TIME_FORMAT_REGEX.test(endTime)) {
88712
+ throw new Error(`endTime 格式错误: "${endTime}"。必须使用 YYYY-MM-DD HH:mm:ss 格式(如 2024-01-01 23:59:59)`);
88713
+ }
88635
88714
  if (startTime && endTime) {
88636
88715
  const start = new Date(startTime).getTime();
88637
88716
  const end = new Date(endTime).getTime();
@@ -88711,15 +88790,29 @@ function registerFunctionTools(server) {
88711
88790
  }
88712
88791
  validateLogRange(input.startTime, input.endTime, input.offset, input.limit);
88713
88792
  const cloudbase = await getManager();
88714
- const result = await cloudbase.functions.getFunctionLogsV2({
88715
- name: input.functionName,
88716
- offset: input.offset,
88717
- limit: input.limit,
88718
- startTime: input.startTime,
88719
- endTime: input.endTime,
88720
- requestId: input.requestId,
88721
- qualifier: input.qualifier,
88722
- });
88793
+ let result;
88794
+ try {
88795
+ result = await cloudbase.functions.getFunctionLogsV2({
88796
+ name: input.functionName,
88797
+ offset: input.offset,
88798
+ limit: input.limit,
88799
+ startTime: input.startTime,
88800
+ endTime: input.endTime,
88801
+ requestId: input.requestId,
88802
+ qualifier: input.qualifier,
88803
+ });
88804
+ }
88805
+ catch (error) {
88806
+ const errMsg = error instanceof Error ? error.message : String(error);
88807
+ if (/invalid parameter/i.test(errMsg)) {
88808
+ throw new Error(`${errMsg}\n\n常见原因:\n` +
88809
+ `1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n` +
88810
+ `2. startTime 和 endTime 间隔超过一天\n` +
88811
+ `3. functionName 不存在或格式不正确\n` +
88812
+ `建议:不传 startTime/endTime 时默认查询最近一天的日志。`);
88813
+ }
88814
+ throw error;
88815
+ }
88723
88816
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
88724
88817
  return buildEnvelope({
88725
88818
  action: input.action,
@@ -88741,11 +88834,24 @@ function registerFunctionTools(server) {
88741
88834
  }
88742
88835
  validateLogRange(input.startTime, input.endTime);
88743
88836
  const cloudbase = await getManager();
88744
- const result = await cloudbase.functions.getFunctionLogDetail({
88745
- startTime: input.startTime,
88746
- endTime: input.endTime,
88747
- logRequestId: input.requestId,
88748
- });
88837
+ let result;
88838
+ try {
88839
+ result = await cloudbase.functions.getFunctionLogDetail({
88840
+ startTime: input.startTime,
88841
+ endTime: input.endTime,
88842
+ logRequestId: input.requestId,
88843
+ });
88844
+ }
88845
+ catch (error) {
88846
+ const errMsg = error instanceof Error ? error.message : String(error);
88847
+ if (/invalid parameter/i.test(errMsg)) {
88848
+ throw new Error(`${errMsg}\n\n常见原因:\n` +
88849
+ `1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n` +
88850
+ `2. startTime 和 endTime 间隔超过一天\n` +
88851
+ `建议:不传 startTime/endTime 时默认查询最近一天的日志。`);
88852
+ }
88853
+ throw error;
88854
+ }
88749
88855
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
88750
88856
  return buildEnvelope({
88751
88857
  action: input.action,
@@ -88941,7 +89047,7 @@ function registerFunctionTools(server) {
88941
89047
  const functionType = typeof func.type === "string" ? func.type : undefined;
88942
89048
  const expectedFunctionPath = getExpectedFunctionPath(processedRootPath, functionName);
88943
89049
  if (functionType === "HTTP" && !processedRootPath && !input.zipFile) {
88944
- throw new Error("createFunction 创建 HTTP 函数时,需要提供 functionRootPath(指向 cloudfunctions 父目录)或 zipFile。");
89050
+ throw new Error("createFunction 创建 HTTP 函数时,需要提供 functionRootPath(指向 cloudfunctions 或 functions 目录的绝对路径,不是项目根目录)或 zipFile。");
88945
89051
  }
88946
89052
  const hasPackageJson = expectedFunctionPath !== undefined
88947
89053
  ? (0, fs_1.existsSync)(path_1.default.join(expectedFunctionPath, "package.json"))
@@ -88980,7 +89086,7 @@ function registerFunctionTools(server) {
88980
89086
  nextActions.push({
88981
89087
  tool: "manageGateway",
88982
89088
  action: "createAccess",
88983
- reason: "如果需要通过 URL 访问 HTTP 函数,请按实际路径和鉴权需求显式创建访问入口,不要默认假设 /函数名 已存在",
89089
+ reason: "如果需要通过 URL 访问 HTTP 函数,请调用 manageGateway(action=\"createAccess\") 并显式传 type=\"HTTP\",再按实际路径和鉴权需求创建访问入口,不要默认假设 /函数名 已存在",
88984
89090
  });
88985
89091
  nextActions.push({
88986
89092
  tool: "queryGateway",
@@ -88999,7 +89105,7 @@ function registerFunctionTools(server) {
88999
89105
  });
89000
89106
  }
89001
89107
  const message = func.type === "HTTP"
89002
- ? `已创建 HTTP 函数 ${functionName}。如果后续需要通过 URL 访问,请显式调用 manageGateway(action="createAccess") 按实际路径和鉴权需求创建访问入口。评测或其他外部调用方可能会以匿名身份访问,而且失败后不一定会把 EXCEED_AUTHORITY 再反馈给 AI;交付前请主动确认访问路径和函数安全规则,若已出现 EXCEED_AUTHORITY,请先调用 queryPermissions(action="getResourcePermission", resourceType="function", resourceId="${functionName}") 查看当前规则,再按需要使用 managePermissions(action="updateResourcePermission") 调整权限。`
89108
+ ? `已创建 HTTP 函数 ${functionName}。如果后续需要通过 URL 访问,请显式调用 manageGateway(action="createAccess"),并把 type="HTTP" 一起传入,再按实际路径和鉴权需求创建访问入口。评测或其他外部调用方可能会以匿名身份访问,而且失败后不一定会把 EXCEED_AUTHORITY 再反馈给 AI;交付前请主动确认访问路径和函数安全规则,若已出现 EXCEED_AUTHORITY,请先调用 queryPermissions(action="getResourcePermission", resourceType="function", resourceId="${functionName}") 查看当前规则,再按需要使用 managePermissions(action="updateResourcePermission") 调整权限。`
89003
89109
  : `已创建函数 ${functionName}`;
89004
89110
  return buildEnvelope({
89005
89111
  action: input.action,
@@ -89114,6 +89220,26 @@ function registerFunctionTools(server) {
89114
89220
  throw error;
89115
89221
  }
89116
89222
  }
89223
+ case "deleteFunction": {
89224
+ if (!input.functionName) {
89225
+ throw new Error("deleteFunction 操作时,functionName 参数是必需的");
89226
+ }
89227
+ requireConfirm(input.action, input.confirm);
89228
+ const cloudbase = await getManager();
89229
+ const result = await cloudbase.functions.deleteFunction(input.functionName);
89230
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
89231
+ return buildEnvelope({
89232
+ action: input.action,
89233
+ functionName: input.functionName,
89234
+ raw: result,
89235
+ }, `已删除函数 ${input.functionName}`, [
89236
+ {
89237
+ tool: "queryFunctions",
89238
+ action: "listFunctions",
89239
+ reason: "确认函数已被删除",
89240
+ },
89241
+ ]);
89242
+ }
89117
89243
  case "createFunctionTrigger": {
89118
89244
  if (!input.functionName) {
89119
89245
  throw new Error("createFunctionTrigger 操作时,functionName 参数是必需的");
@@ -89326,23 +89452,58 @@ function registerFunctionTools(server) {
89326
89452
  };
89327
89453
  server.registerTool?.("queryFunctions", {
89328
89454
  title: "查询云函数域资源",
89329
- description: "函数域统一只读入口。通过更自解释的 action 查询函数列表、函数详情、日志、层、触发器和代码下载地址。",
89455
+ description: "函数域统一只读入口。通过更自解释的 action 查询 CloudBase 云函数列表、函数详情、执行日志、层、触发器和代码下载地址。" +
89456
+ "\n\n**分页说明**:`listFunctions`、`listLayers` 支持 `limit` 和 `offset` 参数。" +
89457
+ "\n- `limit`: 分页数量,默认值由后端决定" +
89458
+ "\n- `offset`: 分页偏移,从 0 开始" +
89459
+ "\n- 示例:`queryFunctions(action=\"listFunctions\", offset=10, limit=10)`" +
89460
+ "\n\n**查询 CloudBase 云函数日志**:使用 `action=\"listFunctionLogs\"`,需要提供 `functionName` 参数。" +
89461
+ "\n- 示例:`queryFunctions(action=\"listFunctionLogs\", functionName=\"my-function\")`" +
89462
+ "\n- 如需查看日志详情:`queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
89463
+ "\n\n**定时任务 / cron / 定时跑**:使用 `listFunctionTriggers` 查询函数的 timer 触发器配置。" +
89464
+ "\n\n**区分 `queryLogs` 工具**:" +
89465
+ "\n- 本工具用于查询特定 CloudBase 云函数的执行日志" +
89466
+ "\n- `queryLogs` 工具用于搜索 CLS 日志服务(跨服务日志聚合)",
89330
89467
  inputSchema: {
89331
89468
  action: zod_1.z
89332
89469
  .enum(exports.QUERY_FUNCTION_ACTIONS)
89333
- .describe("只读操作类型,例如 listFunctions、getFunctionDetail、listFunctionLogs"),
89334
- functionName: zod_1.z.string().optional().describe("函数名称。函数相关 action 必填"),
89335
- limit: zod_1.z.number().optional().describe("分页数量。列表类 action 可选"),
89336
- offset: zod_1.z.number().optional().describe("分页偏移。列表类 action 可选"),
89337
- codeSecret: zod_1.z.string().optional().describe("代码保护密钥"),
89338
- startTime: zod_1.z.string().optional().describe("日志查询开始时间"),
89339
- endTime: zod_1.z.string().optional().describe("日志查询结束时间"),
89340
- requestId: zod_1.z.string().optional().describe("日志 requestId。获取日志详情时必填"),
89341
- qualifier: zod_1.z.string().optional().describe("函数版本,日志查询时可选"),
89342
- runtime: zod_1.z.string().optional().describe("层查询的运行时筛选"),
89470
+ .describe("只读操作类型:" +
89471
+ "\n- `listFunctions`: 列出所有 CloudBase 云函数" +
89472
+ "\n- `getFunctionDetail`: 获取 CloudBase 云函数详情(需要 functionName)" +
89473
+ "\n- `listFunctionLogs`: 查询 CloudBase 云函数执行日志(需要 functionName)" +
89474
+ "\n- `getFunctionLogDetail`: 获取日志详情(需要 requestId)" +
89475
+ "\n- `listFunctionLayers`: 列出函数绑定的层" +
89476
+ "\n- `listLayers`: 列出所有层" +
89477
+ "\n- `listLayerVersions`: 列出层的版本(注意:是 Versions 不是 Version)" +
89478
+ "\n- `getLayerVersionDetail`: 获取层版本详情" +
89479
+ "\n- `listFunctionTriggers`: 列出函数触发器(用于查看定时任务 / cron / timer 配置)" +
89480
+ "\n- `getFunctionDownloadUrl`: 获取函数代码下载地址"),
89481
+ functionName: zod_1.z
89482
+ .string()
89483
+ .optional()
89484
+ .describe("CloudBase 云函数名称。`getFunctionDetail`、`listFunctionLogs`、`listFunctionLayers`、`listFunctionTriggers`、`getFunctionDownloadUrl` 时必填"),
89485
+ limit: zod_1.z.number().optional().describe("分页数量(limit)。列表类 action 可选,默认值由后端决定"),
89486
+ offset: zod_1.z.number().optional().describe("分页偏移(offset)。列表类 action 可选,默认 0"),
89487
+ codeSecret: zod_1.z.string().optional().describe("代码保护密钥,用于解密函数代码"),
89488
+ startTime: zod_1.z
89489
+ .string()
89490
+ .optional()
89491
+ .describe("日志查询开始时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00)。" +
89492
+ "与 endTime 间隔不能超过一天。不传时默认查询最近一天"),
89493
+ endTime: zod_1.z
89494
+ .string()
89495
+ .optional()
89496
+ .describe("日志查询结束时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 23:59:59)。" +
89497
+ "与 startTime 间隔不能超过一天。不传时默认为当前时间"),
89498
+ requestId: zod_1.z
89499
+ .string()
89500
+ .optional()
89501
+ .describe("日志请求 ID。`getFunctionLogDetail` 操作必填,可从 `listFunctionLogs` 结果中获取"),
89502
+ qualifier: zod_1.z.string().optional().describe("函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选"),
89503
+ runtime: zod_1.z.string().optional().describe("层查询的运行时筛选,如 Nodejs18.15"),
89343
89504
  searchKey: zod_1.z.string().optional().describe("层名称搜索关键字"),
89344
- layerName: zod_1.z.string().optional().describe("层名称。层相关 action 必填"),
89345
- layerVersion: zod_1.z.number().optional().describe("层版本号。获取层版本详情时必填"),
89505
+ layerName: zod_1.z.string().optional().describe("层名称。`listLayerVersions`、`getLayerVersionDetail` 操作必填"),
89506
+ layerVersion: zod_1.z.number().optional().describe("层版本号。`getLayerVersionDetail` 操作必填"),
89346
89507
  },
89347
89508
  annotations: {
89348
89509
  readOnlyHint: true,
@@ -89352,15 +89513,20 @@ function registerFunctionTools(server) {
89352
89513
  }, async (input) => withEnvelope(() => handleQueryFunctions(input)));
89353
89514
  server.registerTool?.("manageFunctions", {
89354
89515
  title: "管理云函数域资源",
89355
- description: "函数域统一写入口。通过 action 管理函数创建、代码更新、配置更新、调用函数、触发器和层绑定。危险操作需要显式 confirm=true。",
89516
+ description: "函数域统一写入口。支持创建函数、更新代码、更新配置、调用函数、管理定时跑 / 定时任务 / scheduled job 的 timer 触发器和层绑定。" +
89517
+ "如果要创建 cron 定时任务,先用 createFunction 创建函数,再用 createFunctionTrigger 创建 timer 触发器(支持7段cron表达式),deleteFunctionTrigger 删除触发器。" +
89518
+ "危险操作需要显式 confirm=true。",
89356
89519
  inputSchema: {
89357
89520
  action: zod_1.z
89358
89521
  .enum(exports.MANAGE_FUNCTION_ACTIONS)
89359
- .describe("写操作类型,例如 createFunction、invokeFunction、attachLayer"),
89522
+ .describe("写操作类型,例如 createFunction、updateFunctionCode、invokeFunction、deleteFunction、" +
89523
+ "createFunctionTrigger(定时任务 / cron / timer)、deleteFunctionTrigger、attachLayer、detachLayer"),
89360
89524
  func: CREATE_FUNCTION_SCHEMA.optional().describe("createFunction 操作的函数配置"),
89361
- functionRootPath: zod_1.z.string().optional().describe("创建或更新函数代码时默认推荐的本地目录方式。函数根目录(父目录绝对路径)。" +
89362
- "本地应按 cloudfunctions/<functionName>/index.js 布局," +
89363
- "此参数传 cloudfunctions 目录的绝对路径(如 /abs/path/cloudfunctions),不要传到函数名子目录。" +
89525
+ functionRootPath: zod_1.z.string().optional().describe("创建或更新函数代码时默认推荐的本地目录方式。" +
89526
+ "必须是直接包含函数文件夹的目录绝对路径(如 /abs/path/cloudfunctions 或 /abs/path/functions)," +
89527
+ "不要传项目根目录(如 /abs/path),也不要传到函数名子目录(如 /abs/path/cloudfunctions/hello)。" +
89528
+ "本地应按 cloudfunctions/<functionName>/index.js 或 functions/<functionName>/index.js 布局," +
89529
+ "此参数传 cloudfunctions 或 functions 目录的绝对路径。" +
89364
89530
  "SDK 会自动拼接函数名子目录,无需预先压缩 zip 或 base64 编码。"),
89365
89531
  force: zod_1.z.boolean().optional().describe("createFunction 时是否覆盖"),
89366
89532
  functionName: zod_1.z.string().optional().describe("函数名称。大多数 action 使用该字段作为统一目标"),
@@ -89370,7 +89536,11 @@ function registerFunctionTools(server) {
89370
89536
  envVariables: zod_1.z.record(zod_1.z.string()).optional().describe("配置更新时要合并的环境变量"),
89371
89537
  vpc: VPC_SCHEMA.optional().describe("配置更新时的 VPC 信息"),
89372
89538
  params: zod_1.z.record(zod_1.z.any()).optional().describe("invokeFunction 的调用参数"),
89373
- triggers: zod_1.z.array(TRIGGER_SCHEMA).optional().describe("createFunctionTrigger 的触发器列表"),
89539
+ triggers: zod_1.z
89540
+ .array(TRIGGER_SCHEMA)
89541
+ .optional()
89542
+ .describe("createFunctionTrigger 的触发器列表,用于定时跑 / 定时任务 / scheduled job。timer 触发器使用7段 cron 表达式(秒 分 时 日 月 星期 年)," +
89543
+ '如 "0 */5 * * * * *" 表示每5分钟执行一次'),
89374
89544
  triggerName: zod_1.z.string().optional().describe("deleteFunctionTrigger 的目标触发器名称"),
89375
89545
  layerName: zod_1.z.string().optional().describe("层名称"),
89376
89546
  layerVersion: zod_1.z.number().optional().describe("层版本号"),
@@ -89384,7 +89554,7 @@ function registerFunctionTools(server) {
89384
89554
  .optional()
89385
89555
  .describe("updateFunctionLayers 的目标层列表,顺序即最终顺序"),
89386
89556
  codeSecret: zod_1.z.string().optional().describe("层绑定时的代码保护密钥"),
89387
- confirm: zod_1.z.boolean().optional().describe("危险操作确认开关"),
89557
+ confirm: zod_1.z.boolean().optional().describe("危险操作确认开关。deleteFunction、deleteFunctionTrigger、deleteLayerVersion、detachLayer 等删除类操作需要显式传入 confirm=true"),
89388
89558
  },
89389
89559
  annotations: {
89390
89560
  readOnlyHint: false,
@@ -91629,18 +91799,33 @@ class EnvService {
91629
91799
  * @returns {Promise<IEnvInfoRes>}
91630
91800
  */
91631
91801
  async getEnvInfo() {
91632
- // NOTE: DescribeEnv 接口废弃,需要使用 DescribeEnvs 接口
91802
+ var _a;
91803
+ // 使用 DescribeEnvInfo 进行单环境查询
91633
91804
  const params = {
91634
91805
  EnvId: this.envId
91635
91806
  };
91636
- if (this.envType === 'run') {
91637
- params.EnvType = 'run';
91638
- }
91639
- const { EnvList, RequestId } = await this.describeEnvs(params);
91807
+ const response = await this.describeEnvInfo(params);
91808
+ const envBaseInfo = (_a = response === null || response === void 0 ? void 0 : response.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo;
91809
+ // 成功获取环境信息,将 EnvBaseInfo 映射到 EnvInfo 类型
91810
+ const envInfo = {
91811
+ EnvId: envBaseInfo.EnvId,
91812
+ Alias: envBaseInfo.Alias,
91813
+ Status: envBaseInfo.Status,
91814
+ Databases: envBaseInfo.Databases,
91815
+ Storages: envBaseInfo.Storages,
91816
+ Functions: envBaseInfo.Functions,
91817
+ PackageId: envBaseInfo.PackageId || '',
91818
+ PackageName: envBaseInfo.PackageName || '',
91819
+ LogServices: envBaseInfo.LogServices || [],
91820
+ CustomLogServices: envBaseInfo.CustomLogServices || [],
91821
+ Region: envBaseInfo.Region || '',
91822
+ Source: envBaseInfo.Source,
91823
+ CreateTime: envBaseInfo.CreateTime,
91824
+ UpdateTime: envBaseInfo.UpdateTime
91825
+ };
91640
91826
  return {
91641
- // @ts-ignore
91642
- EnvInfo: (EnvList === null || EnvList === void 0 ? void 0 : EnvList.length) ? EnvList[0] : {},
91643
- RequestId
91827
+ EnvInfo: envInfo,
91828
+ RequestId: response.RequestId || ''
91644
91829
  };
91645
91830
  }
91646
91831
  /**
@@ -91649,6 +91834,18 @@ class EnvService {
91649
91834
  async describeEnvs(params) {
91650
91835
  return this.cloudService.request('DescribeEnvs', params);
91651
91836
  }
91837
+ /**
91838
+ * 查询单个环境信息
91839
+ *
91840
+ * 用于单个环境查询时,规避 DescribeEnvs 的 backend issues。
91841
+ * 当 EnvId 参数存在时,应优先使用本方法而非 describeEnvs。
91842
+ *
91843
+ * @param params 查询参数,必须包含 EnvId
91844
+ * @returns 环境信息响应
91845
+ */
91846
+ async describeEnvInfo(params) {
91847
+ return this.cloudService.request('DescribeEnvInfo', params);
91848
+ }
91652
91849
  /**
91653
91850
  * 修改环境名称
91654
91851
  * @param {string} alias 环境名称
@@ -91969,28 +92166,28 @@ class EnvService {
91969
92166
  async calculatePackageRenewPrice(params) {
91970
92167
  var _a, _b, _c, _d, _e, _f;
91971
92168
  const { envId, period = 1, currency = 'CNY' } = params;
91972
- const [packageRes, envRes, billingRes] = await Promise.all([
92169
+ const [packageRes, envInfoRes, billingRes] = await Promise.all([
91973
92170
  this.describeBaasPackageList({
91974
92171
  Source: 'qcloud',
91975
92172
  PackageTypeList: ['default', 'vip', 'basic', 'trial']
91976
92173
  }),
91977
- this.describeEnvs({
92174
+ this.describeEnvInfo({
91978
92175
  EnvId: envId
91979
92176
  }),
91980
92177
  this.describeBillingInfo({
91981
92178
  EnvId: envId
91982
92179
  })
91983
92180
  ]);
91984
- const envInfo = (_a = envRes.EnvList) === null || _a === void 0 ? void 0 : _a[0];
92181
+ const envBaseInfo = (_a = envInfoRes === null || envInfoRes === void 0 ? void 0 : envInfoRes.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo;
91985
92182
  const billingInfo = (_b = billingRes.EnvBillingInfoList) === null || _b === void 0 ? void 0 : _b[0];
91986
- if (!envInfo || !billingInfo) {
92183
+ if (!(envBaseInfo === null || envBaseInfo === void 0 ? void 0 : envBaseInfo.EnvId) || !billingInfo) {
91987
92184
  throw new error_1.CloudBaseError(`Invalid env id: ${envId}`);
91988
92185
  }
91989
- const packageInfo = (_c = packageRes.PackageList) === null || _c === void 0 ? void 0 : _c.find(item => item.PackageName === envInfo.PackageId);
92186
+ const packageInfo = (_c = packageRes.PackageList) === null || _c === void 0 ? void 0 : _c.find(item => item.PackageName === envBaseInfo.PackageId);
91990
92187
  if (!packageInfo) {
91991
92188
  throw new error_1.CloudBaseError(`Invalid env id: ${envId}`);
91992
92189
  }
91993
- const region = envInfo.Region;
92190
+ const region = envBaseInfo.Region;
91994
92191
  const extPackageData = ((_d = billingInfo.ExtPackageCode) === null || _d === void 0 ? void 0 : _d.reduce((acc, { Code, Num }) => {
91995
92192
  acc[Code] = Num;
91996
92193
  return acc;
@@ -92023,21 +92220,21 @@ class EnvService {
92023
92220
  async calculatePackageModifyPrice(params) {
92024
92221
  var _a, _b, _c, _d, _e, _f, _g;
92025
92222
  const { newPackageId, envId, currency = 'CNY' } = params;
92026
- const [packageRes, envRes, billingRes] = await Promise.all([
92223
+ const [packageRes, envInfoRes, billingRes] = await Promise.all([
92027
92224
  this.describeBaasPackageList({
92028
92225
  Source: 'qcloud',
92029
92226
  PackageTypeList: ['default', 'vip', 'basic', 'trial']
92030
92227
  }),
92031
- this.describeEnvs({
92228
+ this.describeEnvInfo({
92032
92229
  EnvId: envId
92033
92230
  }),
92034
92231
  this.describeBillingInfo({
92035
92232
  EnvId: envId
92036
92233
  })
92037
92234
  ]);
92038
- const envInfo = (_a = envRes.EnvList) === null || _a === void 0 ? void 0 : _a[0];
92235
+ const envBaseInfo = (_a = envInfoRes === null || envInfoRes === void 0 ? void 0 : envInfoRes.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo;
92039
92236
  const billingInfo = (_b = billingRes.EnvBillingInfoList) === null || _b === void 0 ? void 0 : _b[0];
92040
- if (!envInfo || !billingInfo || !billingInfo.PackageId) {
92237
+ if (!(envBaseInfo === null || envBaseInfo === void 0 ? void 0 : envBaseInfo.EnvId) || !billingInfo || !billingInfo.PackageId) {
92041
92238
  throw new error_1.CloudBaseError(`Invalid env id: ${envId}`);
92042
92239
  }
92043
92240
  const oldPackageInfo = (_c = packageRes.PackageList) === null || _c === void 0 ? void 0 : _c.find(item => item.PackageName === billingInfo.PackageId);
@@ -92048,7 +92245,7 @@ class EnvService {
92048
92245
  if (!newPackageInfo) {
92049
92246
  throw new error_1.CloudBaseError(`Invalid package id: ${newPackageId}`);
92050
92247
  }
92051
- const region = envInfo.Region;
92248
+ const region = envBaseInfo.Region;
92052
92249
  const newBillTags = this.parseBillTags(newPackageInfo.BillTags);
92053
92250
  const oldBillTags = this.parseBillTags(oldPackageInfo.BillTags);
92054
92251
  let userNumber = 0;
@@ -101477,6 +101674,7 @@ const DEFAULT_PLUGINS = [
101477
101674
  "cloudrun",
101478
101675
  "gateway",
101479
101676
  "app-auth",
101677
+ "apps",
101480
101678
  "permissions",
101481
101679
  "logs",
101482
101680
  "agents",
@@ -101606,7 +101804,9 @@ async function createCloudBaseMcpServer(options) {
101606
101804
  }
101607
101805
  catch (error) {
101608
101806
  if ((0, tool_result_js_1.isToolPayloadError)(error)) {
101609
- return (0, tool_result_js_1.buildJsonToolResult)(error.payload);
101807
+ // 在结构化错误返回中注入 MCP 版本号
101808
+ const payload = { ...error.payload, mcpVersion: version };
101809
+ return (0, tool_result_js_1.buildJsonToolResult)(payload);
101610
101810
  }
101611
101811
  throw error;
101612
101812
  }
@@ -111556,6 +111756,115 @@ Writable.prototype._destroy = function (err, cb) {
111556
111756
  cb(err);
111557
111757
  };
111558
111758
 
111759
+ /***/ }),
111760
+
111761
+ /***/ 34800:
111762
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
111763
+
111764
+ "use strict";
111765
+
111766
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
111767
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
111768
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
111769
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
111770
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
111771
+ };
111772
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
111773
+ exports.AiModelService = void 0;
111774
+ const utils_1 = __webpack_require__(62358);
111775
+ class AiModelService {
111776
+ constructor(environment) {
111777
+ this.environment = environment;
111778
+ this.tcbService = new utils_1.CloudService(environment.cloudBaseContext, 'tcb', '2018-06-08');
111779
+ }
111780
+ /**
111781
+ * 创建 AI 模型配置分组
111782
+ */
111783
+ async createAIModel(options) {
111784
+ const { EnvId } = this.environment.lazyEnvironmentConfig;
111785
+ const { envId, groupName, baseUrl, models, remark, status, secret } = options;
111786
+ if (!groupName || groupName.trim().length === 0) {
111787
+ throw new Error('groupName is required');
111788
+ }
111789
+ if (groupName.toLowerCase().startsWith('cloudbase')) {
111790
+ throw new Error('groupName cannot start with cloudbase');
111791
+ }
111792
+ if (status !== undefined && status !== 1 && status !== 2) {
111793
+ throw new Error('status must be 1 or 2');
111794
+ }
111795
+ return this.tcbService.request('CreateAIModel', Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ EnvId: envId || EnvId, GroupName: groupName }, (baseUrl !== undefined ? { BaseUrl: baseUrl } : {})), (models !== undefined ? { Models: models } : {})), (remark !== undefined ? { Remark: remark } : {})), (status !== undefined ? { Status: status } : {})), (secret !== undefined ? { Secret: secret } : {})));
111796
+ }
111797
+ /**
111798
+ * 更新 AI 模型配置分组
111799
+ */
111800
+ async updateAIModel(options) {
111801
+ const { EnvId } = this.environment.lazyEnvironmentConfig;
111802
+ const { envId, groupName, baseUrl, models, remark, status, secret } = options;
111803
+ if (!groupName || groupName.trim().length === 0) {
111804
+ throw new Error('groupName is required');
111805
+ }
111806
+ if (status !== undefined && status !== 1 && status !== 2) {
111807
+ throw new Error('status must be 1 or 2');
111808
+ }
111809
+ return this.tcbService.request('UpdateAIModel', Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ EnvId: envId || EnvId, GroupName: groupName }, (baseUrl !== undefined ? { BaseUrl: baseUrl } : {})), (models !== undefined ? { Models: models } : {})), (remark !== undefined ? { Remark: remark } : {})), (status !== undefined ? { Status: status } : {})), (secret !== undefined ? { Secret: secret } : {})));
111810
+ }
111811
+ /**
111812
+ * 删除 AI 模型配置分组,支持批量删除
111813
+ */
111814
+ async deleteAIModel(options) {
111815
+ const { EnvId } = this.environment.lazyEnvironmentConfig;
111816
+ const { envId, groupNames } = options;
111817
+ if (!Array.isArray(groupNames) || groupNames.length === 0) {
111818
+ throw new Error('groupNames is required');
111819
+ }
111820
+ const invalidGroupName = groupNames.find(item => typeof item !== 'string' || item.trim().length === 0);
111821
+ if (invalidGroupName !== undefined) {
111822
+ throw new Error('groupNames contains invalid item');
111823
+ }
111824
+ return this.tcbService.request('DeleteAIModel', {
111825
+ EnvId: envId || EnvId,
111826
+ GroupNames: groupNames
111827
+ });
111828
+ }
111829
+ /**
111830
+ * 查询指定云开发环境下已配置的 AI 模型分组列表
111831
+ */
111832
+ async describeAIModels(options = {}) {
111833
+ const { EnvId } = this.environment.lazyEnvironmentConfig;
111834
+ const { envId } = options;
111835
+ return this.tcbService.request('DescribeAIModels', {
111836
+ EnvId: envId || EnvId
111837
+ });
111838
+ }
111839
+ /**
111840
+ * 查询云开发平台支持的托管类型 AI 模型列表
111841
+ */
111842
+ async describeManagedAIModelList(options = {}) {
111843
+ const { EnvId } = this.environment.lazyEnvironmentConfig;
111844
+ const { envId } = options;
111845
+ return this.tcbService.request('DescribeManagedAIModelList', {
111846
+ EnvId: envId || EnvId
111847
+ });
111848
+ }
111849
+ }
111850
+ exports.AiModelService = AiModelService;
111851
+ __decorate([
111852
+ (0, utils_1.preLazy)()
111853
+ ], AiModelService.prototype, "createAIModel", null);
111854
+ __decorate([
111855
+ (0, utils_1.preLazy)()
111856
+ ], AiModelService.prototype, "updateAIModel", null);
111857
+ __decorate([
111858
+ (0, utils_1.preLazy)()
111859
+ ], AiModelService.prototype, "deleteAIModel", null);
111860
+ __decorate([
111861
+ (0, utils_1.preLazy)()
111862
+ ], AiModelService.prototype, "describeAIModels", null);
111863
+ __decorate([
111864
+ (0, utils_1.preLazy)()
111865
+ ], AiModelService.prototype, "describeManagedAIModelList", null);
111866
+
111867
+
111559
111868
  /***/ }),
111560
111869
 
111561
111870
  /***/ 34836:
@@ -115168,6 +115477,7 @@ const cloudbase_manager_js_1 = __webpack_require__(3431);
115168
115477
  const json_content_js_1 = __webpack_require__(4365);
115169
115478
  const QUERY_APP_ACTIONS = ["listApps", "getApp", "listAppVersions", "getAppVersion"];
115170
115479
  const MANAGE_APP_ACTIONS = ["deployApp", "deleteApp", "deleteAppVersion"];
115480
+ const APP_FRAMEWORKS = ["vue", "react", "next", "nuxt", "vite", "angular", "static"];
115171
115481
  function buildEnvelope(data, message) {
115172
115482
  return {
115173
115483
  success: true,
@@ -115190,15 +115500,24 @@ function registerAppTools(server) {
115190
115500
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
115191
115501
  server.registerTool?.("queryApps", {
115192
115502
  title: "查询 CloudApp",
115193
- description: "CloudApp 域统一只读入口。支持应用、版本列表与版本详情查询。",
115503
+ description: "CloudApp 域统一只读入口。可先查应用/版本,再在重新部署后用 listAppVersions 或 getAppVersion 按 serviceName 验证是否生成了新版本与最新构建状态。",
115194
115504
  inputSchema: {
115195
115505
  action: zod_1.z.enum(QUERY_APP_ACTIONS),
115196
- serviceName: zod_1.z.string().optional(),
115197
- searchKey: zod_1.z.string().optional(),
115198
- pageNo: zod_1.z.number().optional(),
115199
- pageSize: zod_1.z.number().optional(),
115200
- versionName: zod_1.z.string().optional(),
115201
- buildId: zod_1.z.string().optional(),
115506
+ serviceName: zod_1.z
115507
+ .string()
115508
+ .optional()
115509
+ .describe("CloudApp 服务名。getApp / listAppVersions / getAppVersion 时必填;重新部署后复用同一个 serviceName 查询版本历史。"),
115510
+ searchKey: zod_1.z.string().optional().describe("按应用服务名模糊搜索关键词,仅 action=listApps 时使用。"),
115511
+ pageNo: zod_1.z.number().optional().describe("分页页码,从 1 开始。"),
115512
+ pageSize: zod_1.z.number().optional().describe("分页大小。"),
115513
+ versionName: zod_1.z
115514
+ .string()
115515
+ .optional()
115516
+ .describe("版本名称。getAppVersion 时可与 buildId 二选一;已知版本号时优先传该值。"),
115517
+ buildId: zod_1.z
115518
+ .string()
115519
+ .optional()
115520
+ .describe("构建 ID。getAppVersion 时可与 versionName 二选一;部署返回 BuildId 后可直接用它轮询状态。"),
115202
115521
  },
115203
115522
  annotations: {
115204
115523
  readOnlyHint: true,
@@ -115277,20 +115596,49 @@ function registerAppTools(server) {
115277
115596
  });
115278
115597
  server.registerTool?.("manageApps", {
115279
115598
  title: "管理 CloudApp",
115280
- description: "CloudApp 域统一写入口。首轮仅支持静态部署、删除应用和删除版本。",
115599
+ description: "CloudApp 域统一写入口。action=deployApp 会先 uploadCode 再 createApp;首次调用创建应用,后续复用同一个 serviceName 会直接触发重新部署并生成新版本,无需先删除旧应用。",
115281
115600
  inputSchema: {
115282
115601
  action: zod_1.z.enum(MANAGE_APP_ACTIONS),
115283
- serviceName: zod_1.z.string(),
115284
- localPath: zod_1.z.string().optional(),
115285
- appPath: zod_1.z.string().optional(),
115286
- buildPath: zod_1.z.string().optional(),
115287
- framework: zod_1.z.string().optional(),
115288
- nodeJsVersion: zod_1.z.string().optional(),
115289
- installCmd: zod_1.z.string().optional(),
115290
- buildCmd: zod_1.z.string().optional(),
115291
- deployCmd: zod_1.z.string().optional(),
115292
- ignore: zod_1.z.array(zod_1.z.string()).optional(),
115293
- versionName: zod_1.z.string().optional(),
115602
+ serviceName: zod_1.z
115603
+ .string()
115604
+ .describe("CloudApp 服务名。deployApp 时复用现有 serviceName 会新增一个部署版本并触发重新部署,而不是删除重建。"),
115605
+ filePath: zod_1.z
115606
+ .string()
115607
+ .optional()
115608
+ .describe("要上传并部署的本地项目根目录或 zip 文件绝对路径。deployApp 时必填;通常传源码所在目录而不是 build 产物目录,构建产物目录请用 buildPath 指定。"),
115609
+ appPath: zod_1.z
115610
+ .string()
115611
+ .optional()
115612
+ .describe("应用线上访问路径(hosting mount path),例如 /my-web-app。不是本地目录路径;省略时默认为 /serviceName。"),
115613
+ buildPath: zod_1.z
115614
+ .string()
115615
+ .optional()
115616
+ .describe("构建产物目录,相对于 filePath,例如 dist 或 build。纯静态 HTML 如果入口文件直接在项目根目录,可省略。"),
115617
+ framework: zod_1.z
115618
+ .enum(APP_FRAMEWORKS)
115619
+ .optional()
115620
+ .describe("前端框架类型。可选值:vue、react、next、nuxt、vite、angular、static;纯 HTML/静态站点请传 static。"),
115621
+ nodeJsVersion: zod_1.z
115622
+ .string()
115623
+ .optional()
115624
+ .describe("构建时使用的 Node.js 版本;不传时由 CloudApp 使用默认值。"),
115625
+ installCmd: zod_1.z
115626
+ .string()
115627
+ .optional()
115628
+ .describe("依赖安装命令,例如 npm install;静态资源无需安装依赖时可省略。"),
115629
+ buildCmd: zod_1.z
115630
+ .string()
115631
+ .optional()
115632
+ .describe("构建命令,例如 npm run build;纯静态 HTML 无构建步骤时可省略。"),
115633
+ deployCmd: zod_1.z
115634
+ .string()
115635
+ .optional()
115636
+ .describe("自定义部署命令。通常无需填写,只有在默认部署步骤不满足要求时才传。"),
115637
+ ignore: zod_1.z.array(zod_1.z.string()).optional().describe("上传时忽略的文件/目录 glob 模式,例如 **/node_modules/**。"),
115638
+ versionName: zod_1.z
115639
+ .string()
115640
+ .optional()
115641
+ .describe("要删除的历史版本名,仅 action=deleteAppVersion 时必填。"),
115294
115642
  },
115295
115643
  annotations: {
115296
115644
  readOnlyHint: false,
@@ -115299,7 +115647,7 @@ function registerAppTools(server) {
115299
115647
  openWorldHint: true,
115300
115648
  category: "apps",
115301
115649
  },
115302
- }, async ({ action, serviceName, localPath, appPath, buildPath, framework, nodeJsVersion, installCmd, buildCmd, deployCmd, ignore, versionName, }) => {
115650
+ }, async ({ action, serviceName, filePath, appPath, buildPath, framework, nodeJsVersion, installCmd, buildCmd, deployCmd, ignore, versionName, }) => {
115303
115651
  try {
115304
115652
  const cloudbase = await getManager();
115305
115653
  const appService = getCloudAppService(cloudbase);
@@ -115307,13 +115655,13 @@ function registerAppTools(server) {
115307
115655
  throw new Error("当前 manager 未提供 cloudAppService");
115308
115656
  }
115309
115657
  if (action === "deployApp") {
115310
- if (!localPath) {
115311
- throw new Error("action=deployApp 时必须提供 localPath");
115658
+ if (!filePath) {
115659
+ throw new Error("action=deployApp 时必须提供 filePath");
115312
115660
  }
115313
115661
  const uploadResult = await appService.uploadCode({
115314
115662
  deployType: "static-hosting",
115315
115663
  serviceName,
115316
- localPath,
115664
+ localPath: filePath,
115317
115665
  ignore,
115318
115666
  });
115319
115667
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, uploadResult);
@@ -116689,7 +117037,65 @@ const fs_1 = __importDefault(__webpack_require__(29021));
116689
117037
  const path_1 = __importDefault(__webpack_require__(39902));
116690
117038
  const zod_1 = __webpack_require__(21614);
116691
117039
  const cloudbase_manager_js_1 = __webpack_require__(3431);
117040
+ const cloud_mode_js_1 = __webpack_require__(89684);
116692
117041
  const notification_js_1 = __webpack_require__(32720);
117042
+ const tool_result_js_1 = __webpack_require__(9835);
117043
+ const routingRuleSchema = zod_1.z.object({
117044
+ keyPrefixEquals: zod_1.z.string().optional().describe('匹配前缀规则,例如 app/ 或 assets/。与 httpErrorCodeReturnedEquals 二选一或按 CloudBase 规则组合使用。'),
117045
+ httpErrorCodeReturnedEquals: zod_1.z.string().optional().describe('匹配 HTTP 错误码,例如 404。SPA 回退常用 404。'),
117046
+ replaceKeyWith: zod_1.z.string().optional().describe('把匹配结果替换为固定文件路径,例如 index.html。'),
117047
+ replaceKeyPrefixWith: zod_1.z.string().optional().describe('把匹配前缀替换成新的前缀路径。'),
117048
+ });
117049
+ const domainConfigSchema = zod_1.z.object({
117050
+ Refer: zod_1.z.object({
117051
+ Switch: zod_1.z.string().describe('Referer 防盗链开关,例如 on/off。'),
117052
+ RefererRules: zod_1.z.array(zod_1.z.object({
117053
+ RefererType: zod_1.z.string().describe('Referer 规则类型。'),
117054
+ Referers: zod_1.z.array(zod_1.z.string()).describe('Referer 规则值列表。'),
117055
+ AllowEmpty: zod_1.z.boolean().describe('是否允许空 Referer。'),
117056
+ })).optional().describe('Referer 规则列表。'),
117057
+ }).optional().describe('Referer 防盗链配置。'),
117058
+ Cache: zod_1.z.array(zod_1.z.object({
117059
+ RuleType: zod_1.z.string().describe('缓存规则类型,例如 fileType/path。'),
117060
+ RuleValue: zod_1.z.string().describe('规则匹配值。'),
117061
+ CacheTtl: zod_1.z.number().describe('缓存 TTL,单位秒。'),
117062
+ })).optional().describe('CDN 缓存规则列表。'),
117063
+ IpFilter: zod_1.z.object({
117064
+ Switch: zod_1.z.string().describe('IP 访问控制开关,例如 on/off。'),
117065
+ FilterType: zod_1.z.string().optional().describe('过滤类型,例如 blacklist/whitelist。'),
117066
+ Filters: zod_1.z.array(zod_1.z.string()).optional().describe('IP 规则列表。'),
117067
+ }).optional().describe('IP 访问控制配置。'),
117068
+ IpFreqLimit: zod_1.z.object({
117069
+ Switch: zod_1.z.string().describe('IP 频控开关,例如 on/off。'),
117070
+ Qps: zod_1.z.number().optional().describe('每个 IP 的 QPS 上限。'),
117071
+ }).optional().describe('IP 频控配置。'),
117072
+ });
117073
+ const queryHostingInputSchema = {
117074
+ action: zod_1.z.enum(['websiteConfig', 'status', 'findFiles', 'listFiles', 'domainStatus']).describe('查询类型:websiteConfig=查询静态托管网站文档配置与站点域名信息,status=查询静态托管服务状态,findFiles=按前缀查找托管文件,listFiles=列出静态托管中的全部文件,domainStatus=查询自定义域名配置与生效状态。该工具严格只读,不会修改任何资源。'),
117075
+ prefix: zod_1.z.string().optional().describe('文件前缀过滤条件。仅 action=findFiles 时使用,例如 app/ 或 assets/logo。'),
117076
+ marker: zod_1.z.string().optional().describe('分页起始标记。仅 action=findFiles 时使用,用于续查上一页之后的结果。'),
117077
+ maxKeys: zod_1.z.number().int().positive().optional().describe('单次返回的最大文件条数。仅 action=findFiles 时使用。'),
117078
+ domains: zod_1.z.array(zod_1.z.string()).optional().describe('要查询的自定义域名列表。仅 action=domainStatus 时使用,例如 ["www.example.com"]。'),
117079
+ };
117080
+ const manageHostingInputSchema = {
117081
+ action: zod_1.z.enum(['upload', 'delete', 'setWebsiteDocument', 'enableService', 'bindDomain', 'unbindDomain', 'updateDomain', 'downloadFile', 'downloadDirectory']).describe('管理类型:upload=上传本地构建产物到静态托管,delete=删除静态托管文件或目录,setWebsiteDocument=设置首页/错误页/路由规则,enableService=开通静态托管服务,bindDomain=绑定自定义域名,unbindDomain=解绑自定义域名,updateDomain=更新域名缓存/防盗链/IP 规则,downloadFile=下载单个托管文件到本地,downloadDirectory=下载托管目录到本地。'),
117082
+ localPath: zod_1.z.string().optional().describe('本地路径。action=upload 时表示要上传的本地文件/目录路径;action=downloadFile 或 downloadDirectory 时表示下载到本地的目标路径。建议传绝对路径。'),
117083
+ cloudPath: zod_1.z.string().optional().describe('静态托管中的目标路径。action=upload 时表示上传后的托管路径;action=delete/downloadFile/downloadDirectory 时表示托管侧文件或目录路径。'),
117084
+ files: zod_1.z.array(zod_1.z.object({
117085
+ localPath: zod_1.z.string().describe('单个待上传文件的本地绝对路径。'),
117086
+ cloudPath: zod_1.z.string().describe('该文件上传到静态托管后的托管路径。'),
117087
+ })).default([]).describe('多文件上传配置。仅 action=upload 时可选;传入后会逐项上传,不再依赖单个 localPath/cloudPath。'),
117088
+ ignore: zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]).optional().describe('上传时忽略的文件模式。仅 action=upload 时可选,例如 node_modules 或 ["**/*.map", "**/.DS_Store"]。'),
117089
+ isDir: zod_1.z.boolean().optional().default(false).describe('是否把 cloudPath 视为目录。仅 action=delete 时使用;true=删除目录,false=删除单个文件。'),
117090
+ confirm: zod_1.z.boolean().optional().default(false).describe('高风险操作确认开关。action=delete 和 action=unbindDomain 时必须显式传 true,避免误删文件或误解绑域名。'),
117091
+ indexDocument: zod_1.z.string().optional().describe('网站首页文档名称。仅 action=setWebsiteDocument 时必填,例如 index.html。'),
117092
+ errorDocument: zod_1.z.string().optional().describe('错误页文档名称。仅 action=setWebsiteDocument 时可选,例如 404.html。'),
117093
+ routingRules: zod_1.z.array(routingRuleSchema).optional().describe('网站路由规则列表。仅 action=setWebsiteDocument 时可选。SPA 常见配置是将 404 重写到 index.html。'),
117094
+ domain: zod_1.z.string().optional().describe('自定义域名。action=bindDomain / unbindDomain / updateDomain 时使用,例如 www.example.com。'),
117095
+ certId: zod_1.z.string().optional().describe('证书 ID。仅 action=bindDomain 时必填。'),
117096
+ domainId: zod_1.z.number().optional().describe('域名 ID。仅 action=updateDomain 时必填,用于精确更新指定域名配置。'),
117097
+ domainConfig: domainConfigSchema.optional().describe('域名配置。仅 action=updateDomain 时必填,支持缓存、Referer、防盗链、IP 规则与频控。'),
117098
+ };
116693
117099
  function isDirectoryUploadTarget(localPath, cloudPath) {
116694
117100
  if (localPath) {
116695
117101
  try {
@@ -116719,277 +117125,661 @@ function buildHostingAccessUrl(staticDomain, cloudPath, localPath) {
116719
117125
  const pathname = isDirectory ? `${normalizedCloudPath}/` : normalizedCloudPath;
116720
117126
  return `https://${staticDomain}/${pathname}`;
116721
117127
  }
116722
- function buildUploadFilesErrorMessage(error, localPath) {
117128
+ function buildUploadErrorMessage(error, localPath) {
116723
117129
  const baseMessage = error instanceof Error ? error.message : String(error);
116724
117130
  const suggestions = [];
116725
117131
  if (/路径不存在|无读写权限/i.test(baseMessage)) {
116726
117132
  if (localPath) {
116727
117133
  suggestions.push(`请先确认本地路径 \`${localPath}\` 存在且当前进程有读取权限。`);
116728
117134
  }
116729
- suggestions.push("如果报错的是构建产物中的某个静态资源文件,请检查构建后的资源引用路径是否正确。");
116730
- suggestions.push("若站点部署到子路径,请确认 `publicPath`、`base`、`assetPrefix` 等配置没有把资源指向不存在的位置。");
117135
+ suggestions.push('如果报错的是构建产物中的某个静态资源文件,请检查构建后的资源引用路径是否正确。');
117136
+ suggestions.push('若站点部署到子路径,请确认 publicPath、base、assetPrefix 等配置没有把资源指向不存在的位置。');
116731
117137
  }
116732
117138
  if (suggestions.length === 0) {
116733
- suggestions.push("请检查上传目录、文件权限和构建产物完整性后重试。");
117139
+ suggestions.push('请检查上传目录、文件权限和构建产物完整性后重试。');
117140
+ }
117141
+ return `[manageHosting(upload)] ${baseMessage}\n建议:${suggestions.join(' ')}`;
117142
+ }
117143
+ function isRecord(value) {
117144
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
117145
+ }
117146
+ function getRecordString(record, keys) {
117147
+ for (const key of keys) {
117148
+ const value = record[key];
117149
+ if (typeof value === 'string' && value.trim()) {
117150
+ return value;
117151
+ }
117152
+ }
117153
+ return undefined;
117154
+ }
117155
+ function collectDomainRecords(value, seen = new Set(), depth = 0) {
117156
+ if (depth > 5 || !value || typeof value !== 'object' || seen.has(value)) {
117157
+ return [];
117158
+ }
117159
+ seen.add(value);
117160
+ if (Array.isArray(value)) {
117161
+ return value.flatMap((item) => collectDomainRecords(item, seen, depth + 1));
117162
+ }
117163
+ const record = value;
117164
+ const current = getRecordString(record, ['Domain', 'domain'])
117165
+ ? [record]
117166
+ : [];
117167
+ return current.concat(Object.values(record).flatMap((item) => collectDomainRecords(item, seen, depth + 1)));
117168
+ }
117169
+ function summarizeHostingDomainCheck(domains, result) {
117170
+ const targetSet = new Set(domains);
117171
+ const matchedRecords = collectDomainRecords(result).filter((record) => {
117172
+ const domain = getRecordString(record, ['Domain', 'domain']);
117173
+ return domain ? targetSet.has(domain) : false;
117174
+ });
117175
+ const matchedDomains = Array.from(new Set(matchedRecords
117176
+ .map((record) => getRecordString(record, ['Domain', 'domain']))
117177
+ .filter((domain) => Boolean(domain))));
117178
+ return {
117179
+ matchedDomains,
117180
+ missingDomains: domains.filter((domain) => !matchedDomains.includes(domain)),
117181
+ domainDetails: matchedRecords,
117182
+ };
117183
+ }
117184
+ function extractTaskStatus(result) {
117185
+ const candidates = [result];
117186
+ if (isRecord(result)) {
117187
+ candidates.push(result.Data, result.Task, result.Result);
117188
+ }
117189
+ for (const candidate of candidates) {
117190
+ if (!isRecord(candidate)) {
117191
+ continue;
117192
+ }
117193
+ const value = getRecordString(candidate, ['Status', 'status', 'TaskStatus', 'State']);
117194
+ if (value) {
117195
+ return value;
117196
+ }
117197
+ }
117198
+ return undefined;
117199
+ }
117200
+ function buildDomainStatusNextStep(domains) {
117201
+ return {
117202
+ tool: 'queryHosting',
117203
+ action: 'domainStatus',
117204
+ suggested_args: {
117205
+ action: 'domainStatus',
117206
+ domains,
117207
+ },
117208
+ };
117209
+ }
117210
+ function buildStatusNextStep() {
117211
+ return {
117212
+ tool: 'queryHosting',
117213
+ action: 'status',
117214
+ suggested_args: {
117215
+ action: 'status',
117216
+ },
117217
+ };
117218
+ }
117219
+ function buildWebsiteConfigNextStep() {
117220
+ return {
117221
+ tool: 'queryHosting',
117222
+ action: 'websiteConfig',
117223
+ suggested_args: {
117224
+ action: 'websiteConfig',
117225
+ },
117226
+ };
117227
+ }
117228
+ function buildFindFilesNextStep(prefix) {
117229
+ return {
117230
+ tool: 'queryHosting',
117231
+ action: 'findFiles',
117232
+ suggested_args: {
117233
+ action: 'findFiles',
117234
+ prefix,
117235
+ },
117236
+ };
117237
+ }
117238
+ async function callTcbHostingAction(cloudbase, action, param, logger) {
117239
+ const service = cloudbase.commonService?.('tcb', '2018-06-08');
117240
+ if (!service?.call) {
117241
+ throw new Error(`当前 CloudBase Manager 实例不支持 commonService.call,无法执行 ${action}。`);
117242
+ }
117243
+ const result = await service.call({
117244
+ Action: action,
117245
+ Param: param,
117246
+ });
117247
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, result);
117248
+ return result;
117249
+ }
117250
+ function extractStaticStores(value) {
117251
+ if (!value || typeof value !== 'object') {
117252
+ return [];
117253
+ }
117254
+ const payload = value;
117255
+ const stores = payload.Data;
117256
+ return Array.isArray(stores) ? stores.filter(isRecord) : [];
117257
+ }
117258
+ async function describeHostingDomainTask(cloudbase, cloudBaseOptions, logger) {
117259
+ try {
117260
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
117261
+ const result = await callTcbHostingAction(cloudbase, 'DescribeHostingDomainTask', { EnvId: envId }, logger);
117262
+ return {
117263
+ rawStatus: extractTaskStatus(result),
117264
+ raw: result,
117265
+ };
117266
+ }
117267
+ catch {
117268
+ return undefined;
117269
+ }
117270
+ }
117271
+ function buildDomainMutationResult(params) {
117272
+ const { action, domain, certId, domainId, domainConfig, result, taskStatus } = params;
117273
+ const actionLabel = action === 'bindDomain'
117274
+ ? '绑定'
117275
+ : action === 'unbindDomain'
117276
+ ? '解绑'
117277
+ : '修改';
117278
+ const successIndicator = action === 'bindDomain'
117279
+ ? `继续调用 queryHosting(action="domainStatus", domains=["${domain}"]),直到返回中出现该域名,并且相关状态字段显示为已生效。`
117280
+ : action === 'unbindDomain'
117281
+ ? `继续调用 queryHosting(action="domainStatus", domains=["${domain}"]),直到返回中不再出现该域名。`
117282
+ : `继续调用 queryHosting(action="domainStatus", domains=["${domain}"]),确认返回中的配置字段已更新为最新值。`;
117283
+ return {
117284
+ success: true,
117285
+ data: {
117286
+ action,
117287
+ targetDomains: [domain],
117288
+ ...(certId ? { certId } : {}),
117289
+ ...(domainId !== undefined ? { domainId } : {}),
117290
+ ...(domainConfig ? { domainConfig } : {}),
117291
+ asyncState: 'PENDING',
117292
+ ...(taskStatus ? { taskStatus } : {}),
117293
+ propagation: {
117294
+ requiresPolling: true,
117295
+ pollTool: 'queryHosting',
117296
+ pollAction: 'domainStatus',
117297
+ pollIntervalSuggestionSeconds: 30,
117298
+ timeoutSuggestionSeconds: 600,
117299
+ successIndicator,
117300
+ },
117301
+ nextActions: [buildDomainStatusNextStep([domain])],
117302
+ result,
117303
+ },
117304
+ message: `静态托管域名${actionLabel}请求已提交。域名配置、证书校验和边缘侧传播通常需要 30 秒到 10 分钟,请继续调用 queryHosting(action="domainStatus") 确认最终结果。`,
117305
+ };
117306
+ }
117307
+ async function getHostingWebsiteConfig(cloudbase, logger) {
117308
+ const websiteConfig = await cloudbase.hosting.getWebsiteConfig();
117309
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, websiteConfig);
117310
+ const hostingResult = {
117311
+ ...websiteConfig,
117312
+ CdnDomain: websiteConfig.CdnDomain ?? null,
117313
+ Bucket: websiteConfig.Bucket ?? null,
117314
+ };
117315
+ try {
117316
+ const envInfo = await cloudbase.env.getEnvInfo();
117317
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, envInfo);
117318
+ hostingResult.CdnDomain = envInfo.EnvInfo?.StaticStorages?.[0]?.StaticDomain ?? hostingResult.CdnDomain;
117319
+ hostingResult.Bucket = envInfo.EnvInfo?.StaticStorages?.[0]?.Bucket ?? hostingResult.Bucket;
117320
+ }
117321
+ catch {
117322
+ // Ignore enrichment failures and return the website config as-is.
117323
+ }
117324
+ return hostingResult;
117325
+ }
117326
+ async function resolveHostingStaticDomain(cloudbase, logger) {
117327
+ try {
117328
+ const envInfo = await cloudbase.env.getEnvInfo();
117329
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, envInfo);
117330
+ return envInfo.EnvInfo?.StaticStorages?.[0]?.StaticDomain;
117331
+ }
117332
+ catch {
117333
+ return undefined;
117334
+ }
117335
+ }
117336
+ function buildFailureResult(action, error) {
117337
+ return (0, tool_result_js_1.buildJsonToolResult)({
117338
+ success: false,
117339
+ errorCode: `HOSTING_${action.toUpperCase()}_FAILED`,
117340
+ message: error instanceof Error ? error.message : String(error),
117341
+ });
117342
+ }
117343
+ function normalizeFileFields(files) {
117344
+ if (!Array.isArray(files))
117345
+ return [];
117346
+ return files.map(file => {
117347
+ if (typeof file !== 'object' || file === null)
117348
+ return file;
117349
+ const record = file;
117350
+ return {
117351
+ key: record.Key ?? record.key ?? '',
117352
+ size: record.Size ?? record.size ?? 0,
117353
+ lastModified: record.LastModified ?? record.lastModified ?? '',
117354
+ ...record,
117355
+ };
117356
+ });
117357
+ }
117358
+ function normalizeHostingStatus(current) {
117359
+ if (!current)
117360
+ return null;
117361
+ return {
117362
+ ...current,
117363
+ status: current.Status ?? current.status ?? 'unknown',
117364
+ staticDomain: current.StaticDomain ?? current.staticDomain ?? null,
117365
+ bucket: current.Bucket ?? current.bucket ?? null,
117366
+ };
117367
+ }
117368
+ function ensureManageHostingActionAllowedInCloudMode(input) {
117369
+ if (!(0, cloud_mode_js_1.isCloudMode)()) {
117370
+ return;
117371
+ }
117372
+ if (input.action === 'upload' || input.action === 'downloadFile' || input.action === 'downloadDirectory') {
117373
+ throw new Error(`manageHosting(action="${input.action}") 在 cloud mode 下不可用,因为该操作依赖本地文件路径。请改用本地模式执行;若只需要远端管理静态托管,可继续使用 delete / setWebsiteDocument / enableService / bindDomain / unbindDomain / updateDomain。`);
116734
117374
  }
116735
- return `[uploadFiles] ${baseMessage}\n建议:${suggestions.join(" ")}`;
116736
117375
  }
116737
117376
  function registerHostingTools(server) {
116738
- // 获取 cloudBaseOptions,如果没有则为 undefined
116739
117377
  const cloudBaseOptions = server.cloudBaseOptions;
116740
- // 创建闭包函数来获取 CloudBase Manager
116741
117378
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
116742
- // uploadFiles - 上传文件到静态网站托管 (cloud-incompatible)
116743
- server.registerTool("uploadFiles", {
116744
- title: "上传静态文件",
116745
- description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。",
116746
- inputSchema: {
116747
- localPath: zod_1.z.string().optional().describe("本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。"),
116748
- cloudPath: zod_1.z.string().optional().describe("静态托管云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。云存储对象路径请改用 manageStorage。"),
116749
- files: zod_1.z.array(zod_1.z.object({
116750
- localPath: zod_1.z.string(),
116751
- cloudPath: zod_1.z.string()
116752
- })).default([]).describe("多文件上传配置"),
116753
- ignore: zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]).optional().describe("忽略文件模式")
116754
- },
117379
+ server.registerTool('queryHosting', {
117380
+ title: '查询静态托管',
117381
+ description: '查询 CloudBase 静态托管的只读信息。适合 AI 先做发现再决定下一步:action=websiteConfig 查询首页/错误页/路由规则与站点域名信息;action=status 查询托管服务状态;action=findFiles 按前缀查找文件;action=listFiles 列出全部托管文件;action=domainStatus 查询自定义域名的当前状态与配置。该工具不会产生任何副作用。',
117382
+ inputSchema: queryHostingInputSchema,
116755
117383
  annotations: {
116756
- readOnlyHint: false,
116757
- destructiveHint: false,
116758
- idempotentHint: false,
117384
+ readOnlyHint: true,
116759
117385
  openWorldHint: true,
116760
- category: "hosting"
116761
- }
116762
- }, async ({ localPath, cloudPath, files = [], ignore }) => {
116763
- const cloudbase = await getManager();
116764
- let result;
116765
- try {
116766
- result = await cloudbase.hosting.uploadFiles({
116767
- localPath,
116768
- cloudPath,
116769
- files,
116770
- ignore
116771
- });
116772
- }
116773
- catch (error) {
116774
- throw new Error(buildUploadFilesErrorMessage(error, localPath));
116775
- }
116776
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116777
- const uploadResult = result;
116778
- // 获取环境信息
116779
- const envInfo = await cloudbase.env.getEnvInfo();
116780
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, envInfo);
116781
- const staticDomain = envInfo.EnvInfo?.StaticStorages?.[0]?.StaticDomain;
116782
- const accessUrl = buildHostingAccessUrl(staticDomain, cloudPath, localPath);
116783
- // Send deployment notification to CodeBuddy IDE
117386
+ category: 'hosting',
117387
+ },
117388
+ }, async (args) => {
116784
117389
  try {
116785
- const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
116786
- // Extract project name from localPath
116787
- let projectName = "unknown";
116788
- if (localPath) {
116789
- try {
116790
- // If localPath is a file, get parent directory name; if it's a directory, get directory name
116791
- const stats = fs_1.default.statSync(localPath);
116792
- if (stats.isFile()) {
116793
- projectName = path_1.default.basename(path_1.default.dirname(localPath));
116794
- }
116795
- else {
116796
- projectName = path_1.default.basename(localPath);
117390
+ const input = args;
117391
+ const cloudbase = await getManager();
117392
+ switch (input.action) {
117393
+ case 'websiteConfig': {
117394
+ const websiteConfig = await getHostingWebsiteConfig(cloudbase, server.logger);
117395
+ return (0, tool_result_js_1.buildJsonToolResult)({
117396
+ success: true,
117397
+ data: {
117398
+ action: 'websiteConfig',
117399
+ websiteConfig,
117400
+ },
117401
+ message: '已获取静态托管网站文档配置与站点域名信息。',
117402
+ });
117403
+ }
117404
+ case 'status': {
117405
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
117406
+ const result = await callTcbHostingAction(cloudbase, 'DescribeStaticStore', { EnvId: envId }, server.logger);
117407
+ const hostingInfo = extractStaticStores(result);
117408
+ const current = normalizeHostingStatus(hostingInfo[0] ?? null);
117409
+ const normalizedHostingInfo = hostingInfo.map(item => normalizeHostingStatus(item));
117410
+ return (0, tool_result_js_1.buildJsonToolResult)({
117411
+ success: true,
117412
+ data: {
117413
+ action: 'status',
117414
+ enabled: hostingInfo.length > 0,
117415
+ current,
117416
+ hostingInfo: normalizedHostingInfo,
117417
+ result,
117418
+ },
117419
+ message: hostingInfo.length > 0
117420
+ ? '已获取静态托管服务状态。'
117421
+ : '静态托管服务当前未返回可用实例信息,可能尚未开通。',
117422
+ });
117423
+ }
117424
+ case 'findFiles': {
117425
+ if (!input.prefix) {
117426
+ throw new Error('queryHosting(action="findFiles") 需要提供 prefix,用于按前缀查找托管文件。');
116797
117427
  }
117428
+ const result = await cloudbase.hosting.findFiles({
117429
+ prefix: input.prefix,
117430
+ marker: input.marker,
117431
+ maxKeys: input.maxKeys,
117432
+ });
117433
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117434
+ const normalizedFiles = normalizeFileFields(result);
117435
+ return (0, tool_result_js_1.buildJsonToolResult)({
117436
+ success: true,
117437
+ data: {
117438
+ action: 'findFiles',
117439
+ prefix: input.prefix,
117440
+ marker: input.marker,
117441
+ maxKeys: input.maxKeys,
117442
+ files: normalizedFiles,
117443
+ result,
117444
+ },
117445
+ message: `已按前缀 \`${input.prefix}\` 查询静态托管文件,共 ${Array.isArray(normalizedFiles) ? normalizedFiles.length : 0} 个。`,
117446
+ });
116798
117447
  }
116799
- catch (statErr) {
116800
- // If stat fails, try to extract from path directly
116801
- projectName = path_1.default.basename(localPath);
117448
+ case 'listFiles': {
117449
+ const result = await cloudbase.hosting.listFiles();
117450
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117451
+ const normalizedFiles = normalizeFileFields(result);
117452
+ const maxKeys = input.maxKeys ?? 100;
117453
+ const start = input.marker ? parseInt(input.marker, 10) : 0;
117454
+ const paginatedFiles = normalizedFiles.slice(start, start + maxKeys);
117455
+ const nextMarker = start + maxKeys < normalizedFiles.length ? String(start + maxKeys) : undefined;
117456
+ return (0, tool_result_js_1.buildJsonToolResult)({
117457
+ success: true,
117458
+ data: {
117459
+ action: 'listFiles',
117460
+ files: paginatedFiles,
117461
+ totalCount: normalizedFiles.length,
117462
+ marker: input.marker,
117463
+ maxKeys,
117464
+ nextMarker,
117465
+ isTruncated: nextMarker !== undefined,
117466
+ },
117467
+ message: `已列出静态托管中的文件(第 ${start + 1}-${start + paginatedFiles.length} 个,共 ${normalizedFiles.length} 个)。${nextMarker ? ' 还有更多文件,可使用 nextMarker 继续查询。' : ''}`,
117468
+ });
117469
+ }
117470
+ case 'domainStatus': {
117471
+ if (!input.domains || input.domains.length === 0) {
117472
+ throw new Error('queryHosting(action="domainStatus") 需要提供 domains 数组,例如 ["www.example.com"]。');
117473
+ }
117474
+ const result = await cloudbase.hosting.tcbCheckResource({
117475
+ domains: input.domains,
117476
+ });
117477
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117478
+ const summary = summarizeHostingDomainCheck(input.domains, result);
117479
+ const allMatched = summary.missingDomains.length === 0;
117480
+ return (0, tool_result_js_1.buildJsonToolResult)({
117481
+ success: true,
117482
+ data: {
117483
+ action: 'domainStatus',
117484
+ queriedDomains: input.domains,
117485
+ matchedDomains: summary.matchedDomains,
117486
+ missingDomains: summary.missingDomains,
117487
+ domainDetails: summary.domainDetails,
117488
+ ...(allMatched
117489
+ ? {}
117490
+ : {
117491
+ propagation: {
117492
+ requiresPolling: true,
117493
+ pollTool: 'queryHosting',
117494
+ pollAction: 'domainStatus',
117495
+ pollIntervalSuggestionSeconds: 30,
117496
+ timeoutSuggestionSeconds: 600,
117497
+ successIndicator: '目标域名出现在返回结果中,并且相关状态字段显示为已生效。',
117498
+ },
117499
+ nextActions: [buildDomainStatusNextStep(summary.missingDomains)],
117500
+ }),
117501
+ result,
117502
+ },
117503
+ message: allMatched
117504
+ ? '已查询到目标静态托管域名配置。若这是绑定或修改后的确认步骤,请继续核对状态字段、证书信息和配置内容是否符合预期。'
117505
+ : '部分目标静态托管域名尚未在查询结果中出现。若这是绑定后的确认步骤,请继续调用 queryHosting(action="domainStatus") 直到结果收敛或达到超时。',
117506
+ });
116802
117507
  }
116803
117508
  }
116804
- // Build console URL
116805
- const consoleUrl = `https://tcb.cloud.tencent.com/dev?envId=${envId}#/static-hosting`;
116806
- // Send notification
116807
- await (0, notification_js_1.sendDeployNotification)(server, {
116808
- deployType: 'hosting',
116809
- url: accessUrl,
116810
- projectId: envId,
116811
- projectName: projectName,
116812
- consoleUrl: consoleUrl
116813
- });
116814
117509
  }
116815
- catch (notifyErr) {
116816
- // Notification failure should not affect deployment flow
116817
- // Error is already logged in sendDeployNotification
117510
+ catch (error) {
117511
+ const toolPayloadResult = (0, tool_result_js_1.toolPayloadErrorToResult)(error);
117512
+ if (toolPayloadResult) {
117513
+ return toolPayloadResult;
117514
+ }
117515
+ return buildFailureResult(args.action, error);
116818
117516
  }
116819
- return {
116820
- content: [
116821
- {
116822
- type: "text",
116823
- text: JSON.stringify({
116824
- ...uploadResult,
116825
- staticDomain,
116826
- message: "文件上传成功",
116827
- accessUrl: accessUrl
116828
- }, null, 2)
116829
- }
116830
- ]
116831
- };
116832
117517
  });
116833
- // deleteFiles - 删除静态网站托管文件
116834
- server.registerTool?.("deleteFiles", {
116835
- title: "删除静态文件",
116836
- description: "删除静态网站托管的文件或文件夹",
116837
- inputSchema: {
116838
- cloudPath: zod_1.z.string().describe("云端文件或文件夹路径"),
116839
- isDir: zod_1.z.boolean().default(false).describe("是否为文件夹")
116840
- },
117518
+ server.registerTool('manageHosting', {
117519
+ title: '管理静态托管',
117520
+ description: '管理 CloudBase 静态托管的变更操作。action=upload 上传本地构建产物;action=delete 删除托管文件或目录(必须 confirm=true);action=setWebsiteDocument 设置首页/错误页/路由规则;action=enableService 开通静态托管;action=bindDomain / unbindDomain / updateDomain 管理自定义域名;action=downloadFile / downloadDirectory 下载托管内容到本地。若任务只是查看配置、文件或域名状态,请改用 queryHosting。',
117521
+ inputSchema: manageHostingInputSchema,
116841
117522
  annotations: {
116842
117523
  readOnlyHint: false,
116843
117524
  destructiveHint: true,
116844
- idempotentHint: true,
117525
+ idempotentHint: false,
116845
117526
  openWorldHint: true,
116846
- category: "hosting"
116847
- }
116848
- }, async ({ cloudPath, isDir = false }) => {
116849
- const cloudbase = await getManager();
116850
- const result = await cloudbase.hosting.deleteFiles({
116851
- cloudPath,
116852
- isDir
116853
- });
116854
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116855
- return {
116856
- content: [
116857
- {
116858
- type: "text",
116859
- text: JSON.stringify(result, null, 2)
116860
- }
116861
- ]
116862
- };
116863
- });
116864
- // findFiles - 搜索文件
116865
- server.registerTool?.("findFiles", {
116866
- title: "搜索静态文件",
116867
- description: "搜索静态网站托管的文件",
116868
- inputSchema: {
116869
- prefix: zod_1.z.string().describe("匹配前缀"),
116870
- marker: zod_1.z.string().optional().describe("起始对象键标记"),
116871
- maxKeys: zod_1.z.number().optional().describe("单次返回最大条目数")
117527
+ category: 'hosting',
116872
117528
  },
116873
- annotations: {
116874
- readOnlyHint: true,
116875
- openWorldHint: true,
116876
- category: "hosting"
116877
- }
116878
- }, async ({ prefix, marker, maxKeys }) => {
116879
- const cloudbase = await getManager();
116880
- const result = await cloudbase.hosting.findFiles({
116881
- prefix,
116882
- marker,
116883
- maxKeys
116884
- });
116885
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116886
- return {
116887
- content: [
116888
- {
116889
- type: "text",
116890
- text: JSON.stringify(result, null, 2)
117529
+ }, async (args) => {
117530
+ try {
117531
+ const input = args;
117532
+ ensureManageHostingActionAllowedInCloudMode(input);
117533
+ const cloudbase = await getManager();
117534
+ switch (input.action) {
117535
+ case 'upload': {
117536
+ if ((!input.localPath || !input.cloudPath) && (!input.files || input.files.length === 0)) {
117537
+ throw new Error('manageHosting(action="upload") 需要提供 localPath + cloudPath,或提供 files 多文件上传列表。');
117538
+ }
117539
+ let result;
117540
+ try {
117541
+ result = await cloudbase.hosting.uploadFiles({
117542
+ localPath: input.localPath,
117543
+ cloudPath: input.cloudPath,
117544
+ files: input.files ?? [],
117545
+ ignore: input.ignore,
117546
+ });
117547
+ }
117548
+ catch (error) {
117549
+ throw new Error(buildUploadErrorMessage(error, input.localPath));
117550
+ }
117551
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117552
+ const staticDomain = await resolveHostingStaticDomain(cloudbase, server.logger);
117553
+ const accessUrl = buildHostingAccessUrl(staticDomain, input.cloudPath, input.localPath);
117554
+ try {
117555
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
117556
+ let projectName = 'unknown';
117557
+ if (input.localPath) {
117558
+ try {
117559
+ const stats = fs_1.default.statSync(input.localPath);
117560
+ projectName = stats.isFile()
117561
+ ? path_1.default.basename(path_1.default.dirname(input.localPath))
117562
+ : path_1.default.basename(input.localPath);
117563
+ }
117564
+ catch {
117565
+ projectName = path_1.default.basename(input.localPath);
117566
+ }
117567
+ }
117568
+ await (0, notification_js_1.sendDeployNotification)(server, {
117569
+ deployType: 'hosting',
117570
+ url: accessUrl,
117571
+ projectId: envId,
117572
+ projectName,
117573
+ consoleUrl: `https://tcb.cloud.tencent.com/dev?envId=${envId}#/static-hosting`,
117574
+ });
117575
+ }
117576
+ catch {
117577
+ // Notification failure should not block uploads.
117578
+ }
117579
+ const uploadPrefix = input.cloudPath ?? input.files?.[0]?.cloudPath ?? '';
117580
+ return (0, tool_result_js_1.buildJsonToolResult)({
117581
+ success: true,
117582
+ data: {
117583
+ action: 'upload',
117584
+ localPath: input.localPath,
117585
+ cloudPath: input.cloudPath,
117586
+ files: input.files ?? [],
117587
+ ignore: input.ignore,
117588
+ staticDomain,
117589
+ accessUrl,
117590
+ result,
117591
+ nextActions: uploadPrefix ? [buildFindFilesNextStep(uploadPrefix.replace(/^\/+/, ''))] : undefined,
117592
+ },
117593
+ message: '静态托管文件上传成功。若需要校验上传结果,请继续调用 queryHosting(action="findFiles") 或 queryHosting(action="listFiles")。',
117594
+ });
116891
117595
  }
116892
- ]
116893
- };
116894
- });
116895
- // domainManagement - 统一的域名管理工具
116896
- server.registerTool?.("domainManagement", {
116897
- title: "静态托管域名管理",
116898
- description: "统一的域名管理工具,支持绑定、解绑、查询和修改域名配置",
116899
- inputSchema: {
116900
- action: zod_1.z.enum(["create", "delete", "check", "modify"]).describe("操作类型: create=绑定域名, delete=解绑域名, check=查询域名配置, modify=修改域名配置"),
116901
- // 绑定域名参数
116902
- domain: zod_1.z.string().optional().describe("域名"),
116903
- certId: zod_1.z.string().optional().describe("证书ID(绑定域名时必需)"),
116904
- domains: zod_1.z.array(zod_1.z.string()).optional().describe("域名列表(查询配置时使用)"),
116905
- // 修改域名参数
116906
- domainId: zod_1.z.number().optional().describe("域名ID(修改配置时必需)"),
116907
- domainConfig: zod_1.z.object({
116908
- Refer: zod_1.z.object({
116909
- Switch: zod_1.z.string(),
116910
- RefererRules: zod_1.z.array(zod_1.z.object({
116911
- RefererType: zod_1.z.string(),
116912
- Referers: zod_1.z.array(zod_1.z.string()),
116913
- AllowEmpty: zod_1.z.boolean()
116914
- })).optional()
116915
- }).optional(),
116916
- Cache: zod_1.z.array(zod_1.z.object({
116917
- RuleType: zod_1.z.string(),
116918
- RuleValue: zod_1.z.string(),
116919
- CacheTtl: zod_1.z.number()
116920
- })).optional(),
116921
- IpFilter: zod_1.z.object({
116922
- Switch: zod_1.z.string(),
116923
- FilterType: zod_1.z.string().optional(),
116924
- Filters: zod_1.z.array(zod_1.z.string()).optional()
116925
- }).optional(),
116926
- IpFreqLimit: zod_1.z.object({
116927
- Switch: zod_1.z.string(),
116928
- Qps: zod_1.z.number().optional()
116929
- }).optional()
116930
- }).optional().describe("域名配置(修改配置时使用)")
116931
- },
116932
- annotations: {
116933
- readOnlyHint: false,
116934
- destructiveHint: false,
116935
- idempotentHint: false,
116936
- openWorldHint: true,
116937
- category: "hosting"
116938
- }
116939
- }, async ({ action, domain, certId, domains, domainId, domainConfig }) => {
116940
- const cloudbase = await getManager();
116941
- let result;
116942
- switch (action) {
116943
- case "create":
116944
- if (!domain || !certId) {
116945
- throw new Error("绑定域名需要提供域名和证书ID");
117596
+ case 'delete': {
117597
+ if (!input.cloudPath) {
117598
+ throw new Error('manageHosting(action="delete") 需要提供 cloudPath。');
117599
+ }
117600
+ if (!input.confirm) {
117601
+ throw new Error('manageHosting(action="delete") 是破坏性操作,必须显式传 confirm=true。');
117602
+ }
117603
+ const result = await cloudbase.hosting.deleteFiles({
117604
+ cloudPath: input.cloudPath,
117605
+ isDir: input.isDir ?? false,
117606
+ });
117607
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117608
+ // Post-validation: verify deletion was successful
117609
+ let deleteVerified = true;
117610
+ let verificationError;
117611
+ try {
117612
+ const checkResult = await cloudbase.hosting.findFiles({
117613
+ prefix: input.cloudPath,
117614
+ maxKeys: 1,
117615
+ });
117616
+ if (Array.isArray(checkResult) && checkResult.length > 0) {
117617
+ deleteVerified = false;
117618
+ verificationError = `删除后验证失败:文件仍在静态托管中`;
117619
+ }
117620
+ }
117621
+ catch (error) {
117622
+ // If query fails, assume deletion was successful
117623
+ // (file might have been deleted, causing query to return empty)
117624
+ }
117625
+ return (0, tool_result_js_1.buildJsonToolResult)({
117626
+ success: deleteVerified,
117627
+ data: {
117628
+ action: 'delete',
117629
+ cloudPath: input.cloudPath,
117630
+ isDir: input.isDir ?? false,
117631
+ result,
117632
+ verified: deleteVerified,
117633
+ ...(verificationError ? { error: verificationError } : {}),
117634
+ },
117635
+ message: deleteVerified
117636
+ ? `已删除静态托管${input.isDir ? '目录' : '文件'} \`${input.cloudPath}\`。`
117637
+ : `删除操作已提交,但验证发现文件可能未完全删除,请手动确认。`,
117638
+ });
116946
117639
  }
116947
- result = await cloudbase.hosting.CreateHostingDomain({
116948
- domain,
116949
- certId
116950
- });
116951
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116952
- break;
116953
- case "delete":
116954
- if (!domain) {
116955
- throw new Error("解绑域名需要提供域名");
117640
+ case 'setWebsiteDocument': {
117641
+ if (!input.indexDocument) {
117642
+ throw new Error('manageHosting(action="setWebsiteDocument") 需要提供 indexDocument,例如 index.html。');
117643
+ }
117644
+ const result = await cloudbase.hosting.setWebsiteDocument({
117645
+ indexDocument: input.indexDocument,
117646
+ errorDocument: input.errorDocument,
117647
+ routingRules: input.routingRules,
117648
+ });
117649
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117650
+ return (0, tool_result_js_1.buildJsonToolResult)({
117651
+ success: true,
117652
+ data: {
117653
+ action: 'setWebsiteDocument',
117654
+ indexDocument: input.indexDocument,
117655
+ errorDocument: input.errorDocument,
117656
+ routingRules: input.routingRules,
117657
+ result,
117658
+ nextActions: [buildWebsiteConfigNextStep()],
117659
+ },
117660
+ message: '静态托管网站文档配置已提交。若需要确认最终配置,请继续调用 queryHosting(action="websiteConfig")。',
117661
+ });
116956
117662
  }
116957
- result = await cloudbase.hosting.deleteHostingDomain({
116958
- domain
116959
- });
116960
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116961
- break;
116962
- case "check":
116963
- if (!domains || domains.length === 0) {
116964
- throw new Error("查询域名配置需要提供域名列表");
117663
+ case 'enableService': {
117664
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
117665
+ const result = await callTcbHostingAction(cloudbase, 'CreateStaticStore', { EnvId: envId }, server.logger);
117666
+ return (0, tool_result_js_1.buildJsonToolResult)({
117667
+ success: true,
117668
+ data: {
117669
+ action: 'enableService',
117670
+ asyncState: 'PENDING',
117671
+ result,
117672
+ nextActions: [buildStatusNextStep()],
117673
+ },
117674
+ message: '静态托管服务开通请求已提交。请继续调用 queryHosting(action="status") 确认服务是否已可用。',
117675
+ });
116965
117676
  }
116966
- result = await cloudbase.hosting.tcbCheckResource({
116967
- domains
116968
- });
116969
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116970
- break;
116971
- case "modify":
116972
- if (!domain || domainId === undefined || !domainConfig) {
116973
- throw new Error("修改域名配置需要提供域名、域名ID和配置信息");
117677
+ case 'bindDomain': {
117678
+ if (!input.domain || !input.certId) {
117679
+ throw new Error('manageHosting(action="bindDomain") 需要提供 domain 和 certId。');
117680
+ }
117681
+ const result = await cloudbase.hosting.CreateHostingDomain({
117682
+ domain: input.domain,
117683
+ certId: input.certId,
117684
+ });
117685
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117686
+ const taskStatus = await describeHostingDomainTask(cloudbase, cloudBaseOptions, server.logger);
117687
+ return (0, tool_result_js_1.buildJsonToolResult)(buildDomainMutationResult({
117688
+ action: 'bindDomain',
117689
+ domain: input.domain,
117690
+ certId: input.certId,
117691
+ result,
117692
+ taskStatus,
117693
+ }));
116974
117694
  }
116975
- result = await cloudbase.hosting.tcbModifyAttribute({
116976
- domain,
116977
- domainId,
116978
- domainConfig
116979
- });
116980
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116981
- break;
116982
- default:
116983
- throw new Error(`不支持的操作类型: ${action}`);
116984
- }
116985
- return {
116986
- content: [
116987
- {
116988
- type: "text",
116989
- text: JSON.stringify(result, null, 2)
117695
+ case 'unbindDomain': {
117696
+ if (!input.domain) {
117697
+ throw new Error('manageHosting(action="unbindDomain") 需要提供 domain。');
117698
+ }
117699
+ if (!input.confirm) {
117700
+ throw new Error('manageHosting(action="unbindDomain") 会解绑现有自定义域名,必须显式传 confirm=true。');
117701
+ }
117702
+ const result = await cloudbase.hosting.deleteHostingDomain({
117703
+ domain: input.domain,
117704
+ });
117705
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117706
+ const taskStatus = await describeHostingDomainTask(cloudbase, cloudBaseOptions, server.logger);
117707
+ return (0, tool_result_js_1.buildJsonToolResult)(buildDomainMutationResult({
117708
+ action: 'unbindDomain',
117709
+ domain: input.domain,
117710
+ result,
117711
+ taskStatus,
117712
+ }));
116990
117713
  }
116991
- ]
116992
- };
117714
+ case 'updateDomain': {
117715
+ if (!input.domain || input.domainId === undefined || !input.domainConfig) {
117716
+ throw new Error('manageHosting(action="updateDomain") 需要同时提供 domain、domainId 和 domainConfig。');
117717
+ }
117718
+ const result = await cloudbase.hosting.tcbModifyAttribute({
117719
+ domain: input.domain,
117720
+ domainId: input.domainId,
117721
+ domainConfig: input.domainConfig,
117722
+ });
117723
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117724
+ const taskStatus = await describeHostingDomainTask(cloudbase, cloudBaseOptions, server.logger);
117725
+ return (0, tool_result_js_1.buildJsonToolResult)(buildDomainMutationResult({
117726
+ action: 'updateDomain',
117727
+ domain: input.domain,
117728
+ domainId: input.domainId,
117729
+ domainConfig: input.domainConfig,
117730
+ result,
117731
+ taskStatus,
117732
+ }));
117733
+ }
117734
+ case 'downloadFile': {
117735
+ if (!input.cloudPath || !input.localPath) {
117736
+ throw new Error('manageHosting(action="downloadFile") 需要同时提供 cloudPath 和 localPath,localPath 应包含目标文件名。');
117737
+ }
117738
+ const result = await cloudbase.hosting.downloadFile({
117739
+ cloudPath: input.cloudPath,
117740
+ localPath: input.localPath,
117741
+ });
117742
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117743
+ return (0, tool_result_js_1.buildJsonToolResult)({
117744
+ success: true,
117745
+ data: {
117746
+ action: 'downloadFile',
117747
+ cloudPath: input.cloudPath,
117748
+ localPath: input.localPath,
117749
+ result,
117750
+ },
117751
+ message: `已将静态托管文件 \`${input.cloudPath}\` 下载到本地路径 \`${input.localPath}\`。`,
117752
+ });
117753
+ }
117754
+ case 'downloadDirectory': {
117755
+ if (!input.cloudPath || !input.localPath) {
117756
+ throw new Error('manageHosting(action="downloadDirectory") 需要同时提供 cloudPath 和 localPath,localPath 应为本地目录路径。');
117757
+ }
117758
+ const result = await cloudbase.hosting.downloadDirectory({
117759
+ cloudPath: input.cloudPath,
117760
+ localPath: input.localPath,
117761
+ });
117762
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
117763
+ return (0, tool_result_js_1.buildJsonToolResult)({
117764
+ success: true,
117765
+ data: {
117766
+ action: 'downloadDirectory',
117767
+ cloudPath: input.cloudPath,
117768
+ localPath: input.localPath,
117769
+ result,
117770
+ },
117771
+ message: `已将静态托管目录 \`${input.cloudPath}\` 下载到本地目录 \`${input.localPath}\`。`,
117772
+ });
117773
+ }
117774
+ }
117775
+ }
117776
+ catch (error) {
117777
+ const toolPayloadResult = (0, tool_result_js_1.toolPayloadErrorToResult)(error);
117778
+ if (toolPayloadResult) {
117779
+ return toolPayloadResult;
117780
+ }
117781
+ return buildFailureResult(args.action, error);
117782
+ }
116993
117783
  });
116994
117784
  }
116995
117785
 
@@ -123769,6 +124559,7 @@ function buildCapiErrorMessage(service, action, error) {
123769
124559
  const tcbEntry = service === "tcb" ? findTcbActionEntry(action) : undefined;
123770
124560
  const hasInvalidActionError = /invalid or not found|does not exist|not recognized/i.test(baseMessage);
123771
124561
  const hasParameterError = /parameter\s+`?.+?`?\s+is not recognized|MissingParameter|missing parameter|missing required/i.test(baseMessage);
124562
+ const hasInvalidParameterValueError = /invalid parameter value/i.test(baseMessage);
123772
124563
  if (hasInvalidActionError) {
123773
124564
  suggestions.push(`Action \`${action}\` 可能不存在或不对外开放。请不要继续猜测 Action 名称,先确认 service=\`${service}\` 下该 Action 在当前 API 版本是否真实存在。`);
123774
124565
  if (service === "tcb") {
@@ -123799,6 +124590,20 @@ function buildCapiErrorMessage(service, action, error) {
123799
124590
  }
123800
124591
  }
123801
124592
  }
124593
+ if (hasInvalidParameterValueError) {
124594
+ suggestions.push("请求参数值格式不正确或超出有效范围。请检查:");
124595
+ suggestions.push("1. 字符串参数是否为空或包含非法字符");
124596
+ suggestions.push("2. 数值参数是否在允许范围内");
124597
+ suggestions.push("3. 枚举值是否使用了正确的取值(区分大小写)");
124598
+ suggestions.push("4. 必填参数是否有值");
124599
+ if (service === "tcb" && tcbEntry) {
124600
+ const paramsTypeHint = formatTcbParamsTypeHint(tcbEntry.action);
124601
+ if (paramsTypeHint) {
124602
+ suggestions.push("参数类型参考:");
124603
+ suggestions.push(paramsTypeHint);
124604
+ }
124605
+ }
124606
+ }
123802
124607
  if (/ECONNRESET|socket hang up|ETIMEDOUT|ENOTFOUND/i.test(baseMessage)) {
123803
124608
  suggestions.push("网络请求异常,建议稍后重试,并检查本地网络/代理设置。");
123804
124609
  }
@@ -133684,7 +134489,7 @@ class TelemetryReporter {
133684
134489
  const nodeVersion = process.version; // Node.js版本
133685
134490
  const arch = os_1.default.arch(); // 系统架构
133686
134491
  // 从构建时注入的版本号获取MCP版本信息
133687
- const mcpVersion = process.env.npm_package_version || "2.18.0" || 0;
134492
+ const mcpVersion = process.env.npm_package_version || "2.19.5" || 0;
133688
134493
  return {
133689
134494
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
133690
134495
  deviceId: this.deviceId,
@@ -137772,7 +138577,7 @@ exports.isLogServiceEnabled = isLogServiceEnabled;
137772
138577
  const utils_1 = __webpack_require__(62358);
137773
138578
  __exportStar(__webpack_require__(83700), exports);
137774
138579
  /**
137775
- * 纯函数:根据 DescribeEnvs 返回的 LogServices 列表判断日志服务是否已开通。
138580
+ * 纯函数:根据 DescribeEnvInfo 返回的 LogServices 列表判断日志服务是否已开通。
137776
138581
  * 已开通条件:至少一个条目有有效的 TopicId。
137777
138582
  */
137778
138583
  function isLogServiceEnabled(logServices) {
@@ -137789,21 +138594,17 @@ class LogService {
137789
138594
  /**
137790
138595
  * 检查当前环境的日志服务是否已开通
137791
138596
  *
137792
- * 通过 DescribeEnvs 接口获取环境信息,判断 LogServices 字段中是否包含有效的
138597
+ * 通过 DescribeEnvInfo 接口获取环境信息,判断 LogServices 字段中是否包含有效的
137793
138598
  * 日志主题(TopicId 不为空)。
137794
138599
  *
137795
138600
  * @returns true 表示已开通,false 表示未开通或开通中
137796
138601
  */
137797
138602
  async checkLogServiceEnabled() {
137798
- var _a;
137799
- const res = await this.cloudService.request('DescribeEnvs', {
138603
+ var _a, _b;
138604
+ const res = await this.cloudService.request('DescribeEnvInfo', {
137800
138605
  EnvId: this.envId
137801
138606
  });
137802
- const envInfo = (_a = res === null || res === void 0 ? void 0 : res.EnvList) === null || _a === void 0 ? void 0 : _a[0];
137803
- if (!envInfo) {
137804
- return false;
137805
- }
137806
- const logServices = envInfo.LogServices;
138607
+ const logServices = (_b = (_a = res === null || res === void 0 ? void 0 : res.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo) === null || _b === void 0 ? void 0 : _b.LogServices;
137807
138608
  if (!logServices || logServices.length === 0) {
137808
138609
  return false;
137809
138610
  }
@@ -145617,6 +146418,576 @@ module.exports = function generate_contains(it, $keyword, $ruleType) {
145617
146418
  }
145618
146419
 
145619
146420
 
146421
+ /***/ }),
146422
+
146423
+ /***/ 48488:
146424
+ /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
146425
+
146426
+ "use strict";
146427
+
146428
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
146429
+ exports.SandboxService = void 0;
146430
+ const utils_1 = __webpack_require__(62358);
146431
+ class SandboxService {
146432
+ constructor(environment) {
146433
+ this.environment = environment;
146434
+ this.agsService = new utils_1.CloudService(environment.cloudBaseContext, 'ags', '2025-09-20');
146435
+ }
146436
+ /**
146437
+ * 更新沙箱工具
146438
+ * 更新指定沙箱工具的描述、网络配置、标签和自定义配置等信息。
146439
+ * @param {UpdateSandboxToolOptions} options 更新参数
146440
+ * @returns {Promise<UpdateSandboxToolResp>}
146441
+ */
146442
+ async updateSandboxTool(options) {
146443
+ const { toolId, description, networkConfiguration, tags, customConfiguration } = options;
146444
+ // ToolId 校验:必填
146445
+ if (typeof toolId !== 'string' || toolId.trim().length === 0) {
146446
+ throw new Error('toolId is required');
146447
+ }
146448
+ // Description 校验:最大 200 字符
146449
+ if (description !== undefined && description.length > 200) {
146450
+ throw new Error('description length must be <= 200');
146451
+ }
146452
+ // NetworkConfiguration 校验
146453
+ if (networkConfiguration !== undefined) {
146454
+ const validModes = ['PUBLIC', 'VPC', 'SANDBOX'];
146455
+ if (!networkConfiguration.NetworkMode || !validModes.includes(networkConfiguration.NetworkMode)) {
146456
+ throw new Error(`NetworkMode must be one of: ${validModes.join(', ')}`);
146457
+ }
146458
+ }
146459
+ // Tags 校验
146460
+ if (tags !== undefined) {
146461
+ if (!Array.isArray(tags)) {
146462
+ throw new Error('tags must be an array');
146463
+ }
146464
+ }
146465
+ // CustomConfiguration 校验
146466
+ if (customConfiguration !== undefined) {
146467
+ // Image 镜像地址格式基本校验
146468
+ if (customConfiguration.Image !== undefined) {
146469
+ if (typeof customConfiguration.Image !== 'string' || customConfiguration.Image.trim().length === 0) {
146470
+ throw new Error('Image must be a non-empty string');
146471
+ }
146472
+ }
146473
+ // ImageRegistryType 校验
146474
+ if (customConfiguration.ImageRegistryType !== undefined) {
146475
+ const validTypes = ['enterprise', 'personal'];
146476
+ if (!validTypes.includes(customConfiguration.ImageRegistryType)) {
146477
+ throw new Error(`ImageRegistryType must be one of: ${validTypes.join(', ')}`);
146478
+ }
146479
+ }
146480
+ // Ports 校验
146481
+ if (customConfiguration.Ports !== undefined) {
146482
+ if (!Array.isArray(customConfiguration.Ports)) {
146483
+ throw new Error('Ports must be an array');
146484
+ }
146485
+ }
146486
+ // Env 校验
146487
+ if (customConfiguration.Env !== undefined) {
146488
+ if (!Array.isArray(customConfiguration.Env)) {
146489
+ throw new Error('Env must be an array');
146490
+ }
146491
+ }
146492
+ // DNSConfig.Servers 校验:文档标注为必填
146493
+ if (customConfiguration.DNSConfig !== undefined) {
146494
+ if (!Array.isArray(customConfiguration.DNSConfig.Servers) || customConfiguration.DNSConfig.Servers.length === 0) {
146495
+ throw new Error('DNSConfig.Servers is required and must be a non-empty array');
146496
+ }
146497
+ }
146498
+ }
146499
+ // 构造请求体
146500
+ const reqData = {
146501
+ ToolId: toolId
146502
+ };
146503
+ if (description !== undefined) {
146504
+ reqData.Description = description;
146505
+ }
146506
+ if (networkConfiguration !== undefined) {
146507
+ reqData.NetworkConfiguration = networkConfiguration;
146508
+ }
146509
+ if (tags !== undefined) {
146510
+ reqData.Tags = tags;
146511
+ }
146512
+ if (customConfiguration !== undefined) {
146513
+ reqData.CustomConfiguration = customConfiguration;
146514
+ }
146515
+ return this.agsService.request('UpdateSandboxTool', reqData);
146516
+ }
146517
+ /**
146518
+ * 查询沙箱工具列表
146519
+ * @param {DescribeSandboxToolListOptions} options 查询参数
146520
+ * @returns {Promise<DescribeSandboxToolListResp>}
146521
+ */
146522
+ async describeSandboxToolList(options = {}) {
146523
+ const { toolIds, offset, limit, filters } = options;
146524
+ // ToolIds 校验
146525
+ if (toolIds !== undefined) {
146526
+ if (!Array.isArray(toolIds)) {
146527
+ throw new Error('toolIds must be an array');
146528
+ }
146529
+ if (toolIds.length > 100) {
146530
+ throw new Error('toolIds length must be <= 100');
146531
+ }
146532
+ const invalidId = toolIds.find(id => typeof id !== 'string' || id.trim().length === 0);
146533
+ if (invalidId !== undefined) {
146534
+ throw new Error('toolIds contains invalid item');
146535
+ }
146536
+ }
146537
+ // Offset 校验
146538
+ if (offset !== undefined && (!Number.isInteger(offset) || offset < 0)) {
146539
+ throw new Error('offset must be a non-negative integer');
146540
+ }
146541
+ // Limit 校验
146542
+ if (limit !== undefined && (!Number.isInteger(limit) || limit < 1 || limit > 100)) {
146543
+ throw new Error('limit must be an integer between 1 and 100');
146544
+ }
146545
+ // Filters 校验
146546
+ if (filters !== undefined) {
146547
+ if (!Array.isArray(filters)) {
146548
+ throw new Error('filters must be an array');
146549
+ }
146550
+ }
146551
+ // 构造请求体
146552
+ const reqData = {};
146553
+ if (toolIds !== undefined && toolIds.length > 0) {
146554
+ reqData.ToolIds = toolIds;
146555
+ }
146556
+ if (offset !== undefined) {
146557
+ reqData.Offset = offset;
146558
+ }
146559
+ if (limit !== undefined) {
146560
+ reqData.Limit = limit;
146561
+ }
146562
+ if (filters !== undefined && filters.length > 0) {
146563
+ reqData.Filters = filters;
146564
+ }
146565
+ return this.agsService.request('DescribeSandboxToolList', reqData);
146566
+ }
146567
+ /**
146568
+ * 创建沙箱工具
146569
+ * @param {CreateSandboxToolOptions} options 创建参数
146570
+ * @returns {Promise<CreateSandboxToolResp>}
146571
+ */
146572
+ async createSandboxTool(options) {
146573
+ const { toolName, toolType, networkConfiguration, description, defaultTimeout, tags, clientToken, roleArn, storageMounts, customConfiguration, logConfiguration } = options;
146574
+ // ToolName 校验:必填,1-50 字符,英文/数字/下划线/连接线
146575
+ if (typeof toolName !== 'string' || toolName.trim().length === 0) {
146576
+ throw new Error('toolName is required');
146577
+ }
146578
+ if (toolName.length > 50) {
146579
+ throw new Error('toolName length must be <= 50');
146580
+ }
146581
+ if (!/^[a-zA-Z0-9_-]+$/.test(toolName)) {
146582
+ throw new Error('toolName can only contain letters, digits, underscores and hyphens');
146583
+ }
146584
+ // ToolType 校验:必填,枚举
146585
+ const validToolTypes = ['browser', 'code-interpreter', 'custom'];
146586
+ if (!toolType || !validToolTypes.includes(toolType)) {
146587
+ throw new Error(`toolType must be one of: ${validToolTypes.join(', ')}`);
146588
+ }
146589
+ // NetworkConfiguration 校验:必填
146590
+ if (!networkConfiguration) {
146591
+ throw new Error('networkConfiguration is required');
146592
+ }
146593
+ const validModes = ['PUBLIC', 'VPC', 'SANDBOX'];
146594
+ if (!networkConfiguration.NetworkMode || !validModes.includes(networkConfiguration.NetworkMode)) {
146595
+ throw new Error(`NetworkMode must be one of: ${validModes.join(', ')}`);
146596
+ }
146597
+ // Description 校验
146598
+ if (description !== undefined && description.length > 200) {
146599
+ throw new Error('description length must be <= 200');
146600
+ }
146601
+ // DefaultTimeout 校验:格式如 5m、300s、1h
146602
+ if (defaultTimeout !== undefined) {
146603
+ if (typeof defaultTimeout !== 'string' || !/^\d+[smh]$/.test(defaultTimeout)) {
146604
+ throw new Error('defaultTimeout must match format like 5m, 300s, 1h');
146605
+ }
146606
+ }
146607
+ // ClientToken 校验:最大 64 字符
146608
+ if (clientToken !== undefined && clientToken.length > 64) {
146609
+ throw new Error('clientToken length must be <= 64');
146610
+ }
146611
+ // Tags 校验
146612
+ if (tags !== undefined) {
146613
+ if (!Array.isArray(tags)) {
146614
+ throw new Error('tags must be an array');
146615
+ }
146616
+ }
146617
+ // StorageMounts 校验
146618
+ if (storageMounts !== undefined) {
146619
+ if (!Array.isArray(storageMounts)) {
146620
+ throw new Error('storageMounts must be an array');
146621
+ }
146622
+ }
146623
+ // CustomConfiguration 校验
146624
+ if (customConfiguration !== undefined) {
146625
+ if (customConfiguration.Image !== undefined) {
146626
+ if (typeof customConfiguration.Image !== 'string' || customConfiguration.Image.trim().length === 0) {
146627
+ throw new Error('Image must be a non-empty string');
146628
+ }
146629
+ }
146630
+ if (customConfiguration.ImageRegistryType !== undefined) {
146631
+ const validTypes = ['enterprise', 'personal'];
146632
+ if (!validTypes.includes(customConfiguration.ImageRegistryType)) {
146633
+ throw new Error(`ImageRegistryType must be one of: ${validTypes.join(', ')}`);
146634
+ }
146635
+ }
146636
+ if (customConfiguration.DNSConfig !== undefined) {
146637
+ if (!Array.isArray(customConfiguration.DNSConfig.Servers) || customConfiguration.DNSConfig.Servers.length === 0) {
146638
+ throw new Error('DNSConfig.Servers is required and must be a non-empty array');
146639
+ }
146640
+ }
146641
+ }
146642
+ // 构造请求体
146643
+ const reqData = {
146644
+ ToolName: toolName,
146645
+ ToolType: toolType,
146646
+ NetworkConfiguration: networkConfiguration
146647
+ };
146648
+ if (description !== undefined) {
146649
+ reqData.Description = description;
146650
+ }
146651
+ if (defaultTimeout !== undefined) {
146652
+ reqData.DefaultTimeout = defaultTimeout;
146653
+ }
146654
+ if (tags !== undefined) {
146655
+ reqData.Tags = tags;
146656
+ }
146657
+ if (clientToken !== undefined) {
146658
+ reqData.ClientToken = clientToken;
146659
+ }
146660
+ if (roleArn !== undefined) {
146661
+ reqData.RoleArn = roleArn;
146662
+ }
146663
+ if (storageMounts !== undefined) {
146664
+ reqData.StorageMounts = storageMounts;
146665
+ }
146666
+ if (customConfiguration !== undefined) {
146667
+ reqData.CustomConfiguration = customConfiguration;
146668
+ }
146669
+ if (logConfiguration !== undefined) {
146670
+ reqData.LogConfiguration = logConfiguration;
146671
+ }
146672
+ return this.agsService.request('CreateSandboxTool', reqData);
146673
+ }
146674
+ /**
146675
+ * 删除沙箱工具
146676
+ * @param {DeleteSandboxToolOptions} options 删除参数
146677
+ * @returns {Promise<DeleteSandboxToolResp>}
146678
+ */
146679
+ async deleteSandboxTool(options) {
146680
+ const { toolId } = options;
146681
+ // ToolId 校验:必填
146682
+ if (typeof toolId !== 'string' || toolId.trim().length === 0) {
146683
+ throw new Error('toolId is required');
146684
+ }
146685
+ const reqData = {
146686
+ ToolId: toolId
146687
+ };
146688
+ return this.agsService.request('DeleteSandboxTool', reqData);
146689
+ }
146690
+ /**
146691
+ * 恢复沙箱实例
146692
+ * @param {ResumeSandboxInstanceOptions} options 恢复参数
146693
+ * @returns {Promise<ResumeSandboxInstanceResp>}
146694
+ */
146695
+ async resumeSandboxInstance(options) {
146696
+ const { instanceId } = options;
146697
+ // InstanceId 校验:必填
146698
+ if (typeof instanceId !== 'string' || instanceId.trim().length === 0) {
146699
+ throw new Error('instanceId is required');
146700
+ }
146701
+ const reqData = {
146702
+ InstanceId: instanceId
146703
+ };
146704
+ return this.agsService.request('ResumeSandboxInstance', reqData);
146705
+ }
146706
+ /**
146707
+ * 暂停沙箱实例
146708
+ * @param {PauseSandboxInstanceOptions} options 暂停参数
146709
+ * @returns {Promise<PauseSandboxInstanceResp>}
146710
+ */
146711
+ async pauseSandboxInstance(options) {
146712
+ const { instanceId } = options;
146713
+ // InstanceId 校验:必填
146714
+ if (typeof instanceId !== 'string' || instanceId.trim().length === 0) {
146715
+ throw new Error('instanceId is required');
146716
+ }
146717
+ const reqData = {
146718
+ InstanceId: instanceId
146719
+ };
146720
+ return this.agsService.request('PauseSandboxInstance', reqData);
146721
+ }
146722
+ /**
146723
+ * 更新沙箱实例
146724
+ * 更新指定沙箱实例的超时时间和元数据等信息。
146725
+ * @param {UpdateSandboxInstanceOptions} options 更新参数
146726
+ * @returns {Promise<UpdateSandboxInstanceResp>}
146727
+ */
146728
+ async updateSandboxInstance(options) {
146729
+ const { instanceId, timeout, metadata } = options;
146730
+ // InstanceId 校验:必填
146731
+ if (typeof instanceId !== 'string' || instanceId.trim().length === 0) {
146732
+ throw new Error('instanceId is required');
146733
+ }
146734
+ // Timeout 校验:格式如 30s、5m、1h,最小 30s,最大 24h
146735
+ if (timeout !== undefined) {
146736
+ if (typeof timeout !== 'string' || !/^\d+[smh]$/.test(timeout)) {
146737
+ throw new Error('timeout must match format like 30s, 5m, 1h');
146738
+ }
146739
+ }
146740
+ // Metadata 校验
146741
+ if (metadata !== undefined) {
146742
+ if (!Array.isArray(metadata)) {
146743
+ throw new Error('metadata must be an array');
146744
+ }
146745
+ }
146746
+ // 构造请求体
146747
+ const reqData = {
146748
+ InstanceId: instanceId
146749
+ };
146750
+ if (timeout !== undefined) {
146751
+ reqData.Timeout = timeout;
146752
+ }
146753
+ if (metadata !== undefined) {
146754
+ reqData.Metadata = metadata;
146755
+ }
146756
+ return this.agsService.request('UpdateSandboxInstance', reqData);
146757
+ }
146758
+ /**
146759
+ * 停止沙箱实例
146760
+ * @param {StopSandboxInstanceOptions} options 停止参数
146761
+ * @returns {Promise<StopSandboxInstanceResp>}
146762
+ */
146763
+ async stopSandboxInstance(options) {
146764
+ const { instanceId } = options;
146765
+ // InstanceId 校验:必填
146766
+ if (typeof instanceId !== 'string' || instanceId.trim().length === 0) {
146767
+ throw new Error('instanceId is required');
146768
+ }
146769
+ const reqData = {
146770
+ InstanceId: instanceId
146771
+ };
146772
+ return this.agsService.request('StopSandboxInstance', reqData);
146773
+ }
146774
+ /**
146775
+ * 启动沙箱实例
146776
+ * @param {StartSandboxInstanceOptions} options 启动参数
146777
+ * @returns {Promise<StartSandboxInstanceResp>}
146778
+ */
146779
+ async startSandboxInstance(options) {
146780
+ const { toolId, toolName, timeout, clientToken, mountOptions, customConfiguration, authMode, metadata } = options;
146781
+ // ToolId 与 ToolName 至少有一个要填
146782
+ const hasToolId = toolId !== undefined && toolId.trim().length > 0;
146783
+ const hasToolName = toolName !== undefined && toolName.trim().length > 0;
146784
+ if (!hasToolId && !hasToolName) {
146785
+ throw new Error('toolId or toolName is required, at least one must be provided');
146786
+ }
146787
+ // Timeout 校验:格式如 30s、5m、1h
146788
+ if (timeout !== undefined) {
146789
+ if (typeof timeout !== 'string' || !/^\d+[smh]$/.test(timeout)) {
146790
+ throw new Error('timeout must match format like 30s, 5m, 1h');
146791
+ }
146792
+ }
146793
+ // ClientToken 校验:最大 64 字符
146794
+ if (clientToken !== undefined && clientToken.length > 64) {
146795
+ throw new Error('clientToken length must be <= 64');
146796
+ }
146797
+ // MountOptions 校验
146798
+ if (mountOptions !== undefined) {
146799
+ if (!Array.isArray(mountOptions)) {
146800
+ throw new Error('mountOptions must be an array');
146801
+ }
146802
+ }
146803
+ // AuthMode 校验
146804
+ if (authMode !== undefined) {
146805
+ const validAuthModes = ['DEFAULT', 'TOKEN', 'NONE'];
146806
+ if (!validAuthModes.includes(authMode)) {
146807
+ throw new Error(`authMode must be one of: ${validAuthModes.join(', ')}`);
146808
+ }
146809
+ }
146810
+ // CustomConfiguration 校验
146811
+ if (customConfiguration !== undefined) {
146812
+ if (customConfiguration.Image !== undefined) {
146813
+ if (typeof customConfiguration.Image !== 'string' || customConfiguration.Image.trim().length === 0) {
146814
+ throw new Error('Image must be a non-empty string');
146815
+ }
146816
+ }
146817
+ if (customConfiguration.ImageRegistryType !== undefined) {
146818
+ const validTypes = ['enterprise', 'personal'];
146819
+ if (!validTypes.includes(customConfiguration.ImageRegistryType)) {
146820
+ throw new Error(`ImageRegistryType must be one of: ${validTypes.join(', ')}`);
146821
+ }
146822
+ }
146823
+ if (customConfiguration.DNSConfig !== undefined) {
146824
+ if (!Array.isArray(customConfiguration.DNSConfig.Servers) || customConfiguration.DNSConfig.Servers.length === 0) {
146825
+ throw new Error('DNSConfig.Servers is required and must be a non-empty array');
146826
+ }
146827
+ }
146828
+ }
146829
+ // Metadata 校验
146830
+ if (metadata !== undefined) {
146831
+ if (!Array.isArray(metadata)) {
146832
+ throw new Error('metadata must be an array');
146833
+ }
146834
+ }
146835
+ // 构造请求体
146836
+ const reqData = {};
146837
+ if (toolId !== undefined) {
146838
+ reqData.ToolId = toolId;
146839
+ }
146840
+ if (toolName !== undefined) {
146841
+ reqData.ToolName = toolName;
146842
+ }
146843
+ if (timeout !== undefined) {
146844
+ reqData.Timeout = timeout;
146845
+ }
146846
+ if (clientToken !== undefined) {
146847
+ reqData.ClientToken = clientToken;
146848
+ }
146849
+ if (mountOptions !== undefined) {
146850
+ reqData.MountOptions = mountOptions;
146851
+ }
146852
+ if (customConfiguration !== undefined) {
146853
+ reqData.CustomConfiguration = customConfiguration;
146854
+ }
146855
+ if (authMode !== undefined) {
146856
+ reqData.AuthMode = authMode;
146857
+ }
146858
+ if (metadata !== undefined) {
146859
+ reqData.Metadata = metadata;
146860
+ }
146861
+ return this.agsService.request('StartSandboxInstance', reqData);
146862
+ }
146863
+ /**
146864
+ * 查询沙箱实例列表
146865
+ * @param {DescribeSandboxInstanceListOptions} options 查询参数
146866
+ * @returns {Promise<DescribeSandboxInstanceListResp>}
146867
+ */
146868
+ async describeSandboxInstanceList(options = {}) {
146869
+ const { instanceIds, toolId, offset, limit, filters } = options;
146870
+ // InstanceIds 校验
146871
+ if (instanceIds !== undefined) {
146872
+ if (!Array.isArray(instanceIds)) {
146873
+ throw new Error('instanceIds must be an array');
146874
+ }
146875
+ if (instanceIds.length > 100) {
146876
+ throw new Error('instanceIds length must be <= 100');
146877
+ }
146878
+ const invalidId = instanceIds.find(id => typeof id !== 'string' || id.trim().length === 0);
146879
+ if (invalidId !== undefined) {
146880
+ throw new Error('instanceIds contains invalid item');
146881
+ }
146882
+ }
146883
+ // ToolId 校验
146884
+ if (toolId !== undefined) {
146885
+ if (typeof toolId !== 'string' || toolId.trim().length === 0) {
146886
+ throw new Error('toolId must be a non-empty string');
146887
+ }
146888
+ }
146889
+ // Offset 校验
146890
+ if (offset !== undefined && (!Number.isInteger(offset) || offset < 0)) {
146891
+ throw new Error('offset must be a non-negative integer');
146892
+ }
146893
+ // Limit 校验
146894
+ if (limit !== undefined && (!Number.isInteger(limit) || limit < 1 || limit > 100)) {
146895
+ throw new Error('limit must be an integer between 1 and 100');
146896
+ }
146897
+ // Filters 校验
146898
+ if (filters !== undefined) {
146899
+ if (!Array.isArray(filters)) {
146900
+ throw new Error('filters must be an array');
146901
+ }
146902
+ }
146903
+ // 构造请求体
146904
+ const reqData = {};
146905
+ if (instanceIds !== undefined && instanceIds.length > 0) {
146906
+ reqData.InstanceIds = instanceIds;
146907
+ }
146908
+ if (toolId !== undefined) {
146909
+ reqData.ToolId = toolId;
146910
+ }
146911
+ if (offset !== undefined) {
146912
+ reqData.Offset = offset;
146913
+ }
146914
+ if (limit !== undefined) {
146915
+ reqData.Limit = limit;
146916
+ }
146917
+ if (filters !== undefined && filters.length > 0) {
146918
+ reqData.Filters = filters;
146919
+ }
146920
+ return this.agsService.request('DescribeSandboxInstanceList', reqData);
146921
+ }
146922
+ /**
146923
+ * 获取沙箱实例访问 Token
146924
+ * 获取访问沙箱工具时所需要使用的访问 Token,创建沙箱实例后需调用此接口获取沙箱实例访问 Token。
146925
+ * 此 Token 可用于调用代码沙箱实例执行代码,或浏览器沙箱实例进行浏览器操作等。
146926
+ * @param {AcquireSandboxInstanceTokenOptions} options 请求参数
146927
+ * @returns {Promise<AcquireSandboxInstanceTokenResp>}
146928
+ */
146929
+ async acquireSandboxInstanceToken(options) {
146930
+ const { instanceId } = options;
146931
+ // InstanceId 校验:必填
146932
+ if (typeof instanceId !== 'string' || instanceId.trim().length === 0) {
146933
+ throw new Error('instanceId is required');
146934
+ }
146935
+ const reqData = {
146936
+ InstanceId: instanceId
146937
+ };
146938
+ return this.agsService.request('AcquireSandboxInstanceToken', reqData);
146939
+ }
146940
+ /**
146941
+ * 获取 API 密钥列表
146942
+ * 获取 API 密钥简略信息列表,包含名称、创建时间等。
146943
+ * @returns {Promise<DescribeAPIKeyListResp>}
146944
+ */
146945
+ async describeAPIKeyList() {
146946
+ return this.agsService.request('DescribeAPIKeyList', {});
146947
+ }
146948
+ /**
146949
+ * 创建 API 密钥
146950
+ * 创建新的 API 密钥,用于调用 Agent Sandbox 接口。
146951
+ * 注意:生成的 APIKey 仅在此次响应中返回,后续无法再次获取,请妥善保管。
146952
+ * @param {CreateAPIKeyOptions} options 创建参数
146953
+ * @returns {Promise<CreateAPIKeyResp>}
146954
+ */
146955
+ async createAPIKey(options = {}) {
146956
+ const { name } = options;
146957
+ // Name 校验
146958
+ if (name !== undefined) {
146959
+ if (typeof name !== 'string' || name.trim().length === 0) {
146960
+ throw new Error('name must be a non-empty string');
146961
+ }
146962
+ }
146963
+ // 构造请求体
146964
+ const reqData = {};
146965
+ if (name !== undefined) {
146966
+ reqData.Name = name;
146967
+ }
146968
+ return this.agsService.request('CreateAPIKey', reqData);
146969
+ }
146970
+ /**
146971
+ * 删除 API 密钥
146972
+ * 注意:本接口删除的是 Agent Sandbox 专用 API Key,区别于腾讯云 Secret ID / Secret Key。
146973
+ * @param {DeleteAPIKeyOptions} options 删除参数
146974
+ * @returns {Promise<DeleteAPIKeyResp>}
146975
+ */
146976
+ async deleteAPIKey(options) {
146977
+ const { keyId } = options;
146978
+ // KeyId 校验:必填
146979
+ if (typeof keyId !== 'string' || keyId.trim().length === 0) {
146980
+ throw new Error('keyId is required');
146981
+ }
146982
+ const reqData = {
146983
+ KeyId: keyId
146984
+ };
146985
+ return this.agsService.request('DeleteAPIKey', reqData);
146986
+ }
146987
+ }
146988
+ exports.SandboxService = SandboxService;
146989
+
146990
+
145620
146991
  /***/ }),
145621
146992
 
145622
146993
  /***/ 48525:
@@ -167484,6 +168855,8 @@ const cloudBaseRun_1 = __webpack_require__(24344);
167484
168855
  const mysql_1 = __webpack_require__(61697);
167485
168856
  const cloudApp_1 = __webpack_require__(32665);
167486
168857
  const permission_1 = __webpack_require__(24786);
168858
+ const sandbox_1 = __webpack_require__(48488);
168859
+ const aiModel_1 = __webpack_require__(34800);
167487
168860
  class Environment {
167488
168861
  constructor(context, envId) {
167489
168862
  this.inited = false;
@@ -167507,6 +168880,8 @@ class Environment {
167507
168880
  this.mysqlService = new mysql_1.MysqlService(this);
167508
168881
  this.permissionService = new permission_1.PermissionService(this);
167509
168882
  this.cloudAppService = new cloudApp_1.CloudAppService(this);
168883
+ this.sandboxService = new sandbox_1.SandboxService(this);
168884
+ this.aiModelService = new aiModel_1.AiModelService(this);
167510
168885
  }
167511
168886
  async lazyInit() {
167512
168887
  if (!this.inited) {
@@ -167575,6 +168950,12 @@ class Environment {
167575
168950
  getPermissionService() {
167576
168951
  return this.permissionService;
167577
168952
  }
168953
+ getSandboxService() {
168954
+ return this.sandboxService;
168955
+ }
168956
+ getAiModelService() {
168957
+ return this.aiModelService;
168958
+ }
167578
168959
  getCommonService(serviceType = 'tcb', serviceVersion) {
167579
168960
  return new common_1.CommonService(this, serviceType, serviceVersion);
167580
168961
  }
@@ -178128,11 +179509,24 @@ function requireStringParam(value, fieldName, action) {
178128
179509
  }
178129
179510
  return value.trim();
178130
179511
  }
179512
+ function buildOptionalStringEnum(values, description) {
179513
+ const uniqueValues = [...new Set(values.filter((value) => value.trim()))];
179514
+ if (uniqueValues.length > 0) {
179515
+ return zod_1.z
179516
+ .enum(uniqueValues)
179517
+ .optional()
179518
+ .describe(description);
179519
+ }
179520
+ return zod_1.z
179521
+ .string()
179522
+ .optional()
179523
+ .describe(`${description} 当前暂时无法枚举可选值;如需查看当前可用项,可直接传入字符串,工具会在执行时返回可用列表。`);
179524
+ }
178131
179525
  // OpenAPI 文档 URL 列表
178132
179526
  const OPENAPI_SOURCES = [
178133
179527
  {
178134
179528
  name: "mysqldb",
178135
- description: "MySQL RESTful API - 云开发 MySQL 数据库 HTTP API",
179529
+ description: "关系型数据库 RESTful API (MySQL/PostgreSQL) - 云开发关系型数据库 HTTP API",
178136
179530
  url: "https://docs.cloudbase.net/openapi/mysqldb.v1.openapi.yaml",
178137
179531
  },
178138
179532
  {
@@ -178155,6 +179549,16 @@ const OPENAPI_SOURCES = [
178155
179549
  description: "Storage API - 云存储 HTTP API",
178156
179550
  url: "https://docs.cloudbase.net/openapi/storage.v1.openapi.yaml",
178157
179551
  },
179552
+ {
179553
+ name: "nosql",
179554
+ description: "NoSQL RESTful API - 文档型数据库 HTTP API",
179555
+ url: "https://docs.cloudbase.net/openapi/nosql.v1.openapi.yaml",
179556
+ },
179557
+ {
179558
+ name: "ai_model",
179559
+ description: "AI 大模型接入 API - 统一 AI 模型 HTTP API",
179560
+ url: "https://docs.cloudbase.net/openapi/ai_model.v1.openapi.yaml",
179561
+ },
178158
179562
  ];
178159
179563
  async function downloadWebTemplate() {
178160
179564
  const zipPath = path.join(CACHE_BASE_DIR, "web-cloudbase-project.zip");
@@ -178430,6 +179834,8 @@ async function registerRagTools(server) {
178430
179834
  error,
178431
179835
  });
178432
179836
  }
179837
+ const skillNames = skills.map((skill) => path.basename(path.dirname(skill.absolutePath)));
179838
+ const openapiNames = openapis.map((api) => api.name);
178433
179839
  const getDocsManager = () => (0, cloudbase_manager_js_1.createCloudBaseManagerWithOptions)(server.cloudBaseOptions ?? {})
178434
179840
  ?.docs;
178435
179841
  server.registerTool?.("searchKnowledgeBase", {
@@ -178438,6 +179844,11 @@ async function registerRagTools(server) {
178438
179844
 
178439
179845
  强烈推荐始终优先使用固定技能文档 (skill)、OpenAPI 文档 (openapi) 或 CloudBase 官方文档 (docs) 模式进行检索,仅当固定文档无法覆盖你的问题时,再使用向量查询 (vector) 模式。
178440
179846
 
179847
+ ⚠️ 重要:当 CloudBase skills 处于禁用状态或当前 IDE 不支持 skill 文件读取时,必须使用 searchKnowledgeBase(mode=skill, skillName=...) 来获取 CloudBase 技能文档内容,而不是尝试直接读取 skill 文件。直接读取可能返回 400 错误。示例:
179848
+ - 需要 auth-tool 指南时:searchKnowledgeBase(mode=skill, skillName=auth-tool)
179849
+ - 需要 auth-web 指南时:searchKnowledgeBase(mode=skill, skillName=auth-web)
179850
+ - 需要 cloudbase-agent 指南时:searchKnowledgeBase(mode=skill, skillName=cloudbase-agent)
179851
+
178441
179852
  固定技能文档 (skill) 查询当前支持 ${skills.length} 个固定文档,分别是:
178442
179853
  ${skills
178443
179854
  .map((skill) => `文档名:${path.basename(path.dirname(skill.absolutePath))} 文档介绍:${skill.description}`)
@@ -178449,14 +179860,8 @@ async function registerRagTools(server) {
178449
179860
  .join("\n")}`,
178450
179861
  inputSchema: {
178451
179862
  mode: SearchKnowledgeModeEnum,
178452
- skillName: zod_1.z
178453
- .enum(skills.map((skill) => path.basename(path.dirname(skill.absolutePath))))
178454
- .optional()
178455
- .describe("mode=skill 时指定。技能名称。"),
178456
- apiName: zod_1.z
178457
- .enum(openapis.map((api) => api.name))
178458
- .optional()
178459
- .describe("mode=openapi 时指定。API 名称。"),
179863
+ skillName: buildOptionalStringEnum(skillNames, "mode=skill 时指定。技能名称。"),
179864
+ apiName: buildOptionalStringEnum(openapiNames, "mode=openapi 时指定。API 名称。"),
178460
179865
  action: CloudBaseDocsActionEnum.optional().describe("mode=docs 时指定。CloudBase 文档操作类型:listModules=列出所有文档模块,listModuleDocs=获取指定模块的目录结构,findByName=按名称/路径/URL 智能查找,readDoc=读取指定文档 Markdown,searchDocs=全文搜索官方文档。"),
178461
179866
  moduleName: zod_1.z
178462
179867
  .string()
@@ -178549,7 +179954,7 @@ async function registerRagTools(server) {
178549
179954
  content: [
178550
179955
  {
178551
179956
  type: "text",
178552
- text: `Skill document "${skillName}" not found. Available skill docs: ${skills.map((item) => path.basename(path.dirname(item.absolutePath))).join(", ") || "none"}`,
179957
+ text: `Skill document "${skillName}" not found. Available skill docs: ${skillNames.join(", ") || "none"}`,
178553
179958
  },
178554
179959
  ],
178555
179960
  };
@@ -178570,7 +179975,7 @@ async function registerRagTools(server) {
178570
179975
  content: [
178571
179976
  {
178572
179977
  type: "text",
178573
- text: `OpenAPI document "${apiName}" not found. Available APIs: ${openapis.map((a) => a.name).join(", ") || "none"}`,
179978
+ text: `OpenAPI document "${apiName}" not found. Available APIs: ${openapiNames.join(", ") || "none"}`,
178574
179979
  },
178575
179980
  ],
178576
179981
  };
@@ -191551,7 +192956,7 @@ ${envIdSection}
191551
192956
  ## 环境信息
191552
192957
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
191553
192958
  - Node.js版本: ${process.version}
191554
- - MCP 版本:${process.env.npm_package_version || "2.18.0" || 0}
192959
+ - MCP 版本:${process.env.npm_package_version || "2.19.5" || 0}
191555
192960
  - 系统架构: ${os_1.default.arch()}
191556
192961
  - 时间: ${new Date().toISOString()}
191557
192962
  - 请求ID: ${requestId}
@@ -191631,7 +193036,8 @@ function createWrappedHandler(name, handler, server) {
191631
193036
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
191632
193037
  ide: server.ide || process.env.INTEGRATION_IDE || ''
191633
193038
  });
191634
- const enhancedErrorMessage = `${errorMessage}\n\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
193039
+ const mcpVersion = true ? "2.19.5" : 0;
193040
+ const enhancedErrorMessage = `${errorMessage}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
191635
193041
  // 创建新的错误对象,保持原有的错误类型但更新消息
191636
193042
  const enhancedError = error instanceof Error
191637
193043
  ? new Error(enhancedErrorMessage)
@@ -199662,13 +201068,8 @@ async function resolveNoSqlInstanceId(options) {
199662
201068
  return resolved;
199663
201069
  }
199664
201070
  async function callNoSqlContentApi(options) {
199665
- const resolvedInstance = await resolveNoSqlInstanceId({
199666
- toolName: options.toolName,
199667
- cloudbase: options.cloudbase,
199668
- cloudBaseOptions: options.cloudBaseOptions,
199669
- instanceIdOverride: options.instanceIdOverride,
199670
- collectionName: options.collectionName,
199671
- });
201071
+ // 直接使用 EnvId 替代 Tag,无需通过 DescribeEnvs 获取 instanceId
201072
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(options.cloudBaseOptions);
199672
201073
  const startedAt = Date.now();
199673
201074
  try {
199674
201075
  const result = await options.cloudbase
@@ -199678,14 +201079,14 @@ async function callNoSqlContentApi(options) {
199678
201079
  Param: {
199679
201080
  ...options.param,
199680
201081
  TableName: options.collectionName,
199681
- Tag: resolvedInstance.instanceId,
201082
+ EnvId: envId,
199682
201083
  },
199683
201084
  });
199684
201085
  logNoSqlLatency(options.toolName, "cloudApiCall", {
199685
201086
  collectionName: options.collectionName,
199686
201087
  action: options.action,
199687
201088
  durationMs: Date.now() - startedAt,
199688
- instanceIdSource: resolvedInstance.source,
201089
+ instanceIdSource: "envId",
199689
201090
  requestId: result?.RequestId,
199690
201091
  });
199691
201092
  return result;
@@ -199695,20 +201096,9 @@ async function callNoSqlContentApi(options) {
199695
201096
  collectionName: options.collectionName,
199696
201097
  action: options.action,
199697
201098
  durationMs: Date.now() - startedAt,
199698
- instanceIdSource: resolvedInstance.source,
201099
+ instanceIdSource: "envId",
199699
201100
  message: error instanceof Error ? error.message : String(error),
199700
201101
  });
199701
- if (!options.instanceIdOverride &&
199702
- resolvedInstance.cacheKey &&
199703
- isInvalidInstanceIdError(error)) {
199704
- (0, cloudbase_manager_js_1.invalidateDatabaseInstanceIdCache)({
199705
- cacheKey: resolvedInstance.cacheKey,
199706
- });
199707
- logNoSqlLatency(options.toolName, "invalidateInstanceIdCache", {
199708
- collectionName: options.collectionName,
199709
- cacheKey: resolvedInstance.cacheKey,
199710
- });
199711
- }
199712
201102
  throw error;
199713
201103
  }
199714
201104
  }
@@ -199726,18 +201116,25 @@ async function waitForCollectionReady({ cloudbase, collectionName, logger, timeo
199726
201116
  });
199727
201117
  while (Date.now() <= deadline) {
199728
201118
  try {
199729
- const result = await cloudbase.database.checkCollectionExists(collectionName);
201119
+ // 直接用 commonService 调用 DescribeTable 检查集合是否存在,避免 SDK lazyInit → DescribeEnvs
201120
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)();
201121
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
201122
+ Action: "DescribeTable",
201123
+ Param: {
201124
+ EnvId: envId,
201125
+ TableName: collectionName,
201126
+ },
201127
+ });
199730
201128
  (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, result);
199731
- if (result.Exists) {
199732
- logger?.({
199733
- type: "toolInfo",
199734
- toolName: "writeNoSqlDatabaseStructure",
199735
- message: "NoSQL collection is ready for subsequent operations",
199736
- collectionName,
199737
- waitedMs: Date.now() - startedAt,
199738
- });
199739
- return;
199740
- }
201129
+ // DescribeTable 成功返回即表示集合存在
201130
+ logger?.({
201131
+ type: "toolInfo",
201132
+ toolName: "writeNoSqlDatabaseStructure",
201133
+ message: "NoSQL collection is ready for subsequent operations",
201134
+ collectionName,
201135
+ waitedMs: Date.now() - startedAt,
201136
+ });
201137
+ return;
199741
201138
  }
199742
201139
  catch (error) {
199743
201140
  lastError = error;
@@ -199769,7 +201166,7 @@ function registerDatabaseTools(server) {
199769
201166
  // readNoSqlDatabaseStructure
199770
201167
  server.registerTool?.("readNoSqlDatabaseStructure", {
199771
201168
  title: "读取 NoSQL 数据库结构",
199772
- description: "读取 NoSQL 数据库集合与索引结构,支持列出集合、查看集合详情、列出索引以及检查索引是否存在。",
201169
+ description: "读取 NoSQL 数据库集合与索引结构,支持列出集合、查看集合详情、列出索引以及检查索引是否存在。本工具为服务端管理工具,用于管理端查询数据库结构,不用于编写客户端代码。",
199773
201170
  inputSchema: {
199774
201171
  action: zod_1.z.enum([
199775
201172
  "listCollections",
@@ -199807,9 +201204,14 @@ checkIndex: 检查指定索引是否存在`),
199807
201204
  }, async ({ action, limit, offset, collectionName, indexName }) => {
199808
201205
  const cloudbase = await getManager();
199809
201206
  if (action === "listCollections") {
199810
- const result = await cloudbase.database.listCollections({
199811
- MgoOffset: offset,
199812
- MgoLimit: limit,
201207
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions);
201208
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
201209
+ Action: "ListTables",
201210
+ Param: {
201211
+ EnvId: envId,
201212
+ MgoOffset: offset ?? 0,
201213
+ MgoLimit: limit ?? 100,
201214
+ },
199813
201215
  });
199814
201216
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199815
201217
  return {
@@ -199819,7 +201221,7 @@ checkIndex: 检查指定索引是否存在`),
199819
201221
  text: JSON.stringify({
199820
201222
  success: true,
199821
201223
  requestId: result.RequestId,
199822
- collections: result.Collections,
201224
+ collections: result.Tables,
199823
201225
  pager: result.Pager,
199824
201226
  message: "获取 NoSQL 数据库集合列表成功",
199825
201227
  }, null, 2),
@@ -199831,17 +201233,30 @@ checkIndex: 检查指定索引是否存在`),
199831
201233
  if (!collectionName) {
199832
201234
  throw new Error("检查集合时必须提供 collectionName");
199833
201235
  }
199834
- const result = await cloudbase.database.checkCollectionExists(collectionName);
199835
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
201236
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions);
201237
+ let exists = false;
201238
+ let requestId = "";
201239
+ try {
201240
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
201241
+ Action: "DescribeTable",
201242
+ Param: { EnvId: envId, TableName: collectionName },
201243
+ });
201244
+ exists = true;
201245
+ requestId = result?.RequestId ?? "";
201246
+ }
201247
+ catch (e) {
201248
+ // DescribeTable 失败即集合不存在
201249
+ requestId = e?.requestId ?? "";
201250
+ }
199836
201251
  return {
199837
201252
  content: [
199838
201253
  {
199839
201254
  type: "text",
199840
201255
  text: JSON.stringify(withCollectionName(collectionName, {
199841
201256
  success: true,
199842
- exists: result.Exists,
199843
- requestId: result.RequestId,
199844
- message: result.Exists
201257
+ exists,
201258
+ requestId,
201259
+ message: exists
199845
201260
  ? "云开发数据库集合已存在"
199846
201261
  : "云开发数据库集合不存在",
199847
201262
  }), null, 2),
@@ -199853,7 +201268,11 @@ checkIndex: 检查指定索引是否存在`),
199853
201268
  if (!collectionName) {
199854
201269
  throw new Error("查看集合详情时必须提供 collectionName");
199855
201270
  }
199856
- const result = await cloudbase.database.describeCollection(collectionName);
201271
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions);
201272
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
201273
+ Action: "DescribeTable",
201274
+ Param: { EnvId: envId, TableName: collectionName },
201275
+ });
199857
201276
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199858
201277
  return {
199859
201278
  content: [
@@ -199874,7 +201293,11 @@ checkIndex: 检查指定索引是否存在`),
199874
201293
  if (!collectionName) {
199875
201294
  throw new Error("获取索引列表时必须提供 collectionName");
199876
201295
  }
199877
- const result = await cloudbase.database.describeCollection(collectionName);
201296
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions);
201297
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
201298
+ Action: "DescribeTable",
201299
+ Param: { EnvId: envId, TableName: collectionName },
201300
+ });
199878
201301
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199879
201302
  return {
199880
201303
  content: [
@@ -199895,8 +201318,22 @@ checkIndex: 检查指定索引是否存在`),
199895
201318
  if (!collectionName || !indexName) {
199896
201319
  throw new Error("检查索引时必须提供 collectionName 和 indexName");
199897
201320
  }
199898
- const result = await cloudbase.database.checkIndexExists(collectionName, indexName);
199899
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
201321
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions);
201322
+ let exists = false;
201323
+ let requestId = "";
201324
+ try {
201325
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
201326
+ Action: "DescribeTable",
201327
+ Param: { EnvId: envId, TableName: collectionName },
201328
+ });
201329
+ requestId = result?.RequestId ?? "";
201330
+ const indexes = result?.Indexes ?? result?.IndexNum ? result?.Indexes : [];
201331
+ exists = Array.isArray(indexes) && indexes.some((idx) => idx.IndexName === indexName);
201332
+ }
201333
+ catch (e) {
201334
+ requestId = e?.requestId ?? "";
201335
+ }
201336
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, { RequestId: requestId });
199900
201337
  return {
199901
201338
  content: [
199902
201339
  {
@@ -199904,9 +201341,9 @@ checkIndex: 检查指定索引是否存在`),
199904
201341
  text: JSON.stringify(withCollectionName(collectionName, {
199905
201342
  success: true,
199906
201343
  indexName,
199907
- exists: result.Exists,
199908
- requestId: result.RequestId,
199909
- message: result.Exists ? "索引已存在" : "索引不存在",
201344
+ exists,
201345
+ requestId,
201346
+ message: exists ? "索引已存在" : "索引不存在",
199910
201347
  }), null, 2),
199911
201348
  },
199912
201349
  ],
@@ -199916,8 +201353,8 @@ checkIndex: 检查指定索引是否存在`),
199916
201353
  });
199917
201354
  // writeNoSqlDatabaseStructure
199918
201355
  server.registerTool?.("writeNoSqlDatabaseStructure", {
199919
- title: "修改 NoSQL 数据库结构",
199920
- description: "修改 NoSQL 数据库结构,支持创建/删除集合,以及通过 updateCollection 的 updateOptions.CreateIndexes / updateOptions.DropIndexes 添加索引和删除索引。",
201356
+ title: "创建并管理 NoSQL 数据库集合",
201357
+ description: "创建、删除和管理 NoSQL 数据库集合(collection)。支持创建新集合、删除现有集合,以及通过 updateCollection 的 updateOptions.CreateIndexes / updateOptions.DropIndexes 添加索引和删除索引。当需要新建集合时,使用 action=createCollection。本工具为服务端管理工具,用于管理端操作集合和索引结构,不用于编写客户端代码。",
199921
201358
  inputSchema: {
199922
201359
  action: zod_1.z.enum([
199923
201360
  "createCollection",
@@ -199964,9 +201401,22 @@ deleteCollection: 删除集合`),
199964
201401
  }, async ({ action, collectionName, updateOptions }) => {
199965
201402
  const cloudbase = await getManager();
199966
201403
  if (action === "createCollection") {
199967
- const existsResult = await cloudbase.database.checkCollectionExists(collectionName);
199968
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, existsResult);
199969
- if (existsResult.Exists) {
201404
+ // 检查集合是否存在 - 使用 commonService 避免 SDK lazyInit → DescribeEnvs
201405
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions);
201406
+ let collectionExists = false;
201407
+ let existsRequestId = "";
201408
+ try {
201409
+ const descResult = await cloudbase.commonService("tcb", "2018-06-08").call({
201410
+ Action: "DescribeTable",
201411
+ Param: { EnvId: envId, TableName: collectionName },
201412
+ });
201413
+ collectionExists = true;
201414
+ existsRequestId = descResult?.RequestId ?? "";
201415
+ }
201416
+ catch (e) {
201417
+ existsRequestId = e?.requestId ?? "";
201418
+ }
201419
+ if (collectionExists) {
199970
201420
  return {
199971
201421
  content: [
199972
201422
  {
@@ -199974,7 +201424,7 @@ deleteCollection: 删除集合`),
199974
201424
  text: JSON.stringify(withCollectionName(collectionName, {
199975
201425
  success: true,
199976
201426
  action,
199977
- requestId: existsResult.RequestId,
201427
+ requestId: existsRequestId,
199978
201428
  message: "集合已存在,无需重复创建",
199979
201429
  exists: true,
199980
201430
  }), null, 2),
@@ -199982,7 +201432,13 @@ deleteCollection: 删除集合`),
199982
201432
  ],
199983
201433
  };
199984
201434
  }
199985
- const result = await cloudbase.database.createCollection(collectionName);
201435
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
201436
+ Action: "CreateTable",
201437
+ Param: {
201438
+ EnvId: await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions),
201439
+ TableName: collectionName,
201440
+ },
201441
+ });
199986
201442
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199987
201443
  await waitForCollectionReady({
199988
201444
  cloudbase,
@@ -200007,7 +201463,14 @@ deleteCollection: 删除集合`),
200007
201463
  if (!updateOptions) {
200008
201464
  throw new Error("更新集合时必须提供 options");
200009
201465
  }
200010
- const result = await cloudbase.database.updateCollection(collectionName, updateOptions);
201466
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
201467
+ Action: "UpdateTable",
201468
+ Param: {
201469
+ EnvId: await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions),
201470
+ TableName: collectionName,
201471
+ ...updateOptions,
201472
+ },
201473
+ });
200011
201474
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
200012
201475
  return {
200013
201476
  content: [
@@ -200024,7 +201487,13 @@ deleteCollection: 删除集合`),
200024
201487
  };
200025
201488
  }
200026
201489
  if (action === "deleteCollection") {
200027
- const result = await cloudbase.database.deleteCollection(collectionName);
201490
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
201491
+ Action: "DeleteTable",
201492
+ Param: {
201493
+ EnvId: await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions),
201494
+ TableName: collectionName,
201495
+ },
201496
+ });
200028
201497
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
200029
201498
  const body = withCollectionName(collectionName, {
200030
201499
  success: true,
@@ -200049,7 +201518,7 @@ deleteCollection: 删除集合`),
200049
201518
  // readNoSqlDatabaseContent
200050
201519
  server.registerTool?.("readNoSqlDatabaseContent", {
200051
201520
  title: "查询并获取 NoSQL 数据库数据记录",
200052
- description: "查询并获取 NoSQL 数据库数据记录",
201521
+ description: "查询并获取 NoSQL 数据库数据记录。⚠️ 本工具为服务端管理工具,用于管理端/运维端查询。当任务要求编写客户端应用代码时(例如「用 JS SDK 读取数据」),不应使用本工具,而应在项目代码中编写 @cloudbase/js-sdk 客户端代码(如 db.collection().where().get())。",
200053
201522
  inputSchema: {
200054
201523
  collectionName: zod_1.z.string().describe("集合名称"),
200055
201524
  instanceId: zod_1.z
@@ -200136,7 +201605,7 @@ deleteCollection: 删除集合`),
200136
201605
  // writeNoSqlDatabaseContent
200137
201606
  server.registerTool?.("writeNoSqlDatabaseContent", {
200138
201607
  title: "修改 NoSQL 数据库数据记录",
200139
- description: "修改 NoSQL 数据库数据记录。可按 MongoDB updateOne/updateMany 的心智模型理解:部分更新必须使用 `$set`、`$inc`、`$push` 等更新操作符;如果直接传“字段到值的普通对象”这类内容,底层会把它当作替换内容,存在覆盖整条文档的风险。更新嵌套对象中的某个字段时必须使用点号路径,例如把 `address.city` 设为 `shenzhen`;如果把整个 `address` 对象作为 `$set` 的值传入,则整个 `address` 对象会被替换,同级其他字段将丢失。若集合中的角色/档案文档会在前端通过 `db.collection(...).doc(uid)` 读取,请确保文档 `_id` 就是该 `uid`;不要用按 `uid` 条件查询再配合 `upsert=true` 的方式去更新 `users` / `profiles`,否则经常会生成一个不同的 `_id`,导致后续 `doc(uid)` 读取命中不到。",
201608
+ description: "修改 NoSQL 数据库数据记录。⚠️ 本工具为服务端管理工具,用于管理端/运维端操作(如后台脚本、数据迁移、批量修改)。当任务要求编写客户端应用代码时(例如「用 JS SDK 登录并插入数据」、「在前端读写数据库」),不应使用本工具,而应在项目代码中编写 @cloudbase/js-sdk 客户端代码(如 app.database().collection().add()、db.collection().where().get() 等)。可按 MongoDB updateOne/updateMany 的心智模型理解:部分更新必须使用 `$set`、`$inc`、`$push` 等更新操作符;如果直接传「字段到值的普通对象」这类内容,底层会把它当作替换内容,存在覆盖整条文档的风险。⚠️ 嵌套对象部分更新务必使用点号路径:要更新 `shipping.city`,应传 `$set: {\"shipping.city\": \"guangzhou\"}`,绝不能传 `$set: {\"shipping\": {\"city\": \"guangzhou\"}}`(后者会丢失 shipping 下的其他字段)。若集合中的角色/档案文档会在前端通过 `db.collection(...).doc(uid)` 读取,请确保文档 `_id` 就是该 `uid`;不要用按 `uid` 条件查询再配合 `upsert=true` 的方式去更新 `users` / `profiles`,否则经常会生成一个不同的 `_id`,导致后续 `doc(uid)` 读取命中不到。",
200140
201609
  inputSchema: {
200141
201610
  action: zod_1.z
200142
201611
  .enum(["insert", "update", "delete"])
@@ -200157,7 +201626,11 @@ deleteCollection: 删除集合`),
200157
201626
  update: zod_1.z
200158
201627
  .union([zod_1.z.object({}).passthrough(), zod_1.z.string()])
200159
201628
  .optional()
200160
- .describe("更新内容(对象或字符串,推荐对象)(update 操作必填)。按 MongoDB 更新语义传入 MgoUpdate:部分更新请使用 `$set`、`$inc`、`$unset`、`$push` 等操作符,例如使用 `$set` 更新 `status`;不要直接传“字段到值的普通对象”,否则可能替换整条文档。更新嵌套字段时必须使用点号路径,例如通过 `$set` 更新 `address.city`;不要把整个 `address` 对象作为 `$set` 的值传入,否则会替换整个 `address` 对象。"),
201629
+ .describe(`更新内容(对象或字符串,推荐对象)(update 操作必填)。按 MongoDB 更新语义传入 MgoUpdate:部分更新请使用 \`$set\`、\`$inc\`、\`$unset\`、\`$push\` 等操作符,例如使用 \`$set\` 更新 \`status\`;不要直接传"字段到值的普通对象",否则可能替换整条文档。
201630
+
201631
+ ⚠️ 嵌套字段必须用点号路径(如 \`shipping.city\`),禁止整对象替换:
201632
+ - ❌ 错误:{ "$set": { "shipping": { "city": "guangzhou" } } } — shipping 被整块替换,原有 address/province 等字段全部丢失
201633
+ - ✅ 正确:{ "$set": { "shipping.city": "guangzhou" } } — 仅更新 city,shipping 下其他字段保留`),
200161
201634
  isMulti: zod_1.z
200162
201635
  .boolean()
200163
201636
  .optional()
@@ -205148,6 +206621,23 @@ function buildPermissionHints(securityRule, resourceId) {
205148
206621
  buildTemplateLiteralRuleHint(securityRule, resourceId),
205149
206622
  ].filter(Boolean);
205150
206623
  }
206624
+ async function ensureStorageBucketsExist(cloudbase, resourceIds) {
206625
+ if (!resourceIds.length) {
206626
+ return;
206627
+ }
206628
+ const envInfo = await cloudbase.env.getEnvInfo();
206629
+ const existingBuckets = new Set((envInfo?.EnvInfo?.Storages ?? [])
206630
+ .map((item) => item?.Bucket)
206631
+ .filter((bucket) => Boolean(bucket)));
206632
+ const missingBuckets = resourceIds.filter((resourceId) => !existingBuckets.has(resourceId));
206633
+ if (missingBuckets.length === 0) {
206634
+ return;
206635
+ }
206636
+ if (missingBuckets.length === 1) {
206637
+ throw new Error(`存储 Bucket ${missingBuckets[0]} 不存在`);
206638
+ }
206639
+ throw new Error(`以下存储 Bucket 不存在: ${missingBuckets.join(", ")}`);
206640
+ }
205151
206641
  function registerPermissionTools(server) {
205152
206642
  const cloudBaseOptions = server.cloudBaseOptions;
205153
206643
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
@@ -205190,20 +206680,24 @@ function registerPermissionTools(server) {
205190
206680
  if (!resourceType || !resourceId) {
205191
206681
  throw new Error("action=getResourcePermission 时必须提供 resourceType 和 resourceId");
205192
206682
  }
206683
+ if (resourceType === "storage") {
206684
+ await ensureStorageBucketsExist(cloudbase, [resourceId]);
206685
+ }
205193
206686
  const result = await cloudbase.permission.describeResourcePermission({
205194
206687
  resourceType: mapResourceType(resourceType),
205195
206688
  resources: [resourceId],
205196
206689
  });
205197
206690
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
205198
206691
  const permissions = result.Data.PermissionList ?? [];
205199
- const securityRule = permissions.find((item) => item.Resource === resourceId)?.SecurityRule ??
205200
- permissions[0]?.SecurityRule;
206692
+ const matchedPermission = permissions.find((item) => item.Resource === resourceId) ?? permissions[0];
206693
+ const securityRule = matchedPermission?.SecurityRule;
205201
206694
  const hints = buildPermissionHints(securityRule, resourceId);
205202
206695
  return buildEnvelope({
205203
206696
  action,
205204
206697
  envId,
205205
206698
  resourceType,
205206
206699
  resourceId,
206700
+ aclTag: matchedPermission?.Permission,
205207
206701
  permissions,
205208
206702
  hints,
205209
206703
  raw: result,
@@ -205213,6 +206707,9 @@ function registerPermissionTools(server) {
205213
206707
  if (!resourceType) {
205214
206708
  throw new Error("action=listResourcePermissions 时必须提供 resourceType");
205215
206709
  }
206710
+ if (resourceType === "storage" && resourceIds?.length) {
206711
+ await ensureStorageBucketsExist(cloudbase, resourceIds);
206712
+ }
205216
206713
  const result = await cloudbase.permission.describeResourcePermission({
205217
206714
  resourceType: mapResourceType(resourceType),
205218
206715
  resources: resourceIds,
@@ -205683,7 +207180,9 @@ const http = __importStar(__webpack_require__(81630));
205683
207180
  const https = __importStar(__webpack_require__(55591));
205684
207181
  const os = __importStar(__webpack_require__(21820));
205685
207182
  const path = __importStar(__webpack_require__(39902));
207183
+ const url_1 = __webpack_require__(79551);
205686
207184
  const zod_1 = __webpack_require__(21614);
207185
+ const remote_url_safety_js_1 = __webpack_require__(89192);
205687
207186
  // CloudBase 模板配置
205688
207187
  const TEMPLATES = {
205689
207188
  react: {
@@ -205876,13 +207375,18 @@ function resolveDownloadTemplateIDE(ide, integrationIDE) {
205876
207375
  supportedIDEs: exports.IDE_TYPES.filter((t) => t !== "all"),
205877
207376
  };
205878
207377
  }
207378
+ const MAX_REDIRECT_HOPS = 5;
205879
207379
  // 根据 INTEGRATION_IDE 环境变量获取默认 IDE 类型
205880
207380
  // 下载文件到临时目录
205881
- async function downloadFile(url, filePath) {
207381
+ async function downloadFile(url, filePath, redirectCount = 0) {
207382
+ if (redirectCount > MAX_REDIRECT_HOPS) {
207383
+ throw new Error(`重定向次数超过 ${MAX_REDIRECT_HOPS} 次限制`);
207384
+ }
207385
+ const { requestUrl, requestOptions } = await (0, remote_url_safety_js_1.prepareSafeRemoteRequest)(url);
207386
+ const client = requestUrl.protocol === "https:" ? https : http;
205882
207387
  return new Promise((resolve, reject) => {
205883
- const client = url.startsWith("https:") ? https : http;
205884
207388
  client
205885
- .get(url, (res) => {
207389
+ .get(requestUrl, requestOptions ?? {}, (res) => {
205886
207390
  if (res.statusCode === 200) {
205887
207391
  const file = fs.createWriteStream(filePath);
205888
207392
  res.pipe(file);
@@ -205892,10 +207396,11 @@ async function downloadFile(url, filePath) {
205892
207396
  });
205893
207397
  file.on("error", reject);
205894
207398
  }
205895
- else if (res.statusCode === 302 || res.statusCode === 301) {
207399
+ else if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400) {
205896
207400
  // 处理重定向
205897
207401
  if (res.headers.location) {
205898
- downloadFile(res.headers.location, filePath)
207402
+ const redirectUrl = new url_1.URL(res.headers.location, requestUrl).toString();
207403
+ downloadFile(redirectUrl, filePath, redirectCount + 1)
205899
207404
  .then(resolve)
205900
207405
  .catch(reject);
205901
207406
  }
@@ -206091,9 +207596,8 @@ function registerSetupTools(server) {
206091
207596
  // downloadTemplate - 下载项目模板 (cloud-incompatible)
206092
207597
  server.registerTool("downloadTemplate", {
206093
207598
  title: "下载项目模板",
206094
- description: `自动下载并部署CloudBase项目模板。⚠️ **MANDATORY FOR NEW PROJECTS** ⚠️
206095
-
206096
- **CRITICAL**: This tool MUST be called FIRST when starting a new project.\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.18.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
207599
+ description: `自动下载并部署CloudBase项目模板。
207600
+ **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.19.5" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
206097
207601
  inputSchema: {
206098
207602
  template: zod_1.z
206099
207603
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -223091,17 +224595,10 @@ function normalizeAccessPath(path) {
223091
224595
  }
223092
224596
  return path.startsWith("/") ? path : `/${path}`;
223093
224597
  }
223094
- function ensureGatewayEnvId(cloudBaseOptions) {
223095
- const envId = cloudBaseOptions?.envId;
223096
- if (!envId) {
223097
- throw new Error("当前网关操作需要已绑定 envId");
223098
- }
223099
- return envId;
223100
- }
223101
224598
  function registerGatewayTools(server) {
223102
224599
  const cloudBaseOptions = server.cloudBaseOptions;
223103
224600
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
223104
- const getGatewayEnvId = () => ensureGatewayEnvId(cloudBaseOptions);
224601
+ const resolveEnvId = () => (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
223105
224602
  const buildEnvelope = (data, message, nextActions) => ({
223106
224603
  success: true,
223107
224604
  data,
@@ -223138,7 +224635,7 @@ function registerGatewayTools(server) {
223138
224635
  const listHttpServiceRoutes = async (domain) => {
223139
224636
  const cloudbase = await getManager();
223140
224637
  const result = await cloudbase.env.describeHttpServiceRoute({
223141
- EnvId: getGatewayEnvId(),
224638
+ EnvId: await resolveEnvId(),
223142
224639
  ...(domain
223143
224640
  ? {
223144
224641
  Filters: [
@@ -223174,7 +224671,7 @@ function registerGatewayTools(server) {
223174
224671
  throw new Error("route.serviceType 和 route.serviceName 为必填项");
223175
224672
  }
223176
224673
  return {
223177
- EnvId: getGatewayEnvId(),
224674
+ EnvId: await resolveEnvId(),
223178
224675
  Domain: {
223179
224676
  Domain: await resolveRouteDomain(domain),
223180
224677
  Routes: [
@@ -223284,6 +224781,9 @@ function registerGatewayTools(server) {
223284
224781
  if (!input.targetName || !input.targetType) {
223285
224782
  throw new Error("action=createAccess 时必须提供 targetType 和 targetName");
223286
224783
  }
224784
+ if (!input.type) {
224785
+ throw new Error("action=createAccess 且 targetType=function 时必须显式提供 type。HTTP 云函数传 HTTP;Event 函数传 Event。省略会默认按 Event 路由处理,可能让 HTTP 云函数访问后返回 FUNCTION_PARAM_INVALID。");
224786
+ }
223287
224787
  const cloudbase = await getManager();
223288
224788
  const accessPath = normalizeAccessPath(input.path || `/${input.targetName}`);
223289
224789
  let result;
@@ -223291,7 +224791,7 @@ function registerGatewayTools(server) {
223291
224791
  result = await cloudbase.access.createAccess({
223292
224792
  name: input.targetName,
223293
224793
  path: accessPath,
223294
- type: ((input.type || "Event") === "HTTP" ? 6 : 1),
224794
+ type: (input.type === "HTTP" ? 6 : 1),
223295
224795
  auth: input.auth,
223296
224796
  });
223297
224797
  }
@@ -223299,7 +224799,7 @@ function registerGatewayTools(server) {
223299
224799
  if (err.message && err.message.includes("An error has occurred")) {
223300
224800
  let hint = "为目标资源配置访问路由失败(后端内部错误)。请确保:1) 目标云函数已成功创建并处于 Active 状态;2) 环境默认 HTTP 域名已完成初始化;3) 该访问路径未被占用。";
223301
224801
  if (input.type === "HTTP") {
223302
- hint += "此外注意:如果目标函数最初是作为 Event 函数创建的,这里 type 必须依然传 Event(或省略),传 HTTP 会导致此错误。";
224802
+ hint += "此外注意:如果目标函数最初是作为 Event 函数创建的,这里 type 仍必须传 Event;误传 HTTP 会导致此错误。";
223303
224803
  }
223304
224804
  throw new Error(`${hint} 原始错误:${err.message}`);
223305
224805
  }
@@ -223312,12 +224812,22 @@ function registerGatewayTools(server) {
223312
224812
  targetName: input.targetName,
223313
224813
  path: accessPath,
223314
224814
  raw: result,
223315
- }, `已为目标 ${input.targetName} 创建网关访问路径。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。`, [
224815
+ }, `已为目标 ${input.targetName} 创建网关访问路径。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。该操作只创建网关入口,不会自动放开函数安全规则;若需要匿名或浏览器直接访问,请继续检查函数资源权限。`, [
223316
224816
  {
223317
224817
  tool: "queryGateway",
223318
224818
  action: "getAccess",
223319
224819
  reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
223320
224820
  },
224821
+ {
224822
+ tool: "queryPermissions",
224823
+ action: "getResourcePermission",
224824
+ reason: "确认函数安全规则是否允许预期访问方;网关 auth=false 不等于函数已允许匿名访问",
224825
+ },
224826
+ {
224827
+ tool: "managePermissions",
224828
+ action: "updateResourcePermission",
224829
+ reason: "只有在确认需要匿名或浏览器直连访问时,才按实际安全要求更新函数权限",
224830
+ },
223321
224831
  ]);
223322
224832
  }
223323
224833
  case "createRoute": {
@@ -223348,7 +224858,7 @@ function registerGatewayTools(server) {
223348
224858
  const cloudbase = await getManager();
223349
224859
  const domain = await resolveRouteDomain(input.domain);
223350
224860
  const result = await cloudbase.env.deleteHttpServiceRoute({
223351
- EnvId: getGatewayEnvId(),
224861
+ EnvId: await resolveEnvId(),
223352
224862
  Domain: domain,
223353
224863
  Paths: [normalizeAccessPath(routePath)],
223354
224864
  });
@@ -223366,7 +224876,7 @@ function registerGatewayTools(server) {
223366
224876
  }
223367
224877
  const cloudbase = await getManager();
223368
224878
  const result = await cloudbase.env.bindCustomDomain({
223369
- EnvId: getGatewayEnvId(),
224879
+ EnvId: await resolveEnvId(),
223370
224880
  Domain: {
223371
224881
  Domain: input.domain,
223372
224882
  CertId: input.certificateId,
@@ -223386,7 +224896,7 @@ function registerGatewayTools(server) {
223386
224896
  }
223387
224897
  const cloudbase = await getManager();
223388
224898
  const result = await cloudbase.env.deleteCustomDomain({
223389
- EnvId: getGatewayEnvId(),
224899
+ EnvId: await resolveEnvId(),
223390
224900
  Domain: input.domain,
223391
224901
  });
223392
224902
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
@@ -223470,18 +224980,32 @@ function registerGatewayTools(server) {
223470
224980
  },
223471
224981
  }, async (input) => withEnvelope(() => handleQueryGateway(input)));
223472
224982
  server.registerTool?.("manageGateway", {
223473
- title: "管理网关域资源",
223474
- description: "网关域统一写入口。通过 action 创建目标访问入口,后续承接更通用的网关配置能力。",
224983
+ title: "管理网关域资源(含自定义域名绑定)",
224984
+ description: "网关域统一写入口。通过 action 创建目标访问入口,后续承接更通用的网关配置能力。为已存在的 HTTP 云函数补默认域名访问时,通常使用 createAccess 并提供 targetType=\"function\"、targetName、type=\"HTTP\" 与期望 path。注意 createAccess 只创建网关入口,不会自动修改函数资源权限。⚠️ 如需绑定带 SSL 证书的自定义域名供公网 HTTPS 访问,使用 action=\"bindCustomDomain\"(需要 domain 和 certificateId 参数);如需配置 CORS/安全域名(无证书),请使用 envDomainManagement 工具。",
223475
224985
  inputSchema: {
223476
- action: zod_1.z.enum(MANAGE_GATEWAY_ACTIONS).describe("写操作类型,例如 createAccess"),
224986
+ action: zod_1.z
224987
+ .enum(MANAGE_GATEWAY_ACTIONS)
224988
+ .describe('写操作类型,例如 createAccess。为已有函数补默认域名访问入口时使用 createAccess;若 action=createAccess 且 targetType=function,必须显式提供 type。'),
223477
224989
  targetType: zod_1.z
223478
224990
  .enum(["function"])
223479
224991
  .optional()
223480
224992
  .describe("目标资源类型。当前支持 function,后续可扩展"),
223481
- targetName: zod_1.z.string().optional().describe("目标资源名称"),
223482
- path: zod_1.z.string().optional().describe("访问路径,默认 /{targetName}"),
223483
- type: zod_1.z.enum(["Event", "HTTP"]).optional().describe("目标函数的本身类型(非接入形式)。如果被访问的函数是 Event 型(默认),此处必须传 Event;只有当被访问函数在创建时就是 HTTP 函数时才传 HTTP。"),
223484
- auth: zod_1.z.boolean().optional().describe("是否开启鉴权"),
224993
+ targetName: zod_1.z
224994
+ .string()
224995
+ .optional()
224996
+ .describe("目标资源名称。createAccess 到云函数时填写函数名"),
224997
+ path: zod_1.z
224998
+ .string()
224999
+ .optional()
225000
+ .describe("访问路径,默认 /{targetName}。例如为 HTTP 函数暴露 /api/hello 时传 /api/hello。该参数只创建网关入口,不会自动放开函数资源权限。"),
225001
+ type: zod_1.z
225002
+ .enum(["Event", "HTTP"])
225003
+ .optional()
225004
+ .describe("目标函数的运行时类型,不是接入协议。createAccess 到已创建的 HTTP 云函数时传 HTTP;给 Event 函数补网关访问时传 Event 或省略。省略会默认按 Event 路由处理,可能让 HTTP 云函数访问后返回 FUNCTION_PARAM_INVALID。"),
225005
+ auth: zod_1.z
225006
+ .boolean()
225007
+ .optional()
225008
+ .describe("是否开启网关路径鉴权。若要走默认域名做匿名或浏览器访问,通常设为 false;该开关仅控制网关入口本身,不会修改函数资源权限,仍需检查并按需调整函数安全规则。"),
223485
225009
  route: zod_1.z
223486
225010
  .object({
223487
225011
  routeId: zod_1.z.string().optional(),
@@ -232404,6 +233928,192 @@ module.exports = function generate_ref(it, $keyword, $ruleType) {
232404
233928
  }
232405
233929
 
232406
233930
 
233931
+ /***/ }),
233932
+
233933
+ /***/ 89192:
233934
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
233935
+
233936
+ "use strict";
233937
+
233938
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
233939
+ if (k2 === undefined) k2 = k;
233940
+ var desc = Object.getOwnPropertyDescriptor(m, k);
233941
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
233942
+ desc = { enumerable: true, get: function() { return m[k]; } };
233943
+ }
233944
+ Object.defineProperty(o, k2, desc);
233945
+ }) : (function(o, m, k, k2) {
233946
+ if (k2 === undefined) k2 = k;
233947
+ o[k2] = m[k];
233948
+ }));
233949
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
233950
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
233951
+ }) : function(o, v) {
233952
+ o["default"] = v;
233953
+ });
233954
+ var __importStar = (this && this.__importStar) || (function () {
233955
+ var ownKeys = function(o) {
233956
+ ownKeys = Object.getOwnPropertyNames || function (o) {
233957
+ var ar = [];
233958
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
233959
+ return ar;
233960
+ };
233961
+ return ownKeys(o);
233962
+ };
233963
+ return function (mod) {
233964
+ if (mod && mod.__esModule) return mod;
233965
+ var result = {};
233966
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
233967
+ __setModuleDefault(result, mod);
233968
+ return result;
233969
+ };
233970
+ })();
233971
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
233972
+ exports.isPrivateIP = isPrivateIP;
233973
+ exports.prepareSafeRemoteRequest = prepareSafeRemoteRequest;
233974
+ exports.assertRemoteUrlSafe = assertRemoteUrlSafe;
233975
+ exports.getRemoteUrlSafetyErrorMessage = getRemoteUrlSafetyErrorMessage;
233976
+ const dns = __importStar(__webpack_require__(14985));
233977
+ const net = __importStar(__webpack_require__(91645));
233978
+ const url_1 = __webpack_require__(79551);
233979
+ const ALLOWED_PROTOCOLS = new Set(["http:", "https:"]);
233980
+ const DISALLOWED_ERROR_MESSAGE = "不安全的 URL 或目标为内网地址";
233981
+ function ipv4ToNumber(ip) {
233982
+ const parts = ip.split(".").map((part) => Number.parseInt(part, 10));
233983
+ return ((parts[0] << 24) >>> 0) + (parts[1] << 16) + (parts[2] << 8) + parts[3];
233984
+ }
233985
+ function isIpv4InRange(ip, start, end) {
233986
+ const ipNumber = ipv4ToNumber(ip);
233987
+ return ipNumber >= ipv4ToNumber(start) && ipNumber <= ipv4ToNumber(end);
233988
+ }
233989
+ function normalizeIpAddress(ip) {
233990
+ const normalized = ip.toLowerCase();
233991
+ if (normalized.startsWith("::ffff:")) {
233992
+ const mappedIpv4 = normalized.slice(7);
233993
+ if (net.isIPv4(mappedIpv4)) {
233994
+ return mappedIpv4;
233995
+ }
233996
+ }
233997
+ return normalized;
233998
+ }
233999
+ function isPrivateIP(ip) {
234000
+ const normalizedIp = normalizeIpAddress(ip);
234001
+ if (!net.isIP(normalizedIp)) {
234002
+ return true;
234003
+ }
234004
+ if (net.isIPv4(normalizedIp)) {
234005
+ return [
234006
+ ["0.0.0.0", "0.255.255.255"],
234007
+ ["10.0.0.0", "10.255.255.255"],
234008
+ ["100.64.0.0", "100.127.255.255"],
234009
+ ["127.0.0.0", "127.255.255.255"],
234010
+ ["169.254.0.0", "169.254.255.255"],
234011
+ ["172.16.0.0", "172.31.255.255"],
234012
+ ["192.0.0.0", "192.0.0.255"],
234013
+ ["192.0.2.0", "192.0.2.255"],
234014
+ ["192.168.0.0", "192.168.255.255"],
234015
+ ["198.18.0.0", "198.19.255.255"],
234016
+ ["198.51.100.0", "198.51.100.255"],
234017
+ ["203.0.113.0", "203.0.113.255"],
234018
+ ["224.0.0.0", "255.255.255.255"],
234019
+ ].some(([start, end]) => isIpv4InRange(normalizedIp, start, end));
234020
+ }
234021
+ return normalizedIp === "::" ||
234022
+ normalizedIp === "::1" ||
234023
+ normalizedIp.startsWith("fc") ||
234024
+ normalizedIp.startsWith("fd") ||
234025
+ normalizedIp.startsWith("fe8") ||
234026
+ normalizedIp.startsWith("fe9") ||
234027
+ normalizedIp.startsWith("fea") ||
234028
+ normalizedIp.startsWith("feb") ||
234029
+ normalizedIp.startsWith("fec0:") ||
234030
+ normalizedIp.startsWith("ff") ||
234031
+ normalizedIp.startsWith("2001:db8:");
234032
+ }
234033
+ async function resolveSafeAddresses(hostname) {
234034
+ let resolvedAddresses;
234035
+ try {
234036
+ resolvedAddresses = await dns.promises.lookup(hostname, {
234037
+ all: true,
234038
+ verbatim: true,
234039
+ });
234040
+ }
234041
+ catch {
234042
+ throw new Error(DISALLOWED_ERROR_MESSAGE);
234043
+ }
234044
+ if (resolvedAddresses.length === 0) {
234045
+ throw new Error(DISALLOWED_ERROR_MESSAGE);
234046
+ }
234047
+ const normalizedAddresses = resolvedAddresses.map(({ address, family }) => ({
234048
+ address: normalizeIpAddress(address),
234049
+ family,
234050
+ }));
234051
+ if (normalizedAddresses.some(({ address }) => isPrivateIP(address))) {
234052
+ throw new Error(DISALLOWED_ERROR_MESSAGE);
234053
+ }
234054
+ return normalizedAddresses.filter((candidate, index, collection) => collection.findIndex(({ address, family }) => address === candidate.address && family === candidate.family) === index);
234055
+ }
234056
+ function createPinnedLookup(addresses) {
234057
+ return ((hostname, options, callback) => {
234058
+ const normalizedOptions = typeof options === "number" ? { family: options } : (options ?? {});
234059
+ const family = normalizedOptions.family;
234060
+ const candidates = family === 4 || family === 6
234061
+ ? addresses.filter((address) => address.family === family)
234062
+ : addresses;
234063
+ if (candidates.length === 0) {
234064
+ const error = Object.assign(new Error(`No safe address resolved for ${hostname}`), {
234065
+ code: "ENOTFOUND",
234066
+ });
234067
+ callback(error);
234068
+ return;
234069
+ }
234070
+ if (normalizedOptions.all) {
234071
+ callback(null, candidates);
234072
+ return;
234073
+ }
234074
+ callback(null, candidates[0].address, candidates[0].family);
234075
+ });
234076
+ }
234077
+ async function prepareSafeRemoteRequest(url) {
234078
+ let requestUrl;
234079
+ try {
234080
+ requestUrl = new url_1.URL(url);
234081
+ }
234082
+ catch {
234083
+ throw new Error(DISALLOWED_ERROR_MESSAGE);
234084
+ }
234085
+ if (!ALLOWED_PROTOCOLS.has(requestUrl.protocol)) {
234086
+ throw new Error(DISALLOWED_ERROR_MESSAGE);
234087
+ }
234088
+ const hostname = requestUrl.hostname;
234089
+ if (!hostname) {
234090
+ throw new Error(DISALLOWED_ERROR_MESSAGE);
234091
+ }
234092
+ if (hostname.toLowerCase() === "localhost") {
234093
+ throw new Error(DISALLOWED_ERROR_MESSAGE);
234094
+ }
234095
+ if (net.isIP(hostname)) {
234096
+ if (isPrivateIP(hostname)) {
234097
+ throw new Error(DISALLOWED_ERROR_MESSAGE);
234098
+ }
234099
+ return { requestUrl };
234100
+ }
234101
+ const safeAddresses = await resolveSafeAddresses(hostname);
234102
+ return {
234103
+ requestUrl,
234104
+ requestOptions: {
234105
+ lookup: createPinnedLookup(safeAddresses),
234106
+ },
234107
+ };
234108
+ }
234109
+ async function assertRemoteUrlSafe(url) {
234110
+ await prepareSafeRemoteRequest(url);
234111
+ }
234112
+ function getRemoteUrlSafetyErrorMessage() {
234113
+ return DISALLOWED_ERROR_MESSAGE;
234114
+ }
234115
+
234116
+
232407
234117
  /***/ }),
232408
234118
 
232409
234119
  /***/ 89225:
@@ -232883,8 +234593,7 @@ function shouldRegisterTool(toolName) {
232883
234593
  'auth',
232884
234594
  // Storage tools - local file uploads
232885
234595
  'uploadFile',
232886
- // Hosting tools - local file uploads
232887
- 'uploadFiles',
234596
+ // Hosting tools - action-level cloud gating is handled inside manageHosting
232888
234597
  // Function tools - local code uploads
232889
234598
  'updateFunctionCode',
232890
234599
  'createFunction', // also involves local files
@@ -240567,16 +242276,59 @@ function registerLogTools(server) {
240567
242276
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
240568
242277
  server.registerTool?.("queryLogs", {
240569
242278
  title: "查询日志服务",
240570
- description: "日志域统一只读入口。支持检查日志服务状态并搜索 CLS 日志。",
242279
+ description: "日志域统一只读入口。支持检查日志服务状态并搜索 CLS 日志。" +
242280
+ "\n\n**重要区分**:" +
242281
+ "\n- 查询云函数日志:使用 `queryFunctions(action=\"listFunctionLogs\", functionName=\"xxx\")`" +
242282
+ "\n- 查询 CLS 日志(跨服务日志聚合):使用本工具 `queryLogs(action=\"searchLogs\")`" +
242283
+ "\n\n**适用场景**:" +
242284
+ "\n- 检查 CLS 日志服务是否开通:`action=\"checkLogService\"`" +
242285
+ "\n- 跨服务日志搜索(如搜索所有 ERROR 日志):`action=\"searchLogs\"`" +
242286
+ "\n- 按 CLS 语法检索特定服务的日志:`action=\"searchLogs\", service=\"tcb|tcbr\"`",
240571
242287
  inputSchema: {
240572
- action: zod_1.z.enum(QUERY_LOG_ACTIONS),
240573
- queryString: zod_1.z.string().optional(),
240574
- service: zod_1.z.enum(["tcb", "tcbr"]).optional(),
240575
- startTime: zod_1.z.string().optional(),
240576
- endTime: zod_1.z.string().optional(),
240577
- limit: zod_1.z.number().optional(),
240578
- context: zod_1.z.string().optional(),
240579
- sort: zod_1.z.enum(["asc", "desc"]).optional(),
242288
+ action: zod_1.z
242289
+ .enum(QUERY_LOG_ACTIONS)
242290
+ .describe("操作类型:" +
242291
+ "\n- `checkLogService`: 检查 CLS 日志服务是否开通" +
242292
+ "\n- `searchLogs`: 搜索 CLS 日志(需要提供 queryString)"),
242293
+ queryString: zod_1.z
242294
+ .string()
242295
+ .optional()
242296
+ .describe("CLS 查询语句,**action=\"searchLogs\" 时必填**,需严格遵循 CLS(Cloud Log Service)语法规范,详见 https://cloud.tencent.com/document/api/876/128127" +
242297
+ "\n\n**云函数相关查询**:" +
242298
+ "\n- 云函数日志:`(src:app OR src:system) AND log:\"START RequestId\"`" +
242299
+ "\n- 聚合云函数请求状态:`| select request_id, max(status_code) as status where ((request_id='44738f94-16dd-11f1-****' AND retry_num=0) AND retry_num=0) AND status_code!=202 group by request_id, retry_num`" +
242300
+ "\n\n**云数据库 / 文档型**:" +
242301
+ "\n- 云数据库(文档型):`module:database`" +
242302
+ "\n- 云数据库(文档型)事件:`module:database AND eventType:(MongoSlowQuery)`(MongoSlowQuery 为文档型数据库慢查询事件)" +
242303
+ "\n\n**云数据库 / SQL 型**:" +
242304
+ "\n- 云数据库(SQL 型):`module:rdb`" +
242305
+ "\n- 云数据库(SQL 型)事件:`module:rdb AND eventType:(MysqlFreeze OR MysqlRecover OR MysqlSlowQuery)`(MysqlFreeze 冻结、MysqlRecover 恢复、MysqlSlowQuery 慢查询)" +
242306
+ "\n\n**其它服务**:" +
242307
+ "\n- 审批流:`module:workflow`" +
242308
+ "\n- 模型:`module:model`" +
242309
+ "\n- 用户权限:`module:auth`" +
242310
+ "\n- 大模型:`module:llm AND logType:llm-tracelog`" +
242311
+ "\n- 网关服务调用:`logType:accesslog`" +
242312
+ "\n- 应用发布/删除事件:`module:app AND eventType:(AppProdPub OR AppProdDel)`(AppProdPub 发布事件、AppProdDel 删除事件)" +
242313
+ "\n\n以上仅为示例,实际使用时请根据具体日志内容调整。" +
242314
+ "\n\n**注意**:查询特定云函数的执行日志时,优先使用 `queryFunctions(action=\"listFunctionLogs\", functionName=\"xxx\")`。"),
242315
+ service: zod_1.z
242316
+ .enum(["tcb", "tcbr"])
242317
+ .optional()
242318
+ .describe("日志来源服务:" +
242319
+ "\n- `tcb`: 云函数、数据库、存储等基础服务日志" +
242320
+ "\n- `tcbr`: CloudRun 容器服务日志"),
242321
+ startTime: zod_1.z
242322
+ .string()
242323
+ .optional()
242324
+ .describe("查询开始时间,格式:`YYYY-MM-DD HH:mm:ss`,如 `2024-01-01 00:00:00`"),
242325
+ endTime: zod_1.z
242326
+ .string()
242327
+ .optional()
242328
+ .describe("查询结束时间,格式:`YYYY-MM-DD HH:mm:ss`,如 `2024-01-01 23:59:59`"),
242329
+ limit: zod_1.z.number().optional().describe("返回日志条数限制,默认 20"),
242330
+ context: zod_1.z.string().optional().describe("翻页上下文,用于继续上一次查询"),
242331
+ sort: zod_1.z.enum(["asc", "desc"]).optional().describe("按时间排序:`asc` 升序,`desc` 降序"),
240580
242332
  },
240581
242333
  annotations: {
240582
242334
  readOnlyHint: true,
@@ -240594,7 +242346,14 @@ function registerLogTools(server) {
240594
242346
  }, enabled ? "日志服务已开通" : "日志服务未开通或仍在初始化中"));
240595
242347
  }
240596
242348
  if (!queryString) {
240597
- throw new Error("action=searchLogs 时必须提供 queryString");
242349
+ throw new Error("action=\"searchLogs\" 时必须提供 queryString 参数(CLS 查询语句,需遵循 CLS 语法规范,参考 https://cloud.tencent.com/document/api/876/128127)。" +
242350
+ "\n\n常用查询示例:" +
242351
+ "\n- 云函数日志:`(src:app OR src:system) AND log:\"START RequestId\"`" +
242352
+ "\n- 文档型数据库:`module:database`" +
242353
+ "\n- SQL 型数据库:`module:rdb`" +
242354
+ "\n- 网关访问日志:`logType:accesslog`" +
242355
+ "\n- 大模型 trace 日志:`module:llm AND logType:llm-tracelog`" +
242356
+ "\n\n如果需要查询特定云函数的执行日志,建议使用 `queryFunctions(action=\"listFunctionLogs\", functionName=\"xxx\")`。");
240598
242357
  }
240599
242358
  const result = await cloudbase.log.searchClsLog({
240600
242359
  queryString,
@@ -246474,17 +248233,55 @@ exports["default"] = parser;
246474
248233
  /***/ }),
246475
248234
 
246476
248235
  /***/ 94848:
246477
- /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
248236
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
246478
248237
 
246479
248238
  "use strict";
246480
248239
 
248240
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
248241
+ if (k2 === undefined) k2 = k;
248242
+ var desc = Object.getOwnPropertyDescriptor(m, k);
248243
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
248244
+ desc = { enumerable: true, get: function() { return m[k]; } };
248245
+ }
248246
+ Object.defineProperty(o, k2, desc);
248247
+ }) : (function(o, m, k, k2) {
248248
+ if (k2 === undefined) k2 = k;
248249
+ o[k2] = m[k];
248250
+ }));
248251
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
248252
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
248253
+ }) : function(o, v) {
248254
+ o["default"] = v;
248255
+ });
248256
+ var __importStar = (this && this.__importStar) || (function () {
248257
+ var ownKeys = function(o) {
248258
+ ownKeys = Object.getOwnPropertyNames || function (o) {
248259
+ var ar = [];
248260
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
248261
+ return ar;
248262
+ };
248263
+ return ownKeys(o);
248264
+ };
248265
+ return function (mod) {
248266
+ if (mod && mod.__esModule) return mod;
248267
+ var result = {};
248268
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
248269
+ __setModuleDefault(result, mod);
248270
+ return result;
248271
+ };
248272
+ })();
246481
248273
  Object.defineProperty(exports, "__esModule", ({ value: true }));
246482
248274
  exports.registerStorageTools = registerStorageTools;
248275
+ const fs = __importStar(__webpack_require__(79748));
248276
+ const os = __importStar(__webpack_require__(21820));
248277
+ const path = __importStar(__webpack_require__(39902));
246483
248278
  const zod_1 = __webpack_require__(21614);
246484
248279
  const cloudbase_manager_js_1 = __webpack_require__(3431);
248280
+ const MAX_INLINE_TEXT_BYTES = 256 * 1024;
248281
+ const STORAGE_READ_TEMP_PREFIX = 'cloudbase-mcp-storage-read-';
246485
248282
  // Input schema for queryStorage tool
246486
248283
  const queryStorageInputSchema = {
246487
- action: zod_1.z.enum(['list', 'info', 'url']).describe('查询操作类型:list=列出目录下的所有文件,info=获取指定文件的详细信息,url=获取文件的临时下载链接'),
248284
+ action: zod_1.z.enum(['list', 'info', 'url', 'read']).describe('查询操作类型:list=列出目录下的所有文件,info=获取指定文件的详细信息,url=获取文件的临时下载链接,read=读取文本文件内容'),
246488
248285
  cloudPath: zod_1.z.string().describe('云端文件路径,例如 files/data.txt 或 files/(目录)'),
246489
248286
  maxAge: zod_1.z.number().min(1).max(86400).optional().default(3600).describe('临时链接有效期,单位为秒,取值范围:1-86400,默认值:3600(1小时)')
246490
248287
  };
@@ -246496,6 +248293,63 @@ const manageStorageInputSchema = {
246496
248293
  force: zod_1.z.boolean().optional().default(false).describe('强制操作开关,删除操作时建议设置为true以确认删除,默认false'),
246497
248294
  isDirectory: zod_1.z.boolean().optional().default(false).describe('是否为目录操作,true=目录操作,false=文件操作,默认false')
246498
248295
  };
248296
+ function getStorageTempFileName(cloudPath) {
248297
+ const baseName = path.posix.basename(cloudPath);
248298
+ return baseName || 'storage-file';
248299
+ }
248300
+ function decodeInlineTextContent(buffer) {
248301
+ const inlineBuffer = buffer.subarray(0, MAX_INLINE_TEXT_BYTES);
248302
+ if (inlineBuffer.includes(0)) {
248303
+ throw new Error('queryStorage action=read 仅支持读取文本文件内容;二进制文件请改用 action=url 获取下载链接,或使用 manageStorage(action="download") 下载到本地。');
248304
+ }
248305
+ return {
248306
+ content: inlineBuffer.toString('utf8'),
248307
+ truncated: buffer.length > MAX_INLINE_TEXT_BYTES,
248308
+ sizeBytes: buffer.length,
248309
+ encoding: 'utf8',
248310
+ };
248311
+ }
248312
+ function getNonEmptyString(value) {
248313
+ if (typeof value !== 'string') {
248314
+ return null;
248315
+ }
248316
+ const trimmed = value.trim();
248317
+ return trimmed.length > 0 ? trimmed : null;
248318
+ }
248319
+ function buildStoragePublicUrl(cdnDomain, cloudPath) {
248320
+ if (!cdnDomain) {
248321
+ return null;
248322
+ }
248323
+ const normalizedCloudPath = cloudPath.replace(/^\/+/, '');
248324
+ if (!normalizedCloudPath) {
248325
+ return `https://${cdnDomain}`;
248326
+ }
248327
+ return `https://${cdnDomain}/${normalizedCloudPath}`;
248328
+ }
248329
+ async function resolveStoragePublicAccess(params) {
248330
+ const { cloudPath, cloudBaseOptions, manager } = params;
248331
+ try {
248332
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
248333
+ const describeEnvsResult = await manager.commonService('tcb', '2018-06-08').call({
248334
+ Action: 'DescribeEnvs',
248335
+ Param: {
248336
+ EnvId: envId,
248337
+ },
248338
+ });
248339
+ const storageList = describeEnvsResult?.EnvList?.[0]?.Storages ?? describeEnvsResult?.EnvInfo?.Storages ?? [];
248340
+ const storageCdnDomain = getNonEmptyString(storageList[0]?.CdnDomain);
248341
+ return {
248342
+ storageCdnDomain,
248343
+ publicUrl: buildStoragePublicUrl(storageCdnDomain, cloudPath),
248344
+ };
248345
+ }
248346
+ catch {
248347
+ return {
248348
+ storageCdnDomain: null,
248349
+ publicUrl: null,
248350
+ };
248351
+ }
248352
+ }
246499
248353
  function registerStorageTools(server) {
246500
248354
  // 获取 cloudBaseOptions,如果没有则为 undefined
246501
248355
  const cloudBaseOptions = server.cloudBaseOptions;
@@ -246504,7 +248358,7 @@ function registerStorageTools(server) {
246504
248358
  // Tool 1: queryStorage - 查询存储信息(只读操作)
246505
248359
  server.registerTool("queryStorage", {
246506
248360
  title: "查询存储信息",
246507
- description: "查询云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。",
248361
+ description: "查询云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。注意:action=url 返回的 temporaryUrl 是临时签名链接,有效期由 maxAge 参数决定(默认1小时),不要当作永久公网地址使用。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导出 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。",
246508
248362
  inputSchema: queryStorageInputSchema,
246509
248363
  annotations: {
246510
248364
  readOnlyHint: true,
@@ -246563,6 +248417,11 @@ function registerStorageTools(server) {
246563
248417
  cloudPath: input.cloudPath,
246564
248418
  maxAge: input.maxAge || 3600
246565
248419
  }]);
248420
+ const publicAccess = await resolveStoragePublicAccess({
248421
+ cloudPath: input.cloudPath,
248422
+ cloudBaseOptions,
248423
+ manager,
248424
+ });
246566
248425
  return {
246567
248426
  content: [
246568
248427
  {
@@ -246574,7 +248433,10 @@ function registerStorageTools(server) {
246574
248433
  cloudPath: input.cloudPath,
246575
248434
  temporaryUrl: result[0]?.url || "",
246576
248435
  expireTime: `${input.maxAge || 3600}秒`,
246577
- fileId: result[0]?.fileId || ""
248436
+ fileId: result[0]?.fileId || "",
248437
+ storageCdnDomain: publicAccess.storageCdnDomain,
248438
+ publicUrl: publicAccess.publicUrl,
248439
+ note: "temporaryUrl 是临时签名链接,会按 expireTime 过期。publicUrl 基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导,⚠️ 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先将目标路径设置为公有读。"
246578
248440
  },
246579
248441
  message: `Successfully generated temporary URL for '${input.cloudPath}'`
246580
248442
  }, null, 2)
@@ -246582,6 +248444,42 @@ function registerStorageTools(server) {
246582
248444
  ]
246583
248445
  };
246584
248446
  }
248447
+ case 'read': {
248448
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), STORAGE_READ_TEMP_PREFIX));
248449
+ const localPath = path.join(tempDir, getStorageTempFileName(input.cloudPath));
248450
+ try {
248451
+ await storageService.downloadFile({
248452
+ cloudPath: input.cloudPath,
248453
+ localPath
248454
+ });
248455
+ const buffer = await fs.readFile(localPath);
248456
+ const decoded = decodeInlineTextContent(buffer);
248457
+ return {
248458
+ content: [
248459
+ {
248460
+ type: "text",
248461
+ text: JSON.stringify({
248462
+ success: true,
248463
+ data: {
248464
+ action: 'read',
248465
+ cloudPath: input.cloudPath,
248466
+ content: decoded.content,
248467
+ encoding: decoded.encoding,
248468
+ sizeBytes: decoded.sizeBytes,
248469
+ truncated: decoded.truncated
248470
+ },
248471
+ message: decoded.truncated
248472
+ ? `Successfully read text content for '${input.cloudPath}' (truncated to ${MAX_INLINE_TEXT_BYTES} bytes)`
248473
+ : `Successfully read text content for '${input.cloudPath}'`
248474
+ }, null, 2)
248475
+ }
248476
+ ]
248477
+ };
248478
+ }
248479
+ finally {
248480
+ await fs.rm(tempDir, { recursive: true, force: true });
248481
+ }
248482
+ }
246585
248483
  default:
246586
248484
  throw new Error(`Unsupported action: ${input.action}`);
246587
248485
  }
@@ -246589,7 +248487,7 @@ function registerStorageTools(server) {
246589
248487
  // Tool 2: manageStorage - 管理存储文件(写操作)
246590
248488
  server.registerTool("manageStorage", {
246591
248489
  title: "管理存储文件",
246592
- description: "管理云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。",
248490
+ description: "管理云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。注意:上传后返回的 temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。",
246593
248491
  inputSchema: manageStorageInputSchema,
246594
248492
  annotations: {
246595
248493
  readOnlyHint: false,
@@ -246629,6 +248527,11 @@ function registerStorageTools(server) {
246629
248527
  cloudPath: input.cloudPath,
246630
248528
  maxAge: 3600
246631
248529
  }]);
248530
+ const publicAccess = await resolveStoragePublicAccess({
248531
+ cloudPath: input.cloudPath,
248532
+ cloudBaseOptions,
248533
+ manager,
248534
+ });
246632
248535
  return {
246633
248536
  content: [
246634
248537
  {
@@ -246641,7 +248544,10 @@ function registerStorageTools(server) {
246641
248544
  cloudPath: input.cloudPath,
246642
248545
  isDirectory: input.isDirectory,
246643
248546
  temporaryUrl: fileUrls[0]?.url || "",
246644
- expireTime: "1小时"
248547
+ expireTime: "1小时",
248548
+ storageCdnDomain: publicAccess.storageCdnDomain,
248549
+ publicUrl: publicAccess.publicUrl,
248550
+ note: "temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。publicUrl 基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导,⚠️ 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先将目标路径设置为公有读。"
246645
248551
  },
246646
248552
  message: `Successfully uploaded ${input.isDirectory ? 'directory' : 'file'} from '${input.localPath}' to '${input.cloudPath}'`
246647
248553
  }, null, 2)
@@ -255081,6 +256987,12 @@ class CloudBase {
255081
256987
  get permission() {
255082
256988
  return this.currentEnvironment().getPermissionService();
255083
256989
  }
256990
+ get sandbox() {
256991
+ return this.currentEnvironment().getSandboxService();
256992
+ }
256993
+ get aiModel() {
256994
+ return this.currentEnvironment().getAiModelService();
256995
+ }
255084
256996
  get docs() {
255085
256997
  if (!this.docsService) {
255086
256998
  this.docsService = new docs_1.DocsService();