@cloudbase/cloudbase-mcp 2.17.1 → 2.19.4

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,16 +1892,76 @@ 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 "";
1904
1963
  }
1964
+ const verificationUriComplete = (0, auth_js_1.buildVerificationUriComplete)(deviceAuthInfo);
1905
1965
  const lines = [
1906
1966
  "",
1907
1967
  "### Device Flow 授权信息",
@@ -1910,8 +1970,11 @@ function formatDeviceAuthHint(deviceAuthInfo) {
1910
1970
  if (deviceAuthInfo.verification_uri) {
1911
1971
  lines.push(`- verification_uri: ${deviceAuthInfo.verification_uri}`);
1912
1972
  }
1973
+ if (verificationUriComplete) {
1974
+ lines.push(`- verification_uri_complete: ${verificationUriComplete}`);
1975
+ }
1913
1976
  lines.push(`- expires_in: ${deviceAuthInfo.expires_in}s`);
1914
- lines.push("", "请在另一台可用浏览器设备打开 `verification_uri` 并输入 `user_code` 完成授权。");
1977
+ lines.push("", "请优先向用户展示完整的 `verification_uri_complete`,不要截断或改写 URL。");
1915
1978
  return lines.join("\n");
1916
1979
  }
1917
1980
  function emitDeviceAuthNotice(server, deviceAuthInfo) {
@@ -2230,6 +2293,49 @@ function normalizeOptionalToolString(value) {
2230
2293
  ? value.trim()
2231
2294
  : undefined;
2232
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
+ }
2233
2339
  function normalizeOptionalToolBoolean(value) {
2234
2340
  return typeof value === "boolean" ? value : undefined;
2235
2341
  }
@@ -2337,13 +2443,7 @@ function registerEnvTools(server) {
2337
2443
  (0, auth_js_1.setPendingAuthProgressState)(info, "device");
2338
2444
  // emitDeviceAuthNotice(server, info);
2339
2445
  };
2340
- const authChallenge = () => deviceAuthInfo
2341
- ? {
2342
- user_code: deviceAuthInfo.user_code,
2343
- verification_uri: deviceAuthInfo.verification_uri,
2344
- expires_in: deviceAuthInfo.expires_in,
2345
- }
2346
- : undefined;
2446
+ const authChallenge = () => (0, auth_js_1.buildDeviceAuthChallengePayload)(deviceAuthInfo);
2347
2447
  try {
2348
2448
  if (!supportedAuthActions.includes(action)) {
2349
2449
  return (0, tool_result_js_1.buildJsonToolResult)({
@@ -2390,12 +2490,8 @@ function registerEnvTools(server) {
2390
2490
  current_env_id: null,
2391
2491
  ...buildEnvCandidatePayload([]),
2392
2492
  }),
2393
- auth_challenge: authFlowState.status === "PENDING" && authFlowState.authChallenge
2394
- ? {
2395
- user_code: authFlowState.authChallenge.user_code,
2396
- verification_uri: authFlowState.authChallenge.verification_uri,
2397
- expires_in: authFlowState.authChallenge.expires_in,
2398
- }
2493
+ auth_challenge: authFlowState.status === "PENDING"
2494
+ ? (0, auth_js_1.buildDeviceAuthChallengePayload)(authFlowState.authChallenge)
2399
2495
  : undefined,
2400
2496
  message,
2401
2497
  next_step: authStatus === "REQUIRED"
@@ -2416,11 +2512,7 @@ function registerEnvTools(server) {
2416
2512
  ok: true,
2417
2513
  code: "AUTH_PENDING",
2418
2514
  message: "设备码授权进行中,请在浏览器中打开 verification_uri 并输入 user_code 完成授权。",
2419
- auth_challenge: {
2420
- user_code: authFlowState.authChallenge.user_code,
2421
- verification_uri: authFlowState.authChallenge.verification_uri,
2422
- expires_in: authFlowState.authChallenge.expires_in,
2423
- },
2515
+ auth_challenge: (0, auth_js_1.buildDeviceAuthChallengePayload)(authFlowState.authChallenge),
2424
2516
  next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
2425
2517
  suggestedArgs: { action: "status" },
2426
2518
  }),
@@ -2708,17 +2800,17 @@ function registerEnvTools(server) {
2708
2800
  });
2709
2801
  }
2710
2802
  });
2711
- // envQuery - 环境查询(合并 listEnvs + getEnvInfo + getEnvAuthDomains + getWebsiteConfig)
2803
+ // envQuery - 环境查询(合并 listEnvs + getEnvInfo + getEnvAuthDomains)
2712
2804
  server.registerTool?.("envQuery", {
2713
2805
  title: "环境查询",
2714
- 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 等计费字段)。",
2715
2807
  inputSchema: {
2716
2808
  action: zod_1.z
2717
- .enum(["list", "info", "domains", "hosting"])
2718
- .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=安全域名列表"),
2719
2811
  alias: zod_1.z.string().optional().describe("按环境别名筛选。action=list 时可选"),
2720
2812
  aliasExact: zod_1.z.boolean().optional().describe("按环境别名精确筛选。action=list 时可选;与 alias 配合使用"),
2721
- 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"),
2722
2814
  limit: zod_1.z.number().int().positive().optional().describe("返回数量上限。action=list 时可选"),
2723
2815
  offset: zod_1.z.number().int().min(0).optional().describe("分页偏移。action=list 时可选"),
2724
2816
  fields: zod_1.z
@@ -2785,14 +2877,12 @@ function registerEnvTools(server) {
2785
2877
  return toolPayloadResult;
2786
2878
  }
2787
2879
  (0, logger_js_1.debug)("降级到 listEnvs() 也失败:", fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)));
2880
+ const enhancedMessage = buildEnvQueryErrorMessage(fallbackError, "list");
2788
2881
  return {
2789
2882
  content: [
2790
2883
  {
2791
2884
  type: "text",
2792
- text: "获取环境列表时出错: " +
2793
- (fallbackError instanceof Error
2794
- ? fallbackError.message
2795
- : String(fallbackError)),
2885
+ text: enhancedMessage,
2796
2886
  },
2797
2887
  ],
2798
2888
  };
@@ -2857,29 +2947,6 @@ function registerEnvTools(server) {
2857
2947
  };
2858
2948
  }
2859
2949
  break;
2860
- case "hosting": {
2861
- const cloudbaseHosting = await getManager();
2862
- const websiteConfig = await cloudbaseHosting.hosting.getWebsiteConfig();
2863
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, websiteConfig);
2864
- const hostingResult = {
2865
- ...websiteConfig,
2866
- CdnDomain: websiteConfig.CdnDomain ?? null,
2867
- Bucket: websiteConfig.Bucket ?? null,
2868
- };
2869
- try {
2870
- const envInfo = await cloudbaseHosting.env.getEnvInfo();
2871
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, envInfo);
2872
- hostingResult.CdnDomain = envInfo.EnvInfo?.StaticStorages?.[0]?.StaticDomain ?? hostingResult.CdnDomain;
2873
- hostingResult.Bucket = envInfo.EnvInfo?.StaticStorages?.[0]?.Bucket ?? hostingResult.Bucket;
2874
- }
2875
- catch (hostingInfoError) {
2876
- (0, logger_js_1.debug)("Failed to enrich hosting envQuery result with env info", {
2877
- error: hostingInfoError,
2878
- });
2879
- }
2880
- result = hostingResult;
2881
- break;
2882
- }
2883
2950
  default:
2884
2951
  throw new Error(`不支持的查询类型: ${action}`);
2885
2952
  }
@@ -2898,11 +2965,12 @@ function registerEnvTools(server) {
2898
2965
  if (toolPayloadResult) {
2899
2966
  return toolPayloadResult;
2900
2967
  }
2968
+ const enhancedMessage = buildEnvQueryErrorMessage(error, action);
2901
2969
  return {
2902
2970
  content: [
2903
2971
  {
2904
2972
  type: "text",
2905
- text: `环境查询失败: ${error instanceof Error ? error.message : String(error)}`,
2973
+ text: enhancedMessage,
2906
2974
  },
2907
2975
  ],
2908
2976
  };
@@ -2910,13 +2978,13 @@ function registerEnvTools(server) {
2910
2978
  });
2911
2979
  // envDomainManagement - 环境域名管理(合并 createEnvDomain + deleteEnvDomain)
2912
2980
  server.registerTool?.("envDomainManagement", {
2913
- title: "环境域名管理",
2914
- 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\")。",
2915
2983
  inputSchema: {
2916
2984
  action: zod_1.z
2917
2985
  .enum(["create", "delete"])
2918
- .describe("操作类型:create=添加域名,delete=删除域名"),
2919
- 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)。注意:不是自定义域名,不需要证书。"),
2920
2988
  },
2921
2989
  annotations: {
2922
2990
  readOnlyHint: false,
@@ -2941,14 +3009,11 @@ function registerEnvTools(server) {
2941
3009
  default:
2942
3010
  throw new Error(`不支持的操作类型: ${action}`);
2943
3011
  }
2944
- return {
2945
- content: [
2946
- {
2947
- type: "text",
2948
- text: `${JSON.stringify(result, null, 2)}\n\n请注意安全域名需要10分钟才能生效,用户也应该了解这一点。`,
2949
- },
2950
- ],
2951
- };
3012
+ return (0, tool_result_js_1.buildJsonToolResult)(buildEnvDomainManagementResult({
3013
+ action,
3014
+ domains,
3015
+ result,
3016
+ }));
2952
3017
  }
2953
3018
  catch (error) {
2954
3019
  const toolPayloadResult = (0, tool_result_js_1.toolPayloadErrorToResult)(error);
@@ -24602,13 +24667,7 @@ async function throwPendingAuthError() {
24602
24667
  ok: false,
24603
24668
  code: "AUTH_PENDING",
24604
24669
  message: authState.lastError || "设备码授权进行中,请先完成登录后再重试当前工具。",
24605
- auth_challenge: authState.authChallenge
24606
- ? {
24607
- user_code: authState.authChallenge.user_code,
24608
- verification_uri: authState.authChallenge.verification_uri,
24609
- expires_in: authState.authChallenge.expires_in,
24610
- }
24611
- : undefined,
24670
+ auth_challenge: (0, auth_js_1.buildDeviceAuthChallengePayload)(authState.authChallenge),
24612
24671
  next_step: (0, tool_result_js_1.buildAuthNextStep)("status", {
24613
24672
  suggestedArgs: {
24614
24673
  action: "status",
@@ -24921,20 +24980,9 @@ async function getCloudBaseManager(options = {}) {
24921
24980
  }
24922
24981
  // envId priority: explicit option > envManager cache > loginState.envId
24923
24982
  const resolvedEnvId = finalEnvId || loginEnvId;
24924
- let region = fallbackRegion;
24925
- if (resolvedEnvId && !cloudBaseOptions?.region) {
24926
- try {
24927
- const envCandidates = await listAvailableEnvCandidates({ loginState });
24928
- const matchedEnv = envCandidates.find((candidate) => candidate.envId === resolvedEnvId);
24929
- if (matchedEnv?.region) {
24930
- region = matchedEnv.region;
24931
- (0, logger_js_1.debug)('使用环境实际 region:', { envId: resolvedEnvId, region });
24932
- }
24933
- }
24934
- catch {
24935
- (0, logger_js_1.debug)('无法获取环境列表,使用 fallback region');
24936
- }
24937
- }
24983
+ // region 直接使用 fallbackRegion(来自 cloudBaseOptions?.region ?? TCB_REGION ?? 'ap-shanghai')
24984
+ // 不再通过 DescribeEnvs 查询 region,以兼容 STS 临时密钥场景
24985
+ const region = fallbackRegion;
24938
24986
  const manager = new manager_node_1.default({
24939
24987
  secretId,
24940
24988
  secretKey,
@@ -28401,7 +28449,7 @@ function registerAppAuthTools(server) {
28401
28449
  };
28402
28450
  server.registerTool?.("queryAppAuth", {
28403
28451
  title: "查询应用认证配置",
28404
- 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。",
28405
28453
  inputSchema: {
28406
28454
  action: zod_1.z.enum(QUERY_APP_AUTH_ACTIONS),
28407
28455
  providerId: zod_1.z.string().optional().describe("provider 标识,如 email、google"),
@@ -28512,7 +28560,7 @@ function registerAppAuthTools(server) {
28512
28560
  }));
28513
28561
  server.registerTool?.("manageAppAuth", {
28514
28562
  title: "管理应用认证配置",
28515
- 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 },再实现对应前端登录逻辑。",
28516
28564
  inputSchema: {
28517
28565
  action: zod_1.z.enum(MANAGE_APP_AUTH_ACTIONS),
28518
28566
  patch: zod_1.z
@@ -37098,15 +37146,14 @@ const fs = __importStar(__webpack_require__(29021));
37098
37146
  const fsPromises = __importStar(__webpack_require__(79748));
37099
37147
  const http = __importStar(__webpack_require__(81630));
37100
37148
  const https = __importStar(__webpack_require__(55591));
37101
- const net = __importStar(__webpack_require__(91645));
37102
37149
  const os = __importStar(__webpack_require__(21820));
37103
37150
  const path = __importStar(__webpack_require__(39902));
37104
37151
  const url_1 = __webpack_require__(79551);
37105
37152
  const zod_1 = __webpack_require__(21614);
37106
- const dns = __importStar(__webpack_require__(14985));
37153
+ const remote_url_safety_js_1 = __webpack_require__(89192);
37107
37154
  // 常量定义
37108
37155
  const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
37109
- const ALLOWED_PROTOCOLS = ["http:", "https:"];
37156
+ const MAX_REDIRECT_HOPS = 5;
37110
37157
  const ALLOWED_CONTENT_TYPES = [
37111
37158
  "text/",
37112
37159
  "image/",
@@ -37155,65 +37202,6 @@ function calculateDownloadPath(relativePath) {
37155
37202
  }
37156
37203
  return finalPath;
37157
37204
  }
37158
- // 检查是否为内网 IP
37159
- function isPrivateIP(ip) {
37160
- // 如果不是有效的 IP 地址,返回 true(保守处理)
37161
- if (!net.isIP(ip)) {
37162
- return true;
37163
- }
37164
- // 检查特殊地址
37165
- if (ip === '127.0.0.1' ||
37166
- ip === 'localhost' ||
37167
- ip === '::1' || // IPv6 本地回环
37168
- ip.startsWith('169.254.') || // 链路本地地址
37169
- ip.startsWith('0.')) { // 特殊用途地址
37170
- return true;
37171
- }
37172
- // 转换 IP 地址为长整数进行范围检查
37173
- const ipv4Parts = ip.split('.').map(part => parseInt(part, 10));
37174
- if (ipv4Parts.length === 4) {
37175
- const ipNum = ((ipv4Parts[0] << 24) >>> 0) + (ipv4Parts[1] << 16) + (ipv4Parts[2] << 8) + ipv4Parts[3];
37176
- // 检查私有 IP 范围
37177
- // 10.0.0.0 - 10.255.255.255
37178
- if (ipNum >= 167772160 && ipNum <= 184549375)
37179
- return true;
37180
- // 172.16.0.0 - 172.31.255.255
37181
- if (ipNum >= 2886729728 && ipNum <= 2887778303)
37182
- return true;
37183
- // 192.168.0.0 - 192.168.255.255
37184
- if (ipNum >= 3232235520 && ipNum <= 3232301055)
37185
- return true;
37186
- }
37187
- // 检查 IPv6 私有地址
37188
- if (net.isIPv6(ip)) {
37189
- const normalizedIP = ip.toLowerCase();
37190
- if (normalizedIP.startsWith('fc00:') || // 唯一本地地址
37191
- normalizedIP.startsWith('fe80:') || // 链路本地地址
37192
- normalizedIP.startsWith('fec0:') || // 站点本地地址
37193
- normalizedIP.startsWith('::1')) { // 本地回环
37194
- return true;
37195
- }
37196
- }
37197
- return false;
37198
- }
37199
- // 检查域名是否解析到内网 IP
37200
- async function doesDomainResolveToPrivateIP(hostname) {
37201
- try {
37202
- const addresses = await new Promise((resolve, reject) => {
37203
- dns.resolve(hostname, (err, addresses) => {
37204
- if (err)
37205
- reject(err);
37206
- else
37207
- resolve(addresses);
37208
- });
37209
- });
37210
- return addresses.some(ip => isPrivateIP(ip));
37211
- }
37212
- catch (error) {
37213
- // 如果解析失败,为安全起见返回 true
37214
- return true;
37215
- }
37216
- }
37217
37205
  // 生成随机文件名
37218
37206
  function generateRandomFileName(extension = '') {
37219
37207
  const randomBytes = crypto.randomBytes(16);
@@ -37264,45 +37252,52 @@ function getFileExtension(url, contentType, contentDisposition) {
37264
37252
  }
37265
37253
  return extension;
37266
37254
  }
37267
- // 验证 URL 和内容类型是否安全
37268
- async function isUrlAndContentTypeSafe(url, contentType) {
37269
- try {
37270
- const parsedUrl = new url_1.URL(url);
37271
- // 检查协议
37272
- if (!ALLOWED_PROTOCOLS.includes(parsedUrl.protocol)) {
37273
- return false;
37274
- }
37275
- // 检查主机名是否为 IP 地址
37276
- const hostname = parsedUrl.hostname;
37277
- if (net.isIP(hostname) && isPrivateIP(hostname)) {
37278
- return false;
37279
- }
37280
- // 如果是域名,检查它是否解析到内网 IP
37281
- if (!net.isIP(hostname) && await doesDomainResolveToPrivateIP(hostname)) {
37282
- return false;
37283
- }
37284
- // 检查内容类型
37285
- return ALLOWED_CONTENT_TYPES.some(allowedType => contentType.startsWith(allowedType));
37286
- }
37287
- catch {
37288
- return false;
37289
- }
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 || '');
37290
37271
  }
37291
- // 下载文件到指定路径
37292
- 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;
37293
37281
  return new Promise((resolve, reject) => {
37294
- const client = url.startsWith('https:') ? https : http;
37295
- 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
+ }
37296
37293
  if (res.statusCode !== 200) {
37297
37294
  reject(new Error(`HTTP Error: ${res.statusCode}`));
37298
37295
  return;
37299
37296
  }
37300
- const contentType = res.headers['content-type'] || '';
37301
- const contentLength = parseInt(res.headers['content-length'] || '0', 10);
37302
- const contentDisposition = res.headers['content-disposition'];
37303
- // 安全检查
37304
- if (!await isUrlAndContentTypeSafe(url, contentType)) {
37305
- 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"}`));
37306
37301
  return;
37307
37302
  }
37308
37303
  // 文件大小检查
@@ -37316,13 +37311,13 @@ function downloadFileToPath(url, targetPath) {
37316
37311
  await fsPromises.mkdir(targetDir, { recursive: true });
37317
37312
  }
37318
37313
  catch (error) {
37319
- reject(new Error(`无法创建目标目录: ${error instanceof Error ? error.message : '未知错误'}`));
37314
+ reject(new Error(`无法创建目标目录: ${error instanceof Error ? error.message : "未知错误"}`));
37320
37315
  return;
37321
37316
  }
37322
37317
  // 创建写入流
37323
37318
  const fileStream = fs.createWriteStream(targetPath);
37324
37319
  let downloadedSize = 0;
37325
- res.on('data', (chunk) => {
37320
+ res.on("data", (chunk) => {
37326
37321
  downloadedSize += chunk.length;
37327
37322
  if (downloadedSize > MAX_FILE_SIZE) {
37328
37323
  fileStream.destroy();
@@ -37331,37 +37326,65 @@ function downloadFileToPath(url, targetPath) {
37331
37326
  }
37332
37327
  });
37333
37328
  res.pipe(fileStream);
37334
- fileStream.on('finish', () => {
37329
+ fileStream.on("finish", () => {
37335
37330
  resolve({
37336
37331
  filePath: targetPath,
37337
37332
  contentType,
37338
- fileSize: downloadedSize
37333
+ fileSize: downloadedSize,
37339
37334
  });
37340
37335
  });
37341
- fileStream.on('error', (error) => {
37336
+ fileStream.on("error", (error) => {
37342
37337
  fsPromises.unlink(targetPath).catch(() => { });
37343
37338
  reject(error);
37344
37339
  });
37345
- }).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
+ }
37346
37354
  reject(error);
37347
37355
  });
37348
37356
  });
37349
37357
  }
37350
- // 下载文件到临时目录(保持向后兼容)
37351
- 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;
37352
37367
  return new Promise((resolve, reject) => {
37353
- const client = url.startsWith('https:') ? https : http;
37354
- 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
+ }
37355
37379
  if (res.statusCode !== 200) {
37356
37380
  reject(new Error(`HTTP Error: ${res.statusCode}`));
37357
37381
  return;
37358
37382
  }
37359
- const contentType = res.headers['content-type'] || '';
37360
- const contentLength = parseInt(res.headers['content-length'] || '0', 10);
37361
- const contentDisposition = res.headers['content-disposition'];
37362
- // 安全检查
37363
- if (!await isUrlAndContentTypeSafe(url, contentType)) {
37364
- 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"}`));
37365
37388
  return;
37366
37389
  }
37367
37390
  // 文件大小检查
@@ -37370,13 +37393,13 @@ function downloadFile(url) {
37370
37393
  return;
37371
37394
  }
37372
37395
  // 生成临时文件路径
37373
- const extension = getFileExtension(url, contentType, contentDisposition);
37396
+ const extension = getFileExtension(requestUrl.toString(), contentType, contentDisposition);
37374
37397
  const fileName = generateRandomFileName(extension);
37375
37398
  const filePath = getSafeTempFilePath(fileName);
37376
37399
  // 创建写入流
37377
37400
  const fileStream = fs.createWriteStream(filePath);
37378
37401
  let downloadedSize = 0;
37379
- res.on('data', (chunk) => {
37402
+ res.on("data", (chunk) => {
37380
37403
  downloadedSize += chunk.length;
37381
37404
  if (downloadedSize > MAX_FILE_SIZE) {
37382
37405
  fileStream.destroy();
@@ -37385,18 +37408,31 @@ function downloadFile(url) {
37385
37408
  }
37386
37409
  });
37387
37410
  res.pipe(fileStream);
37388
- fileStream.on('finish', () => {
37411
+ fileStream.on("finish", () => {
37389
37412
  resolve({
37390
37413
  filePath,
37391
37414
  contentType,
37392
- fileSize: downloadedSize
37415
+ fileSize: downloadedSize,
37393
37416
  });
37394
37417
  });
37395
- fileStream.on('error', (error) => {
37418
+ fileStream.on("error", (error) => {
37396
37419
  fsPromises.unlink(filePath).catch(() => { });
37397
37420
  reject(error);
37398
37421
  });
37399
- }).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
+ }
37400
37436
  reject(error);
37401
37437
  });
37402
37438
  });
@@ -37409,7 +37445,8 @@ function buildDownloadRemoteFileErrorMessage(url, error) {
37409
37445
  suggestions.push("请先在浏览器中确认该链接可访问,再重新执行下载。");
37410
37446
  }
37411
37447
  if (/ENOTFOUND|ECONNRESET|ETIMEDOUT|socket hang up/i.test(baseMessage)) {
37412
- suggestions.push("下载过程中出现网络异常,建议检查本地网络或代理后重试。");
37448
+ suggestions.push("下载过程中出现网络异常,系统已自动重试 3 次但均失败。");
37449
+ suggestions.push("建议:1) 检查本地网络或代理设置;2) 该网站可能限频,请尝试更换其他图片源(如 Unsplash、Pexels);3) 稍后重试。");
37413
37450
  }
37414
37451
  if (suggestions.length === 0) {
37415
37452
  suggestions.push("请检查 URL 是否可访问,以及目标资源是否允许直接下载。");
@@ -88437,6 +88474,7 @@ exports.MANAGE_FUNCTION_ACTIONS = [
88437
88474
  "updateFunctionCode",
88438
88475
  "updateFunctionConfig",
88439
88476
  "invokeFunction",
88477
+ "deleteFunction",
88440
88478
  "createFunctionTrigger",
88441
88479
  "deleteFunctionTrigger",
88442
88480
  "createLayerVersion",
@@ -88580,14 +88618,32 @@ function buildFunctionOperationErrorMessage(operation, functionName, functionRoo
88580
88618
  if (/路径不存在/i.test(baseMessage) && expectedFunctionPath) {
88581
88619
  suggestions.push(`当前工具会从 \`functionRootPath + 函数名\` 查找代码目录,期望目录是 \`${expectedFunctionPath}\`。`);
88582
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
+ }
88583
88629
  }
88584
88630
  if (/paths\[0\].*undefined/i.test(baseMessage)) {
88585
- suggestions.push("HTTP 函数创建时需要提供 functionRootPath(指向 cloudfunctions 父目录)或 zipFile,否则 SDK 无法定位函数目录。");
88631
+ suggestions.push("HTTP 函数创建时需要提供 functionRootPath(指向 cloudfunctions 或 functions 目录的绝对路径,不是项目根目录)或 zipFile,否则 SDK 无法定位函数目录。");
88586
88632
  }
88587
88633
  if (/依赖安装失败|package\.json/i.test(baseMessage)) {
88588
88634
  suggestions.push("如果 HTTP 函数只使用原生 Node.js API 且没有第三方依赖,可以保留函数目录中的 index.js 和 scf_bootstrap,工具会跳过依赖安装。");
88589
88635
  suggestions.push("如果你确实依赖 npm 包,请在函数目录下补充 package.json 后重试。");
88590
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
+ }
88591
88647
  if (suggestions.length === 0) {
88592
88648
  suggestions.push("请检查函数名、目录结构和环境中的函数状态后重试。");
88593
88649
  }
@@ -88644,10 +88700,17 @@ function registerFunctionTools(server) {
88644
88700
  throw new Error("createLayerVersion 在 cloud mode 下不支持 contentPath,本地文件内容请改为 base64Content 或改用本地模式执行。");
88645
88701
  }
88646
88702
  };
88703
+ const TIME_FORMAT_REGEX = /^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$/;
88647
88704
  const validateLogRange = (startTime, endTime, offset, limit) => {
88648
88705
  if ((offset || 0) + (limit || 0) > 10000) {
88649
88706
  throw new Error("offset+limit 不能大于 10000");
88650
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
+ }
88651
88714
  if (startTime && endTime) {
88652
88715
  const start = new Date(startTime).getTime();
88653
88716
  const end = new Date(endTime).getTime();
@@ -88727,15 +88790,29 @@ function registerFunctionTools(server) {
88727
88790
  }
88728
88791
  validateLogRange(input.startTime, input.endTime, input.offset, input.limit);
88729
88792
  const cloudbase = await getManager();
88730
- const result = await cloudbase.functions.getFunctionLogsV2({
88731
- name: input.functionName,
88732
- offset: input.offset,
88733
- limit: input.limit,
88734
- startTime: input.startTime,
88735
- endTime: input.endTime,
88736
- requestId: input.requestId,
88737
- qualifier: input.qualifier,
88738
- });
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
+ }
88739
88816
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
88740
88817
  return buildEnvelope({
88741
88818
  action: input.action,
@@ -88757,11 +88834,24 @@ function registerFunctionTools(server) {
88757
88834
  }
88758
88835
  validateLogRange(input.startTime, input.endTime);
88759
88836
  const cloudbase = await getManager();
88760
- const result = await cloudbase.functions.getFunctionLogDetail({
88761
- startTime: input.startTime,
88762
- endTime: input.endTime,
88763
- logRequestId: input.requestId,
88764
- });
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
+ }
88765
88855
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
88766
88856
  return buildEnvelope({
88767
88857
  action: input.action,
@@ -88957,7 +89047,7 @@ function registerFunctionTools(server) {
88957
89047
  const functionType = typeof func.type === "string" ? func.type : undefined;
88958
89048
  const expectedFunctionPath = getExpectedFunctionPath(processedRootPath, functionName);
88959
89049
  if (functionType === "HTTP" && !processedRootPath && !input.zipFile) {
88960
- throw new Error("createFunction 创建 HTTP 函数时,需要提供 functionRootPath(指向 cloudfunctions 父目录)或 zipFile。");
89050
+ throw new Error("createFunction 创建 HTTP 函数时,需要提供 functionRootPath(指向 cloudfunctions 或 functions 目录的绝对路径,不是项目根目录)或 zipFile。");
88961
89051
  }
88962
89052
  const hasPackageJson = expectedFunctionPath !== undefined
88963
89053
  ? (0, fs_1.existsSync)(path_1.default.join(expectedFunctionPath, "package.json"))
@@ -88996,7 +89086,7 @@ function registerFunctionTools(server) {
88996
89086
  nextActions.push({
88997
89087
  tool: "manageGateway",
88998
89088
  action: "createAccess",
88999
- reason: "如果需要通过 URL 访问 HTTP 函数,请按实际路径和鉴权需求显式创建访问入口,不要默认假设 /函数名 已存在",
89089
+ reason: "如果需要通过 URL 访问 HTTP 函数,请调用 manageGateway(action=\"createAccess\") 并显式传 type=\"HTTP\",再按实际路径和鉴权需求创建访问入口,不要默认假设 /函数名 已存在",
89000
89090
  });
89001
89091
  nextActions.push({
89002
89092
  tool: "queryGateway",
@@ -89015,7 +89105,7 @@ function registerFunctionTools(server) {
89015
89105
  });
89016
89106
  }
89017
89107
  const message = func.type === "HTTP"
89018
- ? `已创建 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") 调整权限。`
89019
89109
  : `已创建函数 ${functionName}`;
89020
89110
  return buildEnvelope({
89021
89111
  action: input.action,
@@ -89130,6 +89220,26 @@ function registerFunctionTools(server) {
89130
89220
  throw error;
89131
89221
  }
89132
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
+ }
89133
89243
  case "createFunctionTrigger": {
89134
89244
  if (!input.functionName) {
89135
89245
  throw new Error("createFunctionTrigger 操作时,functionName 参数是必需的");
@@ -89342,23 +89452,58 @@ function registerFunctionTools(server) {
89342
89452
  };
89343
89453
  server.registerTool?.("queryFunctions", {
89344
89454
  title: "查询云函数域资源",
89345
- 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 日志服务(跨服务日志聚合)",
89346
89467
  inputSchema: {
89347
89468
  action: zod_1.z
89348
89469
  .enum(exports.QUERY_FUNCTION_ACTIONS)
89349
- .describe("只读操作类型,例如 listFunctions、getFunctionDetail、listFunctionLogs"),
89350
- functionName: zod_1.z.string().optional().describe("函数名称。函数相关 action 必填"),
89351
- limit: zod_1.z.number().optional().describe("分页数量。列表类 action 可选"),
89352
- offset: zod_1.z.number().optional().describe("分页偏移。列表类 action 可选"),
89353
- codeSecret: zod_1.z.string().optional().describe("代码保护密钥"),
89354
- startTime: zod_1.z.string().optional().describe("日志查询开始时间"),
89355
- endTime: zod_1.z.string().optional().describe("日志查询结束时间"),
89356
- requestId: zod_1.z.string().optional().describe("日志 requestId。获取日志详情时必填"),
89357
- qualifier: zod_1.z.string().optional().describe("函数版本,日志查询时可选"),
89358
- 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"),
89359
89504
  searchKey: zod_1.z.string().optional().describe("层名称搜索关键字"),
89360
- layerName: zod_1.z.string().optional().describe("层名称。层相关 action 必填"),
89361
- 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` 操作必填"),
89362
89507
  },
89363
89508
  annotations: {
89364
89509
  readOnlyHint: true,
@@ -89368,25 +89513,34 @@ function registerFunctionTools(server) {
89368
89513
  }, async (input) => withEnvelope(() => handleQueryFunctions(input)));
89369
89514
  server.registerTool?.("manageFunctions", {
89370
89515
  title: "管理云函数域资源",
89371
- description: "函数域统一写入口。通过 action 管理函数创建、代码更新、配置更新、调用函数、触发器和层绑定。危险操作需要显式 confirm=true。",
89516
+ description: "函数域统一写入口。支持创建函数、更新代码、更新配置、调用函数、管理定时跑 / 定时任务 / scheduled job 的 timer 触发器和层绑定。" +
89517
+ "如果要创建 cron 定时任务,先用 createFunction 创建函数,再用 createFunctionTrigger 创建 timer 触发器(支持7段cron表达式),deleteFunctionTrigger 删除触发器。" +
89518
+ "危险操作需要显式 confirm=true。",
89372
89519
  inputSchema: {
89373
89520
  action: zod_1.z
89374
89521
  .enum(exports.MANAGE_FUNCTION_ACTIONS)
89375
- .describe("写操作类型,例如 createFunction、invokeFunction、attachLayer"),
89522
+ .describe("写操作类型,例如 createFunction、updateFunctionCode、invokeFunction、deleteFunction、" +
89523
+ "createFunctionTrigger(定时任务 / cron / timer)、deleteFunctionTrigger、attachLayer、detachLayer"),
89376
89524
  func: CREATE_FUNCTION_SCHEMA.optional().describe("createFunction 操作的函数配置"),
89377
- functionRootPath: zod_1.z.string().optional().describe("函数根目录(父目录绝对路径)。" +
89378
- "本地应按 cloudfunctions/<functionName>/index.js 布局," +
89379
- "此参数传 cloudfunctions 目录的绝对路径(如 /abs/path/cloudfunctions),不要传到函数名子目录。" +
89380
- "SDK 会自动拼接函数名子目录。"),
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 目录的绝对路径。" +
89530
+ "SDK 会自动拼接函数名子目录,无需预先压缩 zip 或 base64 编码。"),
89381
89531
  force: zod_1.z.boolean().optional().describe("createFunction 时是否覆盖"),
89382
89532
  functionName: zod_1.z.string().optional().describe("函数名称。大多数 action 使用该字段作为统一目标"),
89383
- zipFile: zod_1.z.string().optional().describe("代码包的 base64 编码"),
89533
+ zipFile: zod_1.z.string().optional().describe("仅兼容特殊场景:预先准备好的代码包 base64 编码。普通 createFunction/updateFunctionCode 默认不要先压缩 zip,优先使用 functionRootPath。"),
89384
89534
  handler: zod_1.z.string().optional().describe("函数入口"),
89385
89535
  timeout: zod_1.z.number().optional().describe("配置更新时的超时时间"),
89386
89536
  envVariables: zod_1.z.record(zod_1.z.string()).optional().describe("配置更新时要合并的环境变量"),
89387
89537
  vpc: VPC_SCHEMA.optional().describe("配置更新时的 VPC 信息"),
89388
89538
  params: zod_1.z.record(zod_1.z.any()).optional().describe("invokeFunction 的调用参数"),
89389
- 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分钟执行一次'),
89390
89544
  triggerName: zod_1.z.string().optional().describe("deleteFunctionTrigger 的目标触发器名称"),
89391
89545
  layerName: zod_1.z.string().optional().describe("层名称"),
89392
89546
  layerVersion: zod_1.z.number().optional().describe("层版本号"),
@@ -89400,7 +89554,7 @@ function registerFunctionTools(server) {
89400
89554
  .optional()
89401
89555
  .describe("updateFunctionLayers 的目标层列表,顺序即最终顺序"),
89402
89556
  codeSecret: zod_1.z.string().optional().describe("层绑定时的代码保护密钥"),
89403
- confirm: zod_1.z.boolean().optional().describe("危险操作确认开关"),
89557
+ confirm: zod_1.z.boolean().optional().describe("危险操作确认开关。deleteFunction、deleteFunctionTrigger、deleteLayerVersion、detachLayer 等删除类操作需要显式传入 confirm=true"),
89404
89558
  },
89405
89559
  annotations: {
89406
89560
  readOnlyHint: false,
@@ -91645,18 +91799,33 @@ class EnvService {
91645
91799
  * @returns {Promise<IEnvInfoRes>}
91646
91800
  */
91647
91801
  async getEnvInfo() {
91648
- // NOTE: DescribeEnv 接口废弃,需要使用 DescribeEnvs 接口
91802
+ var _a;
91803
+ // 使用 DescribeEnvInfo 进行单环境查询
91649
91804
  const params = {
91650
91805
  EnvId: this.envId
91651
91806
  };
91652
- if (this.envType === 'run') {
91653
- params.EnvType = 'run';
91654
- }
91655
- 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
+ };
91656
91826
  return {
91657
- // @ts-ignore
91658
- EnvInfo: (EnvList === null || EnvList === void 0 ? void 0 : EnvList.length) ? EnvList[0] : {},
91659
- RequestId
91827
+ EnvInfo: envInfo,
91828
+ RequestId: response.RequestId || ''
91660
91829
  };
91661
91830
  }
91662
91831
  /**
@@ -91665,6 +91834,18 @@ class EnvService {
91665
91834
  async describeEnvs(params) {
91666
91835
  return this.cloudService.request('DescribeEnvs', params);
91667
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
+ }
91668
91849
  /**
91669
91850
  * 修改环境名称
91670
91851
  * @param {string} alias 环境名称
@@ -91985,28 +92166,28 @@ class EnvService {
91985
92166
  async calculatePackageRenewPrice(params) {
91986
92167
  var _a, _b, _c, _d, _e, _f;
91987
92168
  const { envId, period = 1, currency = 'CNY' } = params;
91988
- const [packageRes, envRes, billingRes] = await Promise.all([
92169
+ const [packageRes, envInfoRes, billingRes] = await Promise.all([
91989
92170
  this.describeBaasPackageList({
91990
92171
  Source: 'qcloud',
91991
92172
  PackageTypeList: ['default', 'vip', 'basic', 'trial']
91992
92173
  }),
91993
- this.describeEnvs({
92174
+ this.describeEnvInfo({
91994
92175
  EnvId: envId
91995
92176
  }),
91996
92177
  this.describeBillingInfo({
91997
92178
  EnvId: envId
91998
92179
  })
91999
92180
  ]);
92000
- 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;
92001
92182
  const billingInfo = (_b = billingRes.EnvBillingInfoList) === null || _b === void 0 ? void 0 : _b[0];
92002
- if (!envInfo || !billingInfo) {
92183
+ if (!(envBaseInfo === null || envBaseInfo === void 0 ? void 0 : envBaseInfo.EnvId) || !billingInfo) {
92003
92184
  throw new error_1.CloudBaseError(`Invalid env id: ${envId}`);
92004
92185
  }
92005
- 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);
92006
92187
  if (!packageInfo) {
92007
92188
  throw new error_1.CloudBaseError(`Invalid env id: ${envId}`);
92008
92189
  }
92009
- const region = envInfo.Region;
92190
+ const region = envBaseInfo.Region;
92010
92191
  const extPackageData = ((_d = billingInfo.ExtPackageCode) === null || _d === void 0 ? void 0 : _d.reduce((acc, { Code, Num }) => {
92011
92192
  acc[Code] = Num;
92012
92193
  return acc;
@@ -92039,21 +92220,21 @@ class EnvService {
92039
92220
  async calculatePackageModifyPrice(params) {
92040
92221
  var _a, _b, _c, _d, _e, _f, _g;
92041
92222
  const { newPackageId, envId, currency = 'CNY' } = params;
92042
- const [packageRes, envRes, billingRes] = await Promise.all([
92223
+ const [packageRes, envInfoRes, billingRes] = await Promise.all([
92043
92224
  this.describeBaasPackageList({
92044
92225
  Source: 'qcloud',
92045
92226
  PackageTypeList: ['default', 'vip', 'basic', 'trial']
92046
92227
  }),
92047
- this.describeEnvs({
92228
+ this.describeEnvInfo({
92048
92229
  EnvId: envId
92049
92230
  }),
92050
92231
  this.describeBillingInfo({
92051
92232
  EnvId: envId
92052
92233
  })
92053
92234
  ]);
92054
- 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;
92055
92236
  const billingInfo = (_b = billingRes.EnvBillingInfoList) === null || _b === void 0 ? void 0 : _b[0];
92056
- if (!envInfo || !billingInfo || !billingInfo.PackageId) {
92237
+ if (!(envBaseInfo === null || envBaseInfo === void 0 ? void 0 : envBaseInfo.EnvId) || !billingInfo || !billingInfo.PackageId) {
92057
92238
  throw new error_1.CloudBaseError(`Invalid env id: ${envId}`);
92058
92239
  }
92059
92240
  const oldPackageInfo = (_c = packageRes.PackageList) === null || _c === void 0 ? void 0 : _c.find(item => item.PackageName === billingInfo.PackageId);
@@ -92064,7 +92245,7 @@ class EnvService {
92064
92245
  if (!newPackageInfo) {
92065
92246
  throw new error_1.CloudBaseError(`Invalid package id: ${newPackageId}`);
92066
92247
  }
92067
- const region = envInfo.Region;
92248
+ const region = envBaseInfo.Region;
92068
92249
  const newBillTags = this.parseBillTags(newPackageInfo.BillTags);
92069
92250
  const oldBillTags = this.parseBillTags(oldPackageInfo.BillTags);
92070
92251
  let userNumber = 0;
@@ -101493,6 +101674,7 @@ const DEFAULT_PLUGINS = [
101493
101674
  "cloudrun",
101494
101675
  "gateway",
101495
101676
  "app-auth",
101677
+ "apps",
101496
101678
  "permissions",
101497
101679
  "logs",
101498
101680
  "agents",
@@ -101622,7 +101804,9 @@ async function createCloudBaseMcpServer(options) {
101622
101804
  }
101623
101805
  catch (error) {
101624
101806
  if ((0, tool_result_js_1.isToolPayloadError)(error)) {
101625
- 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);
101626
101810
  }
101627
101811
  throw error;
101628
101812
  }
@@ -108450,10 +108634,10 @@ function registerDataModelTools(server) {
108450
108634
  throw new Error(`不支持的操作类型: ${action}`);
108451
108635
  }
108452
108636
  });
108453
- // modifyDataModel - 数据模型修改工具(创建/更新)
108637
+ // modifyDataModel - 数据模型写工具(兼容名称,当前仅支持创建)
108454
108638
  server.registerTool?.("modifyDataModel", {
108455
- title: "修改数据模型",
108456
- description: "基于Mermaid classDiagram创建或更新数据模型。支持创建新模型和更新现有模型结构。内置异步任务监控,自动轮询直至完成或超时。",
108639
+ title: "修改数据模型(当前仅支持创建)",
108640
+ description: "基于Mermaid classDiagram创建数据模型。为保持兼容性,工具名仍为 modifyDataModel;当前仅支持创建新模型,不支持更新现有模型结构。内置异步任务监控,自动轮询直至完成或超时。",
108457
108641
  inputSchema: {
108458
108642
  mermaidDiagram: zod_1.z.string()
108459
108643
  .describe(`Mermaid classDiagram代码,描述数据模型结构。
@@ -108613,10 +108797,10 @@ classDiagram
108613
108797
  status: status,
108614
108798
  action: action,
108615
108799
  message: status === "success"
108616
- ? `数据模型${action === "create" ? "创建" : "更新"}成功,共处理${models.length}个模型`
108800
+ ? `数据模型创建成功,共处理${models.length}个模型`
108617
108801
  : status === "init"
108618
108802
  ? `任务超时,任务ID: ${taskId},请稍后手动查询状态`
108619
- : `数据模型${action === "create" ? "创建" : "更新"}失败`,
108803
+ : `数据模型创建失败`,
108620
108804
  taskResult: statusResult?.Data,
108621
108805
  }, null, 2),
108622
108806
  },
@@ -111572,6 +111756,115 @@ Writable.prototype._destroy = function (err, cb) {
111572
111756
  cb(err);
111573
111757
  };
111574
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
+
111575
111868
  /***/ }),
111576
111869
 
111577
111870
  /***/ 34836:
@@ -111843,14 +112136,15 @@ exports.CLOUDRUN_SERVICE_TYPES = ['function', 'container'];
111843
112136
  exports.CLOUDRUN_ACCESS_TYPES = ['OA', 'PUBLIC', 'MINIAPP', 'VPC'];
111844
112137
  // Input schema for queryCloudRun tool
111845
112138
  const queryCloudRunInputSchema = {
111846
- action: zod_1.z.enum(['list', 'detail', 'templates']).describe('查询操作类型:list=获取云托管服务列表(支持分页和筛选),detail=查询指定服务的详细信息(包括配置、版本、访问地址等),templates=获取可用的项目模板列表(用于初始化新项目)'),
112139
+ action: zod_1.z.enum(['list', 'detail', 'templates', 'getDeployLog']).describe('查询操作类型:list=获取云托管服务列表(支持分页和筛选),detail=查询指定服务的详细信息(包含服务配置和最新部署状态),templates=获取可用的项目模板列表(用于初始化新项目),getDeployLog=获取指定服务最近一次或指定构建的部署日志'),
111847
112140
  // List operation parameters
111848
112141
  pageSize: zod_1.z.number().min(1).max(100).optional().default(10).describe('分页大小,控制每页返回的服务数量。取值范围:1-100,默认值:10。建议根据网络性能和显示需求调整'),
111849
112142
  pageNum: zod_1.z.number().min(1).optional().default(1).describe('页码,用于分页查询。从1开始,默认值:1。配合pageSize使用可实现分页浏览'),
111850
112143
  serverName: zod_1.z.string().optional().describe('服务名称筛选条件,支持模糊匹配。例如:输入"test"可匹配"test-service"、"my-test-app"等服务名称。留空则查询所有服务'),
111851
112144
  serverType: zod_1.z.enum(exports.CLOUDRUN_SERVICE_TYPES).optional().describe('服务类型筛选条件:function=函数型云托管(仅支持Node.js,有特殊的开发要求和限制,适合简单的API服务),container=容器型服务(推荐使用,支持任意语言和框架如Java/Go/Python/PHP/.NET等,适合大多数应用场景)'),
111852
- // Detail operation parameters
111853
- detailServerName: zod_1.z.string().optional().describe('要查询详细信息的服务名称。当action为detail时必需提供,必须是已存在的服务名称。可通过list操作获取可用的服务名称列表'),
112145
+ // Detail and log operation parameters
112146
+ detailServerName: zod_1.z.string().optional().describe('要查询详细信息或部署日志的服务名称。当action为detail或getDeployLog时建议提供,必须是已存在的服务名称。可通过list操作获取可用的服务名称列表'),
112147
+ buildId: zod_1.z.number().optional().describe('构建ID,仅在action=getDeployLog时使用。不传时默认返回最近一次部署的构建日志'),
111854
112148
  };
111855
112149
  // Input schema for manageCloudRun tool
111856
112150
  const ManageCloudRunInputSchema = {
@@ -111958,6 +112252,28 @@ function buildManageCloudRunErrorMessage(action, serverName, error) {
111958
112252
  }
111959
112253
  return `[manageCloudRun/${action}] ${baseMessage}\n建议:${suggestions.join(" ")}`;
111960
112254
  }
112255
+ function getCloudRunQueryServerName(input) {
112256
+ return input.detailServerName || input.serverName;
112257
+ }
112258
+ function normalizeProcessLogText(logs) {
112259
+ return logs
112260
+ .map((log) => {
112261
+ if (typeof log === "string") {
112262
+ return log;
112263
+ }
112264
+ if (log && typeof log === "object") {
112265
+ if ("Log" in log && typeof log.Log === "string") {
112266
+ return log.Log;
112267
+ }
112268
+ if ("Text" in log && typeof log.Text === "string") {
112269
+ return log.Text;
112270
+ }
112271
+ return JSON.stringify(log);
112272
+ }
112273
+ return String(log);
112274
+ })
112275
+ .join("\n");
112276
+ }
111961
112277
  /**
111962
112278
  * Format CloudRun service info for display
111963
112279
  */
@@ -111972,7 +112288,7 @@ function registerCloudRunTools(server) {
111972
112288
  // Tool 1: Get CloudRun service information (read operations)
111973
112289
  server.registerTool("queryCloudRun", {
111974
112290
  title: "查询 CloudRun 服务信息",
111975
- description: "查询云托管服务信息,支持获取服务列表、查询服务详情和获取可用模板列表。返回的服务信息包括服务名称、状态、访问类型、配置详情等。",
112291
+ description: "查询云托管服务信息,支持获取服务列表、查询服务详情、获取可用模板列表和部署日志。返回的服务信息包括服务名称、状态、访问类型、配置详情以及最近部署上下文。",
111976
112292
  inputSchema: queryCloudRunInputSchema,
111977
112293
  annotations: {
111978
112294
  readOnlyHint: true,
@@ -112021,7 +112337,21 @@ function registerCloudRunTools(server) {
112021
112337
  };
112022
112338
  }
112023
112339
  case 'detail': {
112024
- const serverName = input.detailServerName || input.serverName;
112340
+ const serverName = getCloudRunQueryServerName(input);
112341
+ if (!serverName) {
112342
+ return {
112343
+ content: [
112344
+ {
112345
+ type: "text",
112346
+ text: JSON.stringify({
112347
+ success: false,
112348
+ error: "detailServerName or serverName is required for detail action",
112349
+ message: "Please provide detailServerName or serverName."
112350
+ }, null, 2)
112351
+ }
112352
+ ]
112353
+ };
112354
+ }
112025
112355
  const result = await cloudrunService.detail({ serverName });
112026
112356
  if (!result) {
112027
112357
  return {
@@ -112037,6 +112367,30 @@ function registerCloudRunTools(server) {
112037
112367
  ]
112038
112368
  };
112039
112369
  }
112370
+ let latestDeploy = null;
112371
+ let deployRecordsWarning;
112372
+ let message;
112373
+ try {
112374
+ const deployRecords = await cloudrunService.getDeployRecords({ serverName });
112375
+ latestDeploy = deployRecords?.DeployRecords?.[0] ?? null;
112376
+ if (!latestDeploy) {
112377
+ message = `Retrieved details for service '${serverName}'. No deploy records found yet.`;
112378
+ }
112379
+ else if (typeof latestDeploy.Status === 'string' && latestDeploy.Status.includes('failed')) {
112380
+ message = `Service '${serverName}' latest deploy failed. Please use queryCloudRun(action="getDeployLog") for details.`;
112381
+ }
112382
+ else if (typeof latestDeploy.Status === 'string' && latestDeploy.Status.includes('creating')) {
112383
+ message = `Service '${serverName}' latest deploy is still running. Please check again later or query the deploy log for progress.`;
112384
+ }
112385
+ else {
112386
+ message = `Retrieved details for service '${serverName}'. Latest service status: ${result.BaseInfo?.Status || 'unknown'}, latest deploy status: ${latestDeploy.Status || 'unknown'}.`;
112387
+ }
112388
+ }
112389
+ catch (error) {
112390
+ const baseMessage = error instanceof Error ? error.message : String(error);
112391
+ deployRecordsWarning = `Failed to fetch deploy records: ${baseMessage}`;
112392
+ message = `Retrieved details for service '${serverName}', but deploy records are currently unavailable.`;
112393
+ }
112040
112394
  return {
112041
112395
  content: [
112042
112396
  {
@@ -112044,9 +112398,11 @@ function registerCloudRunTools(server) {
112044
112398
  text: JSON.stringify({
112045
112399
  success: true,
112046
112400
  data: {
112047
- service: result
112401
+ service: result,
112402
+ latestDeploy,
112403
+ ...(deployRecordsWarning ? { deployRecordsWarning } : {})
112048
112404
  },
112049
- message: `Retrieved details for service '${serverName}'`
112405
+ message
112050
112406
  }, null, 2)
112051
112407
  }
112052
112408
  ]
@@ -112069,6 +112425,79 @@ function registerCloudRunTools(server) {
112069
112425
  ]
112070
112426
  };
112071
112427
  }
112428
+ case 'getDeployLog': {
112429
+ const serverName = getCloudRunQueryServerName(input);
112430
+ if (!serverName) {
112431
+ return {
112432
+ content: [
112433
+ {
112434
+ type: "text",
112435
+ text: JSON.stringify({
112436
+ success: false,
112437
+ error: "detailServerName or serverName is required for getDeployLog action",
112438
+ message: "Please provide detailServerName or serverName."
112439
+ }, null, 2)
112440
+ }
112441
+ ]
112442
+ };
112443
+ }
112444
+ const deployRecords = await cloudrunService.getDeployRecords({ serverName });
112445
+ const latestDeploy = deployRecords?.DeployRecords?.[0];
112446
+ if (!latestDeploy) {
112447
+ return {
112448
+ content: [
112449
+ {
112450
+ type: "text",
112451
+ text: JSON.stringify({
112452
+ success: false,
112453
+ error: `Service '${serverName}' has no deploy records.`,
112454
+ message: "Please deploy the service first, then query the deploy log again."
112455
+ }, null, 2)
112456
+ }
112457
+ ]
112458
+ };
112459
+ }
112460
+ const buildId = input.buildId ?? latestDeploy.BuildId;
112461
+ const buildLogResult = await cloudrunService.getBuildLog({
112462
+ serverName,
112463
+ buildId,
112464
+ });
112465
+ let processLogs = [];
112466
+ let processLogsWarning;
112467
+ if (latestDeploy.RunId && typeof cloudrunService.getProcessLog === 'function') {
112468
+ try {
112469
+ const processLogResult = await cloudrunService.getProcessLog({
112470
+ RunId: latestDeploy.RunId,
112471
+ });
112472
+ processLogs = processLogResult?.Logs || [];
112473
+ }
112474
+ catch (error) {
112475
+ processLogsWarning = error instanceof Error ? error.message : String(error);
112476
+ }
112477
+ }
112478
+ const buildLogText = typeof buildLogResult?.Log?.Text === 'string' ? buildLogResult.Log.Text : '';
112479
+ const processLogText = Array.isArray(processLogs) && processLogs.length > 0 ? normalizeProcessLogText(processLogs) : '';
112480
+ const combinedLogText = [buildLogText, processLogText].filter(Boolean).join('\n');
112481
+ return {
112482
+ content: [
112483
+ {
112484
+ type: "text",
112485
+ text: JSON.stringify({
112486
+ success: true,
112487
+ data: {
112488
+ buildId,
112489
+ deployRecord: latestDeploy,
112490
+ buildLog: buildLogResult?.Log || null,
112491
+ processLogs,
112492
+ combinedLogText,
112493
+ ...(processLogsWarning ? { processLogsWarning } : {})
112494
+ },
112495
+ message: `Retrieved deploy log for service '${serverName}'`
112496
+ }, null, 2)
112497
+ }
112498
+ ]
112499
+ };
112500
+ }
112072
112501
  default:
112073
112502
  throw new Error(`Unsupported action: ${input.action}`);
112074
112503
  }
@@ -112336,6 +112765,7 @@ for await (let x of res.textStream) {
112336
112765
  name: input.serverName
112337
112766
  }
112338
112767
  };
112768
+ const consoleUrl = `https://tcb.cloud.tencent.com/dev?envId=${currentEnvId}#/platform-run/service/detail?serverName=${input.serverName}&tabId=overview&envId=${currentEnvId}`;
112339
112769
  try {
112340
112770
  fs_1.default.writeFileSync(cloudbasercPath, JSON.stringify(cloudbasercContent, null, 2));
112341
112771
  }
@@ -112379,8 +112809,6 @@ for await (let x of res.textStream) {
112379
112809
  }
112380
112810
  // Extract project name from targetPath
112381
112811
  const projectName = path_1.default.basename(targetPath);
112382
- // Build console URL
112383
- const consoleUrl = `https://tcb.cloud.tencent.com/dev?envId=${currentEnvId}#/platform-run/service/detail?serverName=${input.serverName}&tabId=overview&envId=${currentEnvId}`;
112384
112812
  // Send notification
112385
112813
  await (0, notification_js_1.sendDeployNotification)(server, {
112386
112814
  deployType: 'cloudrun',
@@ -112402,12 +112830,13 @@ for await (let x of res.textStream) {
112402
112830
  success: true,
112403
112831
  data: {
112404
112832
  serviceName: input.serverName,
112405
- status: 'deployed',
112833
+ status: 'deploying',
112406
112834
  deployPath: targetPath,
112407
112835
  serverType: serverType,
112408
- cloudbasercGenerated: true
112836
+ cloudbasercGenerated: true,
112837
+ consoleUrl
112409
112838
  },
112410
- message: `Successfully deployed ${serverType} service '${input.serverName}' from ${targetPath}`
112839
+ message: `Triggered deployment for ${serverType} service '${input.serverName}' from ${targetPath}. You can follow the progress in ${consoleUrl}`
112411
112840
  }, null, 2)
112412
112841
  }
112413
112842
  ]
@@ -115048,6 +115477,7 @@ const cloudbase_manager_js_1 = __webpack_require__(3431);
115048
115477
  const json_content_js_1 = __webpack_require__(4365);
115049
115478
  const QUERY_APP_ACTIONS = ["listApps", "getApp", "listAppVersions", "getAppVersion"];
115050
115479
  const MANAGE_APP_ACTIONS = ["deployApp", "deleteApp", "deleteAppVersion"];
115480
+ const APP_FRAMEWORKS = ["vue", "react", "next", "nuxt", "vite", "angular", "static"];
115051
115481
  function buildEnvelope(data, message) {
115052
115482
  return {
115053
115483
  success: true,
@@ -115070,15 +115500,24 @@ function registerAppTools(server) {
115070
115500
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
115071
115501
  server.registerTool?.("queryApps", {
115072
115502
  title: "查询 CloudApp",
115073
- description: "CloudApp 域统一只读入口。支持应用、版本列表与版本详情查询。",
115503
+ description: "CloudApp 域统一只读入口。可先查应用/版本,再在重新部署后用 listAppVersions 或 getAppVersion 按 serviceName 验证是否生成了新版本与最新构建状态。",
115074
115504
  inputSchema: {
115075
115505
  action: zod_1.z.enum(QUERY_APP_ACTIONS),
115076
- serviceName: zod_1.z.string().optional(),
115077
- searchKey: zod_1.z.string().optional(),
115078
- pageNo: zod_1.z.number().optional(),
115079
- pageSize: zod_1.z.number().optional(),
115080
- versionName: zod_1.z.string().optional(),
115081
- 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 后可直接用它轮询状态。"),
115082
115521
  },
115083
115522
  annotations: {
115084
115523
  readOnlyHint: true,
@@ -115157,20 +115596,49 @@ function registerAppTools(server) {
115157
115596
  });
115158
115597
  server.registerTool?.("manageApps", {
115159
115598
  title: "管理 CloudApp",
115160
- description: "CloudApp 域统一写入口。首轮仅支持静态部署、删除应用和删除版本。",
115599
+ description: "CloudApp 域统一写入口。action=deployApp 会先 uploadCode 再 createApp;首次调用创建应用,后续复用同一个 serviceName 会直接触发重新部署并生成新版本,无需先删除旧应用。",
115161
115600
  inputSchema: {
115162
115601
  action: zod_1.z.enum(MANAGE_APP_ACTIONS),
115163
- serviceName: zod_1.z.string(),
115164
- localPath: zod_1.z.string().optional(),
115165
- appPath: zod_1.z.string().optional(),
115166
- buildPath: zod_1.z.string().optional(),
115167
- framework: zod_1.z.string().optional(),
115168
- nodeJsVersion: zod_1.z.string().optional(),
115169
- installCmd: zod_1.z.string().optional(),
115170
- buildCmd: zod_1.z.string().optional(),
115171
- deployCmd: zod_1.z.string().optional(),
115172
- ignore: zod_1.z.array(zod_1.z.string()).optional(),
115173
- 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 时必填。"),
115174
115642
  },
115175
115643
  annotations: {
115176
115644
  readOnlyHint: false,
@@ -115179,7 +115647,7 @@ function registerAppTools(server) {
115179
115647
  openWorldHint: true,
115180
115648
  category: "apps",
115181
115649
  },
115182
- }, 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, }) => {
115183
115651
  try {
115184
115652
  const cloudbase = await getManager();
115185
115653
  const appService = getCloudAppService(cloudbase);
@@ -115187,13 +115655,13 @@ function registerAppTools(server) {
115187
115655
  throw new Error("当前 manager 未提供 cloudAppService");
115188
115656
  }
115189
115657
  if (action === "deployApp") {
115190
- if (!localPath) {
115191
- throw new Error("action=deployApp 时必须提供 localPath");
115658
+ if (!filePath) {
115659
+ throw new Error("action=deployApp 时必须提供 filePath");
115192
115660
  }
115193
115661
  const uploadResult = await appService.uploadCode({
115194
115662
  deployType: "static-hosting",
115195
115663
  serviceName,
115196
- localPath,
115664
+ localPath: filePath,
115197
115665
  ignore,
115198
115666
  });
115199
115667
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, uploadResult);
@@ -116569,7 +117037,65 @@ const fs_1 = __importDefault(__webpack_require__(29021));
116569
117037
  const path_1 = __importDefault(__webpack_require__(39902));
116570
117038
  const zod_1 = __webpack_require__(21614);
116571
117039
  const cloudbase_manager_js_1 = __webpack_require__(3431);
117040
+ const cloud_mode_js_1 = __webpack_require__(89684);
116572
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
+ };
116573
117099
  function isDirectoryUploadTarget(localPath, cloudPath) {
116574
117100
  if (localPath) {
116575
117101
  try {
@@ -116599,277 +117125,661 @@ function buildHostingAccessUrl(staticDomain, cloudPath, localPath) {
116599
117125
  const pathname = isDirectory ? `${normalizedCloudPath}/` : normalizedCloudPath;
116600
117126
  return `https://${staticDomain}/${pathname}`;
116601
117127
  }
116602
- function buildUploadFilesErrorMessage(error, localPath) {
117128
+ function buildUploadErrorMessage(error, localPath) {
116603
117129
  const baseMessage = error instanceof Error ? error.message : String(error);
116604
117130
  const suggestions = [];
116605
117131
  if (/路径不存在|无读写权限/i.test(baseMessage)) {
116606
117132
  if (localPath) {
116607
117133
  suggestions.push(`请先确认本地路径 \`${localPath}\` 存在且当前进程有读取权限。`);
116608
117134
  }
116609
- suggestions.push("如果报错的是构建产物中的某个静态资源文件,请检查构建后的资源引用路径是否正确。");
116610
- suggestions.push("若站点部署到子路径,请确认 `publicPath`、`base`、`assetPrefix` 等配置没有把资源指向不存在的位置。");
117135
+ suggestions.push('如果报错的是构建产物中的某个静态资源文件,请检查构建后的资源引用路径是否正确。');
117136
+ suggestions.push('若站点部署到子路径,请确认 publicPath、base、assetPrefix 等配置没有把资源指向不存在的位置。');
116611
117137
  }
116612
117138
  if (suggestions.length === 0) {
116613
- 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。`);
116614
117374
  }
116615
- return `[uploadFiles] ${baseMessage}\n建议:${suggestions.join(" ")}`;
116616
117375
  }
116617
117376
  function registerHostingTools(server) {
116618
- // 获取 cloudBaseOptions,如果没有则为 undefined
116619
117377
  const cloudBaseOptions = server.cloudBaseOptions;
116620
- // 创建闭包函数来获取 CloudBase Manager
116621
117378
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
116622
- // uploadFiles - 上传文件到静态网站托管 (cloud-incompatible)
116623
- server.registerTool("uploadFiles", {
116624
- title: "上传静态文件",
116625
- description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage。对于本地评测、现有脚手架补全或仅需本地开发服务器验证的任务,通常不需要调用此工具,除非用户明确要求部署站点。",
116626
- inputSchema: {
116627
- localPath: zod_1.z.string().optional().describe("本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。"),
116628
- cloudPath: zod_1.z.string().optional().describe("静态托管云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。云存储对象路径请改用 manageStorage。"),
116629
- files: zod_1.z.array(zod_1.z.object({
116630
- localPath: zod_1.z.string(),
116631
- cloudPath: zod_1.z.string()
116632
- })).default([]).describe("多文件上传配置"),
116633
- ignore: zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]).optional().describe("忽略文件模式")
116634
- },
117379
+ server.registerTool('queryHosting', {
117380
+ title: '查询静态托管',
117381
+ description: '查询 CloudBase 静态托管的只读信息。适合 AI 先做发现再决定下一步:action=websiteConfig 查询首页/错误页/路由规则与站点域名信息;action=status 查询托管服务状态;action=findFiles 按前缀查找文件;action=listFiles 列出全部托管文件;action=domainStatus 查询自定义域名的当前状态与配置。该工具不会产生任何副作用。',
117382
+ inputSchema: queryHostingInputSchema,
116635
117383
  annotations: {
116636
- readOnlyHint: false,
116637
- destructiveHint: false,
116638
- idempotentHint: false,
117384
+ readOnlyHint: true,
116639
117385
  openWorldHint: true,
116640
- category: "hosting"
116641
- }
116642
- }, async ({ localPath, cloudPath, files = [], ignore }) => {
116643
- const cloudbase = await getManager();
116644
- let result;
116645
- try {
116646
- result = await cloudbase.hosting.uploadFiles({
116647
- localPath,
116648
- cloudPath,
116649
- files,
116650
- ignore
116651
- });
116652
- }
116653
- catch (error) {
116654
- throw new Error(buildUploadFilesErrorMessage(error, localPath));
116655
- }
116656
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116657
- const uploadResult = result;
116658
- // 获取环境信息
116659
- const envInfo = await cloudbase.env.getEnvInfo();
116660
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, envInfo);
116661
- const staticDomain = envInfo.EnvInfo?.StaticStorages?.[0]?.StaticDomain;
116662
- const accessUrl = buildHostingAccessUrl(staticDomain, cloudPath, localPath);
116663
- // Send deployment notification to CodeBuddy IDE
117386
+ category: 'hosting',
117387
+ },
117388
+ }, async (args) => {
116664
117389
  try {
116665
- const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
116666
- // Extract project name from localPath
116667
- let projectName = "unknown";
116668
- if (localPath) {
116669
- try {
116670
- // If localPath is a file, get parent directory name; if it's a directory, get directory name
116671
- const stats = fs_1.default.statSync(localPath);
116672
- if (stats.isFile()) {
116673
- projectName = path_1.default.basename(path_1.default.dirname(localPath));
116674
- }
116675
- else {
116676
- 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,用于按前缀查找托管文件。');
116677
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
+ });
116678
117447
  }
116679
- catch (statErr) {
116680
- // If stat fails, try to extract from path directly
116681
- 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
+ });
116682
117507
  }
116683
117508
  }
116684
- // Build console URL
116685
- const consoleUrl = `https://tcb.cloud.tencent.com/dev?envId=${envId}#/static-hosting`;
116686
- // Send notification
116687
- await (0, notification_js_1.sendDeployNotification)(server, {
116688
- deployType: 'hosting',
116689
- url: accessUrl,
116690
- projectId: envId,
116691
- projectName: projectName,
116692
- consoleUrl: consoleUrl
116693
- });
116694
117509
  }
116695
- catch (notifyErr) {
116696
- // Notification failure should not affect deployment flow
116697
- // 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);
116698
117516
  }
116699
- return {
116700
- content: [
116701
- {
116702
- type: "text",
116703
- text: JSON.stringify({
116704
- ...uploadResult,
116705
- staticDomain,
116706
- message: "文件上传成功",
116707
- accessUrl: accessUrl
116708
- }, null, 2)
116709
- }
116710
- ]
116711
- };
116712
117517
  });
116713
- // deleteFiles - 删除静态网站托管文件
116714
- server.registerTool?.("deleteFiles", {
116715
- title: "删除静态文件",
116716
- description: "删除静态网站托管的文件或文件夹",
116717
- inputSchema: {
116718
- cloudPath: zod_1.z.string().describe("云端文件或文件夹路径"),
116719
- isDir: zod_1.z.boolean().default(false).describe("是否为文件夹")
116720
- },
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,
116721
117522
  annotations: {
116722
117523
  readOnlyHint: false,
116723
117524
  destructiveHint: true,
116724
- idempotentHint: true,
117525
+ idempotentHint: false,
116725
117526
  openWorldHint: true,
116726
- category: "hosting"
116727
- }
116728
- }, async ({ cloudPath, isDir = false }) => {
116729
- const cloudbase = await getManager();
116730
- const result = await cloudbase.hosting.deleteFiles({
116731
- cloudPath,
116732
- isDir
116733
- });
116734
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116735
- return {
116736
- content: [
116737
- {
116738
- type: "text",
116739
- text: JSON.stringify(result, null, 2)
116740
- }
116741
- ]
116742
- };
116743
- });
116744
- // findFiles - 搜索文件
116745
- server.registerTool?.("findFiles", {
116746
- title: "搜索静态文件",
116747
- description: "搜索静态网站托管的文件",
116748
- inputSchema: {
116749
- prefix: zod_1.z.string().describe("匹配前缀"),
116750
- marker: zod_1.z.string().optional().describe("起始对象键标记"),
116751
- maxKeys: zod_1.z.number().optional().describe("单次返回最大条目数")
117527
+ category: 'hosting',
116752
117528
  },
116753
- annotations: {
116754
- readOnlyHint: true,
116755
- openWorldHint: true,
116756
- category: "hosting"
116757
- }
116758
- }, async ({ prefix, marker, maxKeys }) => {
116759
- const cloudbase = await getManager();
116760
- const result = await cloudbase.hosting.findFiles({
116761
- prefix,
116762
- marker,
116763
- maxKeys
116764
- });
116765
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116766
- return {
116767
- content: [
116768
- {
116769
- type: "text",
116770
- 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
+ });
116771
117595
  }
116772
- ]
116773
- };
116774
- });
116775
- // domainManagement - 统一的域名管理工具
116776
- server.registerTool?.("domainManagement", {
116777
- title: "静态托管域名管理",
116778
- description: "统一的域名管理工具,支持绑定、解绑、查询和修改域名配置",
116779
- inputSchema: {
116780
- action: zod_1.z.enum(["create", "delete", "check", "modify"]).describe("操作类型: create=绑定域名, delete=解绑域名, check=查询域名配置, modify=修改域名配置"),
116781
- // 绑定域名参数
116782
- domain: zod_1.z.string().optional().describe("域名"),
116783
- certId: zod_1.z.string().optional().describe("证书ID(绑定域名时必需)"),
116784
- domains: zod_1.z.array(zod_1.z.string()).optional().describe("域名列表(查询配置时使用)"),
116785
- // 修改域名参数
116786
- domainId: zod_1.z.number().optional().describe("域名ID(修改配置时必需)"),
116787
- domainConfig: zod_1.z.object({
116788
- Refer: zod_1.z.object({
116789
- Switch: zod_1.z.string(),
116790
- RefererRules: zod_1.z.array(zod_1.z.object({
116791
- RefererType: zod_1.z.string(),
116792
- Referers: zod_1.z.array(zod_1.z.string()),
116793
- AllowEmpty: zod_1.z.boolean()
116794
- })).optional()
116795
- }).optional(),
116796
- Cache: zod_1.z.array(zod_1.z.object({
116797
- RuleType: zod_1.z.string(),
116798
- RuleValue: zod_1.z.string(),
116799
- CacheTtl: zod_1.z.number()
116800
- })).optional(),
116801
- IpFilter: zod_1.z.object({
116802
- Switch: zod_1.z.string(),
116803
- FilterType: zod_1.z.string().optional(),
116804
- Filters: zod_1.z.array(zod_1.z.string()).optional()
116805
- }).optional(),
116806
- IpFreqLimit: zod_1.z.object({
116807
- Switch: zod_1.z.string(),
116808
- Qps: zod_1.z.number().optional()
116809
- }).optional()
116810
- }).optional().describe("域名配置(修改配置时使用)")
116811
- },
116812
- annotations: {
116813
- readOnlyHint: false,
116814
- destructiveHint: false,
116815
- idempotentHint: false,
116816
- openWorldHint: true,
116817
- category: "hosting"
116818
- }
116819
- }, async ({ action, domain, certId, domains, domainId, domainConfig }) => {
116820
- const cloudbase = await getManager();
116821
- let result;
116822
- switch (action) {
116823
- case "create":
116824
- if (!domain || !certId) {
116825
- 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
+ });
116826
117639
  }
116827
- result = await cloudbase.hosting.CreateHostingDomain({
116828
- domain,
116829
- certId
116830
- });
116831
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116832
- break;
116833
- case "delete":
116834
- if (!domain) {
116835
- 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
+ });
116836
117662
  }
116837
- result = await cloudbase.hosting.deleteHostingDomain({
116838
- domain
116839
- });
116840
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116841
- break;
116842
- case "check":
116843
- if (!domains || domains.length === 0) {
116844
- 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
+ });
116845
117676
  }
116846
- result = await cloudbase.hosting.tcbCheckResource({
116847
- domains
116848
- });
116849
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116850
- break;
116851
- case "modify":
116852
- if (!domain || domainId === undefined || !domainConfig) {
116853
- 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
+ }));
116854
117694
  }
116855
- result = await cloudbase.hosting.tcbModifyAttribute({
116856
- domain,
116857
- domainId,
116858
- domainConfig
116859
- });
116860
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
116861
- break;
116862
- default:
116863
- throw new Error(`不支持的操作类型: ${action}`);
116864
- }
116865
- return {
116866
- content: [
116867
- {
116868
- type: "text",
116869
- 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
+ }));
116870
117713
  }
116871
- ]
116872
- };
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
+ }
116873
117783
  });
116874
117784
  }
116875
117785
 
@@ -123649,6 +124559,7 @@ function buildCapiErrorMessage(service, action, error) {
123649
124559
  const tcbEntry = service === "tcb" ? findTcbActionEntry(action) : undefined;
123650
124560
  const hasInvalidActionError = /invalid or not found|does not exist|not recognized/i.test(baseMessage);
123651
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);
123652
124563
  if (hasInvalidActionError) {
123653
124564
  suggestions.push(`Action \`${action}\` 可能不存在或不对外开放。请不要继续猜测 Action 名称,先确认 service=\`${service}\` 下该 Action 在当前 API 版本是否真实存在。`);
123654
124565
  if (service === "tcb") {
@@ -123679,6 +124590,20 @@ function buildCapiErrorMessage(service, action, error) {
123679
124590
  }
123680
124591
  }
123681
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
+ }
123682
124607
  if (/ECONNRESET|socket hang up|ETIMEDOUT|ENOTFOUND/i.test(baseMessage)) {
123683
124608
  suggestions.push("网络请求异常,建议稍后重试,并检查本地网络/代理设置。");
123684
124609
  }
@@ -133564,7 +134489,7 @@ class TelemetryReporter {
133564
134489
  const nodeVersion = process.version; // Node.js版本
133565
134490
  const arch = os_1.default.arch(); // 系统架构
133566
134491
  // 从构建时注入的版本号获取MCP版本信息
133567
- const mcpVersion = process.env.npm_package_version || "2.17.1" || 0;
134492
+ const mcpVersion = process.env.npm_package_version || "2.19.4" || 0;
133568
134493
  return {
133569
134494
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
133570
134495
  deviceId: this.deviceId,
@@ -137652,7 +138577,7 @@ exports.isLogServiceEnabled = isLogServiceEnabled;
137652
138577
  const utils_1 = __webpack_require__(62358);
137653
138578
  __exportStar(__webpack_require__(83700), exports);
137654
138579
  /**
137655
- * 纯函数:根据 DescribeEnvs 返回的 LogServices 列表判断日志服务是否已开通。
138580
+ * 纯函数:根据 DescribeEnvInfo 返回的 LogServices 列表判断日志服务是否已开通。
137656
138581
  * 已开通条件:至少一个条目有有效的 TopicId。
137657
138582
  */
137658
138583
  function isLogServiceEnabled(logServices) {
@@ -137669,21 +138594,17 @@ class LogService {
137669
138594
  /**
137670
138595
  * 检查当前环境的日志服务是否已开通
137671
138596
  *
137672
- * 通过 DescribeEnvs 接口获取环境信息,判断 LogServices 字段中是否包含有效的
138597
+ * 通过 DescribeEnvInfo 接口获取环境信息,判断 LogServices 字段中是否包含有效的
137673
138598
  * 日志主题(TopicId 不为空)。
137674
138599
  *
137675
138600
  * @returns true 表示已开通,false 表示未开通或开通中
137676
138601
  */
137677
138602
  async checkLogServiceEnabled() {
137678
- var _a;
137679
- const res = await this.cloudService.request('DescribeEnvs', {
138603
+ var _a, _b;
138604
+ const res = await this.cloudService.request('DescribeEnvInfo', {
137680
138605
  EnvId: this.envId
137681
138606
  });
137682
- const envInfo = (_a = res === null || res === void 0 ? void 0 : res.EnvList) === null || _a === void 0 ? void 0 : _a[0];
137683
- if (!envInfo) {
137684
- return false;
137685
- }
137686
- 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;
137687
138608
  if (!logServices || logServices.length === 0) {
137688
138609
  return false;
137689
138610
  }
@@ -145497,6 +146418,576 @@ module.exports = function generate_contains(it, $keyword, $ruleType) {
145497
146418
  }
145498
146419
 
145499
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
+
145500
146991
  /***/ }),
145501
146992
 
145502
146993
  /***/ 48525:
@@ -167364,6 +168855,8 @@ const cloudBaseRun_1 = __webpack_require__(24344);
167364
168855
  const mysql_1 = __webpack_require__(61697);
167365
168856
  const cloudApp_1 = __webpack_require__(32665);
167366
168857
  const permission_1 = __webpack_require__(24786);
168858
+ const sandbox_1 = __webpack_require__(48488);
168859
+ const aiModel_1 = __webpack_require__(34800);
167367
168860
  class Environment {
167368
168861
  constructor(context, envId) {
167369
168862
  this.inited = false;
@@ -167387,6 +168880,8 @@ class Environment {
167387
168880
  this.mysqlService = new mysql_1.MysqlService(this);
167388
168881
  this.permissionService = new permission_1.PermissionService(this);
167389
168882
  this.cloudAppService = new cloudApp_1.CloudAppService(this);
168883
+ this.sandboxService = new sandbox_1.SandboxService(this);
168884
+ this.aiModelService = new aiModel_1.AiModelService(this);
167390
168885
  }
167391
168886
  async lazyInit() {
167392
168887
  if (!this.inited) {
@@ -167455,6 +168950,12 @@ class Environment {
167455
168950
  getPermissionService() {
167456
168951
  return this.permissionService;
167457
168952
  }
168953
+ getSandboxService() {
168954
+ return this.sandboxService;
168955
+ }
168956
+ getAiModelService() {
168957
+ return this.aiModelService;
168958
+ }
167458
168959
  getCommonService(serviceType = 'tcb', serviceVersion) {
167459
168960
  return new common_1.CommonService(this, serviceType, serviceVersion);
167460
168961
  }
@@ -178008,11 +179509,24 @@ function requireStringParam(value, fieldName, action) {
178008
179509
  }
178009
179510
  return value.trim();
178010
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
+ }
178011
179525
  // OpenAPI 文档 URL 列表
178012
179526
  const OPENAPI_SOURCES = [
178013
179527
  {
178014
179528
  name: "mysqldb",
178015
- description: "MySQL RESTful API - 云开发 MySQL 数据库 HTTP API",
179529
+ description: "关系型数据库 RESTful API (MySQL/PostgreSQL) - 云开发关系型数据库 HTTP API",
178016
179530
  url: "https://docs.cloudbase.net/openapi/mysqldb.v1.openapi.yaml",
178017
179531
  },
178018
179532
  {
@@ -178035,6 +179549,16 @@ const OPENAPI_SOURCES = [
178035
179549
  description: "Storage API - 云存储 HTTP API",
178036
179550
  url: "https://docs.cloudbase.net/openapi/storage.v1.openapi.yaml",
178037
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
+ },
178038
179562
  ];
178039
179563
  async function downloadWebTemplate() {
178040
179564
  const zipPath = path.join(CACHE_BASE_DIR, "web-cloudbase-project.zip");
@@ -178310,6 +179834,8 @@ async function registerRagTools(server) {
178310
179834
  error,
178311
179835
  });
178312
179836
  }
179837
+ const skillNames = skills.map((skill) => path.basename(path.dirname(skill.absolutePath)));
179838
+ const openapiNames = openapis.map((api) => api.name);
178313
179839
  const getDocsManager = () => (0, cloudbase_manager_js_1.createCloudBaseManagerWithOptions)(server.cloudBaseOptions ?? {})
178314
179840
  ?.docs;
178315
179841
  server.registerTool?.("searchKnowledgeBase", {
@@ -178318,6 +179844,11 @@ async function registerRagTools(server) {
178318
179844
 
178319
179845
  强烈推荐始终优先使用固定技能文档 (skill)、OpenAPI 文档 (openapi) 或 CloudBase 官方文档 (docs) 模式进行检索,仅当固定文档无法覆盖你的问题时,再使用向量查询 (vector) 模式。
178320
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
+
178321
179852
  固定技能文档 (skill) 查询当前支持 ${skills.length} 个固定文档,分别是:
178322
179853
  ${skills
178323
179854
  .map((skill) => `文档名:${path.basename(path.dirname(skill.absolutePath))} 文档介绍:${skill.description}`)
@@ -178329,14 +179860,8 @@ async function registerRagTools(server) {
178329
179860
  .join("\n")}`,
178330
179861
  inputSchema: {
178331
179862
  mode: SearchKnowledgeModeEnum,
178332
- skillName: zod_1.z
178333
- .enum(skills.map((skill) => path.basename(path.dirname(skill.absolutePath))))
178334
- .optional()
178335
- .describe("mode=skill 时指定。技能名称。"),
178336
- apiName: zod_1.z
178337
- .enum(openapis.map((api) => api.name))
178338
- .optional()
178339
- .describe("mode=openapi 时指定。API 名称。"),
179863
+ skillName: buildOptionalStringEnum(skillNames, "mode=skill 时指定。技能名称。"),
179864
+ apiName: buildOptionalStringEnum(openapiNames, "mode=openapi 时指定。API 名称。"),
178340
179865
  action: CloudBaseDocsActionEnum.optional().describe("mode=docs 时指定。CloudBase 文档操作类型:listModules=列出所有文档模块,listModuleDocs=获取指定模块的目录结构,findByName=按名称/路径/URL 智能查找,readDoc=读取指定文档 Markdown,searchDocs=全文搜索官方文档。"),
178341
179866
  moduleName: zod_1.z
178342
179867
  .string()
@@ -178429,7 +179954,7 @@ async function registerRagTools(server) {
178429
179954
  content: [
178430
179955
  {
178431
179956
  type: "text",
178432
- 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"}`,
178433
179958
  },
178434
179959
  ],
178435
179960
  };
@@ -178450,7 +179975,7 @@ async function registerRagTools(server) {
178450
179975
  content: [
178451
179976
  {
178452
179977
  type: "text",
178453
- 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"}`,
178454
179979
  },
178455
179980
  ],
178456
179981
  };
@@ -191431,7 +192956,7 @@ ${envIdSection}
191431
192956
  ## 环境信息
191432
192957
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
191433
192958
  - Node.js版本: ${process.version}
191434
- - MCP 版本:${process.env.npm_package_version || "2.17.1" || 0}
192959
+ - MCP 版本:${process.env.npm_package_version || "2.19.4" || 0}
191435
192960
  - 系统架构: ${os_1.default.arch()}
191436
192961
  - 时间: ${new Date().toISOString()}
191437
192962
  - 请求ID: ${requestId}
@@ -191511,7 +193036,8 @@ function createWrappedHandler(name, handler, server) {
191511
193036
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
191512
193037
  ide: server.ide || process.env.INTEGRATION_IDE || ''
191513
193038
  });
191514
- const enhancedErrorMessage = `${errorMessage}\n\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
193039
+ const mcpVersion = true ? "2.19.4" : 0;
193040
+ const enhancedErrorMessage = `${errorMessage}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
191515
193041
  // 创建新的错误对象,保持原有的错误类型但更新消息
191516
193042
  const enhancedError = error instanceof Error
191517
193043
  ? new Error(enhancedErrorMessage)
@@ -199542,13 +201068,8 @@ async function resolveNoSqlInstanceId(options) {
199542
201068
  return resolved;
199543
201069
  }
199544
201070
  async function callNoSqlContentApi(options) {
199545
- const resolvedInstance = await resolveNoSqlInstanceId({
199546
- toolName: options.toolName,
199547
- cloudbase: options.cloudbase,
199548
- cloudBaseOptions: options.cloudBaseOptions,
199549
- instanceIdOverride: options.instanceIdOverride,
199550
- collectionName: options.collectionName,
199551
- });
201071
+ // 直接使用 EnvId 替代 Tag,无需通过 DescribeEnvs 获取 instanceId
201072
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(options.cloudBaseOptions);
199552
201073
  const startedAt = Date.now();
199553
201074
  try {
199554
201075
  const result = await options.cloudbase
@@ -199558,14 +201079,14 @@ async function callNoSqlContentApi(options) {
199558
201079
  Param: {
199559
201080
  ...options.param,
199560
201081
  TableName: options.collectionName,
199561
- Tag: resolvedInstance.instanceId,
201082
+ EnvId: envId,
199562
201083
  },
199563
201084
  });
199564
201085
  logNoSqlLatency(options.toolName, "cloudApiCall", {
199565
201086
  collectionName: options.collectionName,
199566
201087
  action: options.action,
199567
201088
  durationMs: Date.now() - startedAt,
199568
- instanceIdSource: resolvedInstance.source,
201089
+ instanceIdSource: "envId",
199569
201090
  requestId: result?.RequestId,
199570
201091
  });
199571
201092
  return result;
@@ -199575,20 +201096,9 @@ async function callNoSqlContentApi(options) {
199575
201096
  collectionName: options.collectionName,
199576
201097
  action: options.action,
199577
201098
  durationMs: Date.now() - startedAt,
199578
- instanceIdSource: resolvedInstance.source,
201099
+ instanceIdSource: "envId",
199579
201100
  message: error instanceof Error ? error.message : String(error),
199580
201101
  });
199581
- if (!options.instanceIdOverride &&
199582
- resolvedInstance.cacheKey &&
199583
- isInvalidInstanceIdError(error)) {
199584
- (0, cloudbase_manager_js_1.invalidateDatabaseInstanceIdCache)({
199585
- cacheKey: resolvedInstance.cacheKey,
199586
- });
199587
- logNoSqlLatency(options.toolName, "invalidateInstanceIdCache", {
199588
- collectionName: options.collectionName,
199589
- cacheKey: resolvedInstance.cacheKey,
199590
- });
199591
- }
199592
201102
  throw error;
199593
201103
  }
199594
201104
  }
@@ -199606,18 +201116,25 @@ async function waitForCollectionReady({ cloudbase, collectionName, logger, timeo
199606
201116
  });
199607
201117
  while (Date.now() <= deadline) {
199608
201118
  try {
199609
- 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
+ });
199610
201128
  (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, result);
199611
- if (result.Exists) {
199612
- logger?.({
199613
- type: "toolInfo",
199614
- toolName: "writeNoSqlDatabaseStructure",
199615
- message: "NoSQL collection is ready for subsequent operations",
199616
- collectionName,
199617
- waitedMs: Date.now() - startedAt,
199618
- });
199619
- return;
199620
- }
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;
199621
201138
  }
199622
201139
  catch (error) {
199623
201140
  lastError = error;
@@ -199649,7 +201166,7 @@ function registerDatabaseTools(server) {
199649
201166
  // readNoSqlDatabaseStructure
199650
201167
  server.registerTool?.("readNoSqlDatabaseStructure", {
199651
201168
  title: "读取 NoSQL 数据库结构",
199652
- description: "读取 NoSQL 数据库集合与索引结构,支持列出集合、查看集合详情、列出索引以及检查索引是否存在。",
201169
+ description: "读取 NoSQL 数据库集合与索引结构,支持列出集合、查看集合详情、列出索引以及检查索引是否存在。本工具为服务端管理工具,用于管理端查询数据库结构,不用于编写客户端代码。",
199653
201170
  inputSchema: {
199654
201171
  action: zod_1.z.enum([
199655
201172
  "listCollections",
@@ -199687,9 +201204,14 @@ checkIndex: 检查指定索引是否存在`),
199687
201204
  }, async ({ action, limit, offset, collectionName, indexName }) => {
199688
201205
  const cloudbase = await getManager();
199689
201206
  if (action === "listCollections") {
199690
- const result = await cloudbase.database.listCollections({
199691
- MgoOffset: offset,
199692
- 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
+ },
199693
201215
  });
199694
201216
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199695
201217
  return {
@@ -199711,17 +201233,30 @@ checkIndex: 检查指定索引是否存在`),
199711
201233
  if (!collectionName) {
199712
201234
  throw new Error("检查集合时必须提供 collectionName");
199713
201235
  }
199714
- const result = await cloudbase.database.checkCollectionExists(collectionName);
199715
- (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
+ }
199716
201251
  return {
199717
201252
  content: [
199718
201253
  {
199719
201254
  type: "text",
199720
201255
  text: JSON.stringify(withCollectionName(collectionName, {
199721
201256
  success: true,
199722
- exists: result.Exists,
199723
- requestId: result.RequestId,
199724
- message: result.Exists
201257
+ exists,
201258
+ requestId,
201259
+ message: exists
199725
201260
  ? "云开发数据库集合已存在"
199726
201261
  : "云开发数据库集合不存在",
199727
201262
  }), null, 2),
@@ -199733,7 +201268,11 @@ checkIndex: 检查指定索引是否存在`),
199733
201268
  if (!collectionName) {
199734
201269
  throw new Error("查看集合详情时必须提供 collectionName");
199735
201270
  }
199736
- 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
+ });
199737
201276
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199738
201277
  return {
199739
201278
  content: [
@@ -199754,7 +201293,11 @@ checkIndex: 检查指定索引是否存在`),
199754
201293
  if (!collectionName) {
199755
201294
  throw new Error("获取索引列表时必须提供 collectionName");
199756
201295
  }
199757
- 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
+ });
199758
201301
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199759
201302
  return {
199760
201303
  content: [
@@ -199775,8 +201318,22 @@ checkIndex: 检查指定索引是否存在`),
199775
201318
  if (!collectionName || !indexName) {
199776
201319
  throw new Error("检查索引时必须提供 collectionName 和 indexName");
199777
201320
  }
199778
- const result = await cloudbase.database.checkIndexExists(collectionName, indexName);
199779
- (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 });
199780
201337
  return {
199781
201338
  content: [
199782
201339
  {
@@ -199784,9 +201341,9 @@ checkIndex: 检查指定索引是否存在`),
199784
201341
  text: JSON.stringify(withCollectionName(collectionName, {
199785
201342
  success: true,
199786
201343
  indexName,
199787
- exists: result.Exists,
199788
- requestId: result.RequestId,
199789
- message: result.Exists ? "索引已存在" : "索引不存在",
201344
+ exists,
201345
+ requestId,
201346
+ message: exists ? "索引已存在" : "索引不存在",
199790
201347
  }), null, 2),
199791
201348
  },
199792
201349
  ],
@@ -199796,8 +201353,8 @@ checkIndex: 检查指定索引是否存在`),
199796
201353
  });
199797
201354
  // writeNoSqlDatabaseStructure
199798
201355
  server.registerTool?.("writeNoSqlDatabaseStructure", {
199799
- title: "修改 NoSQL 数据库结构",
199800
- description: "修改 NoSQL 数据库结构,支持创建/删除集合,以及通过 updateCollection 的 updateOptions.CreateIndexes / updateOptions.DropIndexes 添加索引和删除索引。",
201356
+ title: "创建并管理 NoSQL 数据库集合",
201357
+ description: "创建、删除和管理 NoSQL 数据库集合(collection)。支持创建新集合、删除现有集合,以及通过 updateCollection 的 updateOptions.CreateIndexes / updateOptions.DropIndexes 添加索引和删除索引。当需要新建集合时,使用 action=createCollection。本工具为服务端管理工具,用于管理端操作集合和索引结构,不用于编写客户端代码。",
199801
201358
  inputSchema: {
199802
201359
  action: zod_1.z.enum([
199803
201360
  "createCollection",
@@ -199844,9 +201401,22 @@ deleteCollection: 删除集合`),
199844
201401
  }, async ({ action, collectionName, updateOptions }) => {
199845
201402
  const cloudbase = await getManager();
199846
201403
  if (action === "createCollection") {
199847
- const existsResult = await cloudbase.database.checkCollectionExists(collectionName);
199848
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, existsResult);
199849
- 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) {
199850
201420
  return {
199851
201421
  content: [
199852
201422
  {
@@ -199854,7 +201424,7 @@ deleteCollection: 删除集合`),
199854
201424
  text: JSON.stringify(withCollectionName(collectionName, {
199855
201425
  success: true,
199856
201426
  action,
199857
- requestId: existsResult.RequestId,
201427
+ requestId: existsRequestId,
199858
201428
  message: "集合已存在,无需重复创建",
199859
201429
  exists: true,
199860
201430
  }), null, 2),
@@ -199862,7 +201432,13 @@ deleteCollection: 删除集合`),
199862
201432
  ],
199863
201433
  };
199864
201434
  }
199865
- 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
+ });
199866
201442
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199867
201443
  await waitForCollectionReady({
199868
201444
  cloudbase,
@@ -199887,7 +201463,14 @@ deleteCollection: 删除集合`),
199887
201463
  if (!updateOptions) {
199888
201464
  throw new Error("更新集合时必须提供 options");
199889
201465
  }
199890
- 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
+ });
199891
201474
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199892
201475
  return {
199893
201476
  content: [
@@ -199904,7 +201487,13 @@ deleteCollection: 删除集合`),
199904
201487
  };
199905
201488
  }
199906
201489
  if (action === "deleteCollection") {
199907
- 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
+ });
199908
201497
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
199909
201498
  const body = withCollectionName(collectionName, {
199910
201499
  success: true,
@@ -199929,7 +201518,7 @@ deleteCollection: 删除集合`),
199929
201518
  // readNoSqlDatabaseContent
199930
201519
  server.registerTool?.("readNoSqlDatabaseContent", {
199931
201520
  title: "查询并获取 NoSQL 数据库数据记录",
199932
- description: "查询并获取 NoSQL 数据库数据记录",
201521
+ description: "查询并获取 NoSQL 数据库数据记录。⚠️ 本工具为服务端管理工具,用于管理端/运维端查询。当任务要求编写客户端应用代码时(例如「用 JS SDK 读取数据」),不应使用本工具,而应在项目代码中编写 @cloudbase/js-sdk 客户端代码(如 db.collection().where().get())。",
199933
201522
  inputSchema: {
199934
201523
  collectionName: zod_1.z.string().describe("集合名称"),
199935
201524
  instanceId: zod_1.z
@@ -200016,7 +201605,7 @@ deleteCollection: 删除集合`),
200016
201605
  // writeNoSqlDatabaseContent
200017
201606
  server.registerTool?.("writeNoSqlDatabaseContent", {
200018
201607
  title: "修改 NoSQL 数据库数据记录",
200019
- 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)` 读取命中不到。",
200020
201609
  inputSchema: {
200021
201610
  action: zod_1.z
200022
201611
  .enum(["insert", "update", "delete"])
@@ -200037,7 +201626,11 @@ deleteCollection: 删除集合`),
200037
201626
  update: zod_1.z
200038
201627
  .union([zod_1.z.object({}).passthrough(), zod_1.z.string()])
200039
201628
  .optional()
200040
- .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 下其他字段保留`),
200041
201634
  isMulti: zod_1.z
200042
201635
  .boolean()
200043
201636
  .optional()
@@ -205028,6 +206621,23 @@ function buildPermissionHints(securityRule, resourceId) {
205028
206621
  buildTemplateLiteralRuleHint(securityRule, resourceId),
205029
206622
  ].filter(Boolean);
205030
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
+ }
205031
206641
  function registerPermissionTools(server) {
205032
206642
  const cloudBaseOptions = server.cloudBaseOptions;
205033
206643
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
@@ -205070,20 +206680,24 @@ function registerPermissionTools(server) {
205070
206680
  if (!resourceType || !resourceId) {
205071
206681
  throw new Error("action=getResourcePermission 时必须提供 resourceType 和 resourceId");
205072
206682
  }
206683
+ if (resourceType === "storage") {
206684
+ await ensureStorageBucketsExist(cloudbase, [resourceId]);
206685
+ }
205073
206686
  const result = await cloudbase.permission.describeResourcePermission({
205074
206687
  resourceType: mapResourceType(resourceType),
205075
206688
  resources: [resourceId],
205076
206689
  });
205077
206690
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
205078
206691
  const permissions = result.Data.PermissionList ?? [];
205079
- const securityRule = permissions.find((item) => item.Resource === resourceId)?.SecurityRule ??
205080
- permissions[0]?.SecurityRule;
206692
+ const matchedPermission = permissions.find((item) => item.Resource === resourceId) ?? permissions[0];
206693
+ const securityRule = matchedPermission?.SecurityRule;
205081
206694
  const hints = buildPermissionHints(securityRule, resourceId);
205082
206695
  return buildEnvelope({
205083
206696
  action,
205084
206697
  envId,
205085
206698
  resourceType,
205086
206699
  resourceId,
206700
+ aclTag: matchedPermission?.Permission,
205087
206701
  permissions,
205088
206702
  hints,
205089
206703
  raw: result,
@@ -205093,6 +206707,9 @@ function registerPermissionTools(server) {
205093
206707
  if (!resourceType) {
205094
206708
  throw new Error("action=listResourcePermissions 时必须提供 resourceType");
205095
206709
  }
206710
+ if (resourceType === "storage" && resourceIds?.length) {
206711
+ await ensureStorageBucketsExist(cloudbase, resourceIds);
206712
+ }
205096
206713
  const result = await cloudbase.permission.describeResourcePermission({
205097
206714
  resourceType: mapResourceType(resourceType),
205098
206715
  resources: resourceIds,
@@ -205563,7 +207180,9 @@ const http = __importStar(__webpack_require__(81630));
205563
207180
  const https = __importStar(__webpack_require__(55591));
205564
207181
  const os = __importStar(__webpack_require__(21820));
205565
207182
  const path = __importStar(__webpack_require__(39902));
207183
+ const url_1 = __webpack_require__(79551);
205566
207184
  const zod_1 = __webpack_require__(21614);
207185
+ const remote_url_safety_js_1 = __webpack_require__(89192);
205567
207186
  // CloudBase 模板配置
205568
207187
  const TEMPLATES = {
205569
207188
  react: {
@@ -205756,13 +207375,18 @@ function resolveDownloadTemplateIDE(ide, integrationIDE) {
205756
207375
  supportedIDEs: exports.IDE_TYPES.filter((t) => t !== "all"),
205757
207376
  };
205758
207377
  }
207378
+ const MAX_REDIRECT_HOPS = 5;
205759
207379
  // 根据 INTEGRATION_IDE 环境变量获取默认 IDE 类型
205760
207380
  // 下载文件到临时目录
205761
- 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;
205762
207387
  return new Promise((resolve, reject) => {
205763
- const client = url.startsWith("https:") ? https : http;
205764
207388
  client
205765
- .get(url, (res) => {
207389
+ .get(requestUrl, requestOptions ?? {}, (res) => {
205766
207390
  if (res.statusCode === 200) {
205767
207391
  const file = fs.createWriteStream(filePath);
205768
207392
  res.pipe(file);
@@ -205772,10 +207396,11 @@ async function downloadFile(url, filePath) {
205772
207396
  });
205773
207397
  file.on("error", reject);
205774
207398
  }
205775
- else if (res.statusCode === 302 || res.statusCode === 301) {
207399
+ else if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400) {
205776
207400
  // 处理重定向
205777
207401
  if (res.headers.location) {
205778
- downloadFile(res.headers.location, filePath)
207402
+ const redirectUrl = new url_1.URL(res.headers.location, requestUrl).toString();
207403
+ downloadFile(redirectUrl, filePath, redirectCount + 1)
205779
207404
  .then(resolve)
205780
207405
  .catch(reject);
205781
207406
  }
@@ -205971,9 +207596,8 @@ function registerSetupTools(server) {
205971
207596
  // downloadTemplate - 下载项目模板 (cloud-incompatible)
205972
207597
  server.registerTool("downloadTemplate", {
205973
207598
  title: "下载项目模板",
205974
- description: `自动下载并部署CloudBase项目模板。⚠️ **MANDATORY FOR NEW PROJECTS** ⚠️
205975
-
205976
- **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.17.1" : 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.4" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
205977
207601
  inputSchema: {
205978
207602
  template: zod_1.z
205979
207603
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -207265,6 +208889,8 @@ module.exports.sync = path => {
207265
208889
  "use strict";
207266
208890
 
207267
208891
  Object.defineProperty(exports, "__esModule", ({ value: true }));
208892
+ exports.buildVerificationUriComplete = buildVerificationUriComplete;
208893
+ exports.buildDeviceAuthChallengePayload = buildDeviceAuthChallengePayload;
207268
208894
  exports.getAuthProgressStateSync = getAuthProgressStateSync;
207269
208895
  exports.getAuthProgressState = getAuthProgressState;
207270
208896
  exports.resolveAuthOptions = resolveAuthOptions;
@@ -207293,6 +208919,54 @@ function normalizeOptionalString(value) {
207293
208919
  const trimmed = value.trim();
207294
208920
  return trimmed.length > 0 ? trimmed : undefined;
207295
208921
  }
208922
+ function buildVerificationUriComplete(deviceAuthInfo) {
208923
+ const explicitComplete = normalizeOptionalString(deviceAuthInfo?.verification_uri_complete);
208924
+ if (explicitComplete) {
208925
+ return explicitComplete;
208926
+ }
208927
+ const verificationUri = normalizeOptionalString(deviceAuthInfo?.verification_uri);
208928
+ const userCode = normalizeOptionalString(deviceAuthInfo?.user_code);
208929
+ if (!verificationUri || !userCode) {
208930
+ return undefined;
208931
+ }
208932
+ try {
208933
+ const url = new URL(verificationUri);
208934
+ const hash = url.hash.startsWith("#") ? url.hash.slice(1) : url.hash;
208935
+ if (hash) {
208936
+ const [hashPath, hashQuery = ""] = hash.split("?");
208937
+ const hashParams = new URLSearchParams(hashQuery);
208938
+ if (!hashParams.has("user_code")) {
208939
+ hashParams.set("user_code", userCode);
208940
+ }
208941
+ url.hash = hashParams.toString()
208942
+ ? `#${hashPath}?${hashParams.toString()}`
208943
+ : `#${hashPath}`;
208944
+ return url.toString();
208945
+ }
208946
+ if (!url.searchParams.has("user_code")) {
208947
+ url.searchParams.set("user_code", userCode);
208948
+ }
208949
+ return url.toString();
208950
+ }
208951
+ catch {
208952
+ if (verificationUri.includes("user_code=")) {
208953
+ return verificationUri;
208954
+ }
208955
+ const separator = verificationUri.includes("?") ? "&" : "?";
208956
+ return `${verificationUri}${separator}user_code=${encodeURIComponent(userCode)}`;
208957
+ }
208958
+ }
208959
+ function buildDeviceAuthChallengePayload(deviceAuthInfo) {
208960
+ if (!deviceAuthInfo) {
208961
+ return undefined;
208962
+ }
208963
+ return {
208964
+ user_code: deviceAuthInfo.user_code,
208965
+ verification_uri: deviceAuthInfo.verification_uri,
208966
+ verification_uri_complete: buildVerificationUriComplete(deviceAuthInfo),
208967
+ expires_in: deviceAuthInfo.expires_in,
208968
+ };
208969
+ }
207296
208970
  function normalizeAuthMode(value) {
207297
208971
  return value === "web" || value === "device" ? value : undefined;
207298
208972
  }
@@ -222921,17 +224595,10 @@ function normalizeAccessPath(path) {
222921
224595
  }
222922
224596
  return path.startsWith("/") ? path : `/${path}`;
222923
224597
  }
222924
- function ensureGatewayEnvId(cloudBaseOptions) {
222925
- const envId = cloudBaseOptions?.envId;
222926
- if (!envId) {
222927
- throw new Error("当前网关操作需要已绑定 envId");
222928
- }
222929
- return envId;
222930
- }
222931
224598
  function registerGatewayTools(server) {
222932
224599
  const cloudBaseOptions = server.cloudBaseOptions;
222933
224600
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
222934
- const getGatewayEnvId = () => ensureGatewayEnvId(cloudBaseOptions);
224601
+ const resolveEnvId = () => (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
222935
224602
  const buildEnvelope = (data, message, nextActions) => ({
222936
224603
  success: true,
222937
224604
  data,
@@ -222968,7 +224635,7 @@ function registerGatewayTools(server) {
222968
224635
  const listHttpServiceRoutes = async (domain) => {
222969
224636
  const cloudbase = await getManager();
222970
224637
  const result = await cloudbase.env.describeHttpServiceRoute({
222971
- EnvId: getGatewayEnvId(),
224638
+ EnvId: await resolveEnvId(),
222972
224639
  ...(domain
222973
224640
  ? {
222974
224641
  Filters: [
@@ -223004,7 +224671,7 @@ function registerGatewayTools(server) {
223004
224671
  throw new Error("route.serviceType 和 route.serviceName 为必填项");
223005
224672
  }
223006
224673
  return {
223007
- EnvId: getGatewayEnvId(),
224674
+ EnvId: await resolveEnvId(),
223008
224675
  Domain: {
223009
224676
  Domain: await resolveRouteDomain(domain),
223010
224677
  Routes: [
@@ -223114,6 +224781,9 @@ function registerGatewayTools(server) {
223114
224781
  if (!input.targetName || !input.targetType) {
223115
224782
  throw new Error("action=createAccess 时必须提供 targetType 和 targetName");
223116
224783
  }
224784
+ if (!input.type) {
224785
+ throw new Error("action=createAccess 且 targetType=function 时必须显式提供 type。HTTP 云函数传 HTTP;Event 函数传 Event。省略会默认按 Event 路由处理,可能让 HTTP 云函数访问后返回 FUNCTION_PARAM_INVALID。");
224786
+ }
223117
224787
  const cloudbase = await getManager();
223118
224788
  const accessPath = normalizeAccessPath(input.path || `/${input.targetName}`);
223119
224789
  let result;
@@ -223121,7 +224791,7 @@ function registerGatewayTools(server) {
223121
224791
  result = await cloudbase.access.createAccess({
223122
224792
  name: input.targetName,
223123
224793
  path: accessPath,
223124
- type: ((input.type || "Event") === "HTTP" ? 6 : 1),
224794
+ type: (input.type === "HTTP" ? 6 : 1),
223125
224795
  auth: input.auth,
223126
224796
  });
223127
224797
  }
@@ -223129,7 +224799,7 @@ function registerGatewayTools(server) {
223129
224799
  if (err.message && err.message.includes("An error has occurred")) {
223130
224800
  let hint = "为目标资源配置访问路由失败(后端内部错误)。请确保:1) 目标云函数已成功创建并处于 Active 状态;2) 环境默认 HTTP 域名已完成初始化;3) 该访问路径未被占用。";
223131
224801
  if (input.type === "HTTP") {
223132
- hint += "此外注意:如果目标函数最初是作为 Event 函数创建的,这里 type 必须依然传 Event(或省略),传 HTTP 会导致此错误。";
224802
+ hint += "此外注意:如果目标函数最初是作为 Event 函数创建的,这里 type 仍必须传 Event;误传 HTTP 会导致此错误。";
223133
224803
  }
223134
224804
  throw new Error(`${hint} 原始错误:${err.message}`);
223135
224805
  }
@@ -223142,12 +224812,22 @@ function registerGatewayTools(server) {
223142
224812
  targetName: input.targetName,
223143
224813
  path: accessPath,
223144
224814
  raw: result,
223145
- }, `已为目标 ${input.targetName} 创建网关访问路径。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。`, [
224815
+ }, `已为目标 ${input.targetName} 创建网关访问路径。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。该操作只创建网关入口,不会自动放开函数安全规则;若需要匿名或浏览器直接访问,请继续检查函数资源权限。`, [
223146
224816
  {
223147
224817
  tool: "queryGateway",
223148
224818
  action: "getAccess",
223149
224819
  reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
223150
224820
  },
224821
+ {
224822
+ tool: "queryPermissions",
224823
+ action: "getResourcePermission",
224824
+ reason: "确认函数安全规则是否允许预期访问方;网关 auth=false 不等于函数已允许匿名访问",
224825
+ },
224826
+ {
224827
+ tool: "managePermissions",
224828
+ action: "updateResourcePermission",
224829
+ reason: "只有在确认需要匿名或浏览器直连访问时,才按实际安全要求更新函数权限",
224830
+ },
223151
224831
  ]);
223152
224832
  }
223153
224833
  case "createRoute": {
@@ -223178,7 +224858,7 @@ function registerGatewayTools(server) {
223178
224858
  const cloudbase = await getManager();
223179
224859
  const domain = await resolveRouteDomain(input.domain);
223180
224860
  const result = await cloudbase.env.deleteHttpServiceRoute({
223181
- EnvId: getGatewayEnvId(),
224861
+ EnvId: await resolveEnvId(),
223182
224862
  Domain: domain,
223183
224863
  Paths: [normalizeAccessPath(routePath)],
223184
224864
  });
@@ -223196,7 +224876,7 @@ function registerGatewayTools(server) {
223196
224876
  }
223197
224877
  const cloudbase = await getManager();
223198
224878
  const result = await cloudbase.env.bindCustomDomain({
223199
- EnvId: getGatewayEnvId(),
224879
+ EnvId: await resolveEnvId(),
223200
224880
  Domain: {
223201
224881
  Domain: input.domain,
223202
224882
  CertId: input.certificateId,
@@ -223216,7 +224896,7 @@ function registerGatewayTools(server) {
223216
224896
  }
223217
224897
  const cloudbase = await getManager();
223218
224898
  const result = await cloudbase.env.deleteCustomDomain({
223219
- EnvId: getGatewayEnvId(),
224899
+ EnvId: await resolveEnvId(),
223220
224900
  Domain: input.domain,
223221
224901
  });
223222
224902
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
@@ -223300,18 +224980,32 @@ function registerGatewayTools(server) {
223300
224980
  },
223301
224981
  }, async (input) => withEnvelope(() => handleQueryGateway(input)));
223302
224982
  server.registerTool?.("manageGateway", {
223303
- title: "管理网关域资源",
223304
- description: "网关域统一写入口。通过 action 创建目标访问入口,后续承接更通用的网关配置能力。",
224983
+ title: "管理网关域资源(含自定义域名绑定)",
224984
+ description: "网关域统一写入口。通过 action 创建目标访问入口,后续承接更通用的网关配置能力。为已存在的 HTTP 云函数补默认域名访问时,通常使用 createAccess 并提供 targetType=\"function\"、targetName、type=\"HTTP\" 与期望 path。注意 createAccess 只创建网关入口,不会自动修改函数资源权限。⚠️ 如需绑定带 SSL 证书的自定义域名供公网 HTTPS 访问,使用 action=\"bindCustomDomain\"(需要 domain 和 certificateId 参数);如需配置 CORS/安全域名(无证书),请使用 envDomainManagement 工具。",
223305
224985
  inputSchema: {
223306
- 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。'),
223307
224989
  targetType: zod_1.z
223308
224990
  .enum(["function"])
223309
224991
  .optional()
223310
224992
  .describe("目标资源类型。当前支持 function,后续可扩展"),
223311
- targetName: zod_1.z.string().optional().describe("目标资源名称"),
223312
- path: zod_1.z.string().optional().describe("访问路径,默认 /{targetName}"),
223313
- type: zod_1.z.enum(["Event", "HTTP"]).optional().describe("目标函数的本身类型(非接入形式)。如果被访问的函数是 Event 型(默认),此处必须传 Event;只有当被访问函数在创建时就是 HTTP 函数时才传 HTTP。"),
223314
- 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;该开关仅控制网关入口本身,不会修改函数资源权限,仍需检查并按需调整函数安全规则。"),
223315
225009
  route: zod_1.z
223316
225010
  .object({
223317
225011
  routeId: zod_1.z.string().optional(),
@@ -232234,6 +233928,192 @@ module.exports = function generate_ref(it, $keyword, $ruleType) {
232234
233928
  }
232235
233929
 
232236
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
+
232237
234117
  /***/ }),
232238
234118
 
232239
234119
  /***/ 89225:
@@ -232713,8 +234593,7 @@ function shouldRegisterTool(toolName) {
232713
234593
  'auth',
232714
234594
  // Storage tools - local file uploads
232715
234595
  'uploadFile',
232716
- // Hosting tools - local file uploads
232717
- 'uploadFiles',
234596
+ // Hosting tools - action-level cloud gating is handled inside manageHosting
232718
234597
  // Function tools - local code uploads
232719
234598
  'updateFunctionCode',
232720
234599
  'createFunction', // also involves local files
@@ -240397,16 +242276,59 @@ function registerLogTools(server) {
240397
242276
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
240398
242277
  server.registerTool?.("queryLogs", {
240399
242278
  title: "查询日志服务",
240400
- 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\"`",
240401
242287
  inputSchema: {
240402
- action: zod_1.z.enum(QUERY_LOG_ACTIONS),
240403
- queryString: zod_1.z.string().optional(),
240404
- service: zod_1.z.enum(["tcb", "tcbr"]).optional(),
240405
- startTime: zod_1.z.string().optional(),
240406
- endTime: zod_1.z.string().optional(),
240407
- limit: zod_1.z.number().optional(),
240408
- context: zod_1.z.string().optional(),
240409
- 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` 降序"),
240410
242332
  },
240411
242333
  annotations: {
240412
242334
  readOnlyHint: true,
@@ -240424,7 +242346,14 @@ function registerLogTools(server) {
240424
242346
  }, enabled ? "日志服务已开通" : "日志服务未开通或仍在初始化中"));
240425
242347
  }
240426
242348
  if (!queryString) {
240427
- 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\")`。");
240428
242357
  }
240429
242358
  const result = await cloudbase.log.searchClsLog({
240430
242359
  queryString,
@@ -246304,17 +248233,55 @@ exports["default"] = parser;
246304
248233
  /***/ }),
246305
248234
 
246306
248235
  /***/ 94848:
246307
- /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
248236
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
246308
248237
 
246309
248238
  "use strict";
246310
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
+ })();
246311
248273
  Object.defineProperty(exports, "__esModule", ({ value: true }));
246312
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));
246313
248278
  const zod_1 = __webpack_require__(21614);
246314
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-';
246315
248282
  // Input schema for queryStorage tool
246316
248283
  const queryStorageInputSchema = {
246317
- 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=读取文本文件内容'),
246318
248285
  cloudPath: zod_1.z.string().describe('云端文件路径,例如 files/data.txt 或 files/(目录)'),
246319
248286
  maxAge: zod_1.z.number().min(1).max(86400).optional().default(3600).describe('临时链接有效期,单位为秒,取值范围:1-86400,默认值:3600(1小时)')
246320
248287
  };
@@ -246326,6 +248293,63 @@ const manageStorageInputSchema = {
246326
248293
  force: zod_1.z.boolean().optional().default(false).describe('强制操作开关,删除操作时建议设置为true以确认删除,默认false'),
246327
248294
  isDirectory: zod_1.z.boolean().optional().default(false).describe('是否为目录操作,true=目录操作,false=文件操作,默认false')
246328
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
+ }
246329
248353
  function registerStorageTools(server) {
246330
248354
  // 获取 cloudBaseOptions,如果没有则为 undefined
246331
248355
  const cloudBaseOptions = server.cloudBaseOptions;
@@ -246334,7 +248358,7 @@ function registerStorageTools(server) {
246334
248358
  // Tool 1: queryStorage - 查询存储信息(只读操作)
246335
248359
  server.registerTool("queryStorage", {
246336
248360
  title: "查询存储信息",
246337
- description: "查询云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。",
248361
+ description: "查询云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。注意:action=url 返回的 temporaryUrl 是临时签名链接,有效期由 maxAge 参数决定(默认1小时),不要当作永久公网地址使用。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导出 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。",
246338
248362
  inputSchema: queryStorageInputSchema,
246339
248363
  annotations: {
246340
248364
  readOnlyHint: true,
@@ -246393,6 +248417,11 @@ function registerStorageTools(server) {
246393
248417
  cloudPath: input.cloudPath,
246394
248418
  maxAge: input.maxAge || 3600
246395
248419
  }]);
248420
+ const publicAccess = await resolveStoragePublicAccess({
248421
+ cloudPath: input.cloudPath,
248422
+ cloudBaseOptions,
248423
+ manager,
248424
+ });
246396
248425
  return {
246397
248426
  content: [
246398
248427
  {
@@ -246404,7 +248433,10 @@ function registerStorageTools(server) {
246404
248433
  cloudPath: input.cloudPath,
246405
248434
  temporaryUrl: result[0]?.url || "",
246406
248435
  expireTime: `${input.maxAge || 3600}秒`,
246407
- 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 或先将目标路径设置为公有读。"
246408
248440
  },
246409
248441
  message: `Successfully generated temporary URL for '${input.cloudPath}'`
246410
248442
  }, null, 2)
@@ -246412,6 +248444,42 @@ function registerStorageTools(server) {
246412
248444
  ]
246413
248445
  };
246414
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
+ }
246415
248483
  default:
246416
248484
  throw new Error(`Unsupported action: ${input.action}`);
246417
248485
  }
@@ -246419,7 +248487,7 @@ function registerStorageTools(server) {
246419
248487
  // Tool 2: manageStorage - 管理存储文件(写操作)
246420
248488
  server.registerTool("manageStorage", {
246421
248489
  title: "管理存储文件",
246422
- description: "管理云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。",
248490
+ description: "管理云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。注意:上传后返回的 temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。",
246423
248491
  inputSchema: manageStorageInputSchema,
246424
248492
  annotations: {
246425
248493
  readOnlyHint: false,
@@ -246459,6 +248527,11 @@ function registerStorageTools(server) {
246459
248527
  cloudPath: input.cloudPath,
246460
248528
  maxAge: 3600
246461
248529
  }]);
248530
+ const publicAccess = await resolveStoragePublicAccess({
248531
+ cloudPath: input.cloudPath,
248532
+ cloudBaseOptions,
248533
+ manager,
248534
+ });
246462
248535
  return {
246463
248536
  content: [
246464
248537
  {
@@ -246471,7 +248544,10 @@ function registerStorageTools(server) {
246471
248544
  cloudPath: input.cloudPath,
246472
248545
  isDirectory: input.isDirectory,
246473
248546
  temporaryUrl: fileUrls[0]?.url || "",
246474
- 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 或先将目标路径设置为公有读。"
246475
248551
  },
246476
248552
  message: `Successfully uploaded ${input.isDirectory ? 'directory' : 'file'} from '${input.localPath}' to '${input.cloudPath}'`
246477
248553
  }, null, 2)
@@ -254911,6 +256987,12 @@ class CloudBase {
254911
256987
  get permission() {
254912
256988
  return this.currentEnvironment().getPermissionService();
254913
256989
  }
256990
+ get sandbox() {
256991
+ return this.currentEnvironment().getSandboxService();
256992
+ }
256993
+ get aiModel() {
256994
+ return this.currentEnvironment().getAiModelService();
256995
+ }
254914
256996
  get docs() {
254915
256997
  if (!this.docsService) {
254916
256998
  this.docsService = new docs_1.DocsService();