@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/README.md +83 -26
- package/dist/cli.cjs +3 -3
- package/dist/index.cjs +2758 -676
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2021 -658
- package/package.json +2 -2
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("", "
|
|
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"
|
|
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
|
|
2803
|
+
// envQuery - 环境查询(合并 listEnvs + getEnvInfo + getEnvAuthDomains)
|
|
2712
2804
|
server.registerTool?.("envQuery", {
|
|
2713
2805
|
title: "环境查询",
|
|
2714
|
-
description: "
|
|
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"
|
|
2718
|
-
.describe("查询类型:list
|
|
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
|
|
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:
|
|
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: "
|
|
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
|
|
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
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
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
|
-
|
|
24925
|
-
|
|
24926
|
-
|
|
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
|
|
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
|
|
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
|
|
37153
|
+
const remote_url_safety_js_1 = __webpack_require__(89192);
|
|
37107
37154
|
// 常量定义
|
|
37108
37155
|
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
|
|
37109
|
-
const
|
|
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
|
-
|
|
37268
|
-
|
|
37269
|
-
|
|
37270
|
-
|
|
37271
|
-
|
|
37272
|
-
|
|
37273
|
-
|
|
37274
|
-
|
|
37275
|
-
|
|
37276
|
-
|
|
37277
|
-
|
|
37278
|
-
|
|
37279
|
-
|
|
37280
|
-
|
|
37281
|
-
|
|
37282
|
-
|
|
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
|
|
37295
|
-
|
|
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[
|
|
37301
|
-
const contentLength = parseInt(res.headers[
|
|
37302
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
37336
|
+
fileStream.on("error", (error) => {
|
|
37342
37337
|
fsPromises.unlink(targetPath).catch(() => { });
|
|
37343
37338
|
reject(error);
|
|
37344
37339
|
});
|
|
37345
|
-
})
|
|
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
|
|
37354
|
-
|
|
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[
|
|
37360
|
-
const contentLength = parseInt(res.headers[
|
|
37361
|
-
const contentDisposition = res.headers[
|
|
37362
|
-
|
|
37363
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
37418
|
+
fileStream.on("error", (error) => {
|
|
37396
37419
|
fsPromises.unlink(filePath).catch(() => { });
|
|
37397
37420
|
reject(error);
|
|
37398
37421
|
});
|
|
37399
|
-
})
|
|
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
|
|
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
|
-
|
|
88731
|
-
|
|
88732
|
-
|
|
88733
|
-
|
|
88734
|
-
|
|
88735
|
-
|
|
88736
|
-
|
|
88737
|
-
|
|
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
|
-
|
|
88761
|
-
|
|
88762
|
-
|
|
88763
|
-
|
|
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
|
|
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")
|
|
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("
|
|
89350
|
-
|
|
89351
|
-
|
|
89352
|
-
|
|
89353
|
-
|
|
89354
|
-
|
|
89355
|
-
|
|
89356
|
-
|
|
89357
|
-
|
|
89358
|
-
|
|
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("
|
|
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: "
|
|
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、
|
|
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
|
-
"
|
|
89379
|
-
"
|
|
89380
|
-
"
|
|
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("
|
|
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
|
|
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
|
-
|
|
91802
|
+
var _a;
|
|
91803
|
+
// 使用 DescribeEnvInfo 进行单环境查询
|
|
91649
91804
|
const params = {
|
|
91650
91805
|
EnvId: this.envId
|
|
91651
91806
|
};
|
|
91652
|
-
|
|
91653
|
-
|
|
91654
|
-
|
|
91655
|
-
const
|
|
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
|
-
|
|
91658
|
-
|
|
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,
|
|
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.
|
|
92174
|
+
this.describeEnvInfo({
|
|
91994
92175
|
EnvId: envId
|
|
91995
92176
|
}),
|
|
91996
92177
|
this.describeBillingInfo({
|
|
91997
92178
|
EnvId: envId
|
|
91998
92179
|
})
|
|
91999
92180
|
]);
|
|
92000
|
-
const
|
|
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 (!
|
|
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 ===
|
|
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 =
|
|
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,
|
|
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.
|
|
92228
|
+
this.describeEnvInfo({
|
|
92048
92229
|
EnvId: envId
|
|
92049
92230
|
}),
|
|
92050
92231
|
this.describeBillingInfo({
|
|
92051
92232
|
EnvId: envId
|
|
92052
92233
|
})
|
|
92053
92234
|
]);
|
|
92054
|
-
const
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
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
|
-
?
|
|
108800
|
+
? `数据模型创建成功,共处理${models.length}个模型`
|
|
108617
108801
|
: status === "init"
|
|
108618
108802
|
? `任务超时,任务ID: ${taskId},请稍后手动查询状态`
|
|
108619
|
-
:
|
|
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
|
|
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('
|
|
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
|
|
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
|
|
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: '
|
|
112833
|
+
status: 'deploying',
|
|
112406
112834
|
deployPath: targetPath,
|
|
112407
112835
|
serverType: serverType,
|
|
112408
|
-
cloudbasercGenerated: true
|
|
112836
|
+
cloudbasercGenerated: true,
|
|
112837
|
+
consoleUrl
|
|
112409
112838
|
},
|
|
112410
|
-
message: `
|
|
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
|
|
115077
|
-
|
|
115078
|
-
|
|
115079
|
-
|
|
115080
|
-
|
|
115081
|
-
|
|
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
|
|
115164
|
-
|
|
115165
|
-
|
|
115166
|
-
|
|
115167
|
-
|
|
115168
|
-
|
|
115169
|
-
|
|
115170
|
-
|
|
115171
|
-
|
|
115172
|
-
|
|
115173
|
-
|
|
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,
|
|
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 (!
|
|
115191
|
-
throw new Error("action=deployApp 时必须提供
|
|
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
|
|
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(
|
|
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
|
-
|
|
116623
|
-
|
|
116624
|
-
|
|
116625
|
-
|
|
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:
|
|
116637
|
-
destructiveHint: false,
|
|
116638
|
-
idempotentHint: false,
|
|
117384
|
+
readOnlyHint: true,
|
|
116639
117385
|
openWorldHint: true,
|
|
116640
|
-
category:
|
|
116641
|
-
}
|
|
116642
|
-
}, async (
|
|
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
|
|
116666
|
-
|
|
116667
|
-
|
|
116668
|
-
|
|
116669
|
-
|
|
116670
|
-
|
|
116671
|
-
|
|
116672
|
-
|
|
116673
|
-
|
|
116674
|
-
|
|
116675
|
-
|
|
116676
|
-
|
|
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
|
-
|
|
116680
|
-
|
|
116681
|
-
|
|
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 (
|
|
116696
|
-
|
|
116697
|
-
|
|
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
|
-
|
|
116714
|
-
|
|
116715
|
-
|
|
116716
|
-
|
|
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:
|
|
117525
|
+
idempotentHint: false,
|
|
116725
117526
|
openWorldHint: true,
|
|
116726
|
-
category:
|
|
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
|
-
|
|
116754
|
-
|
|
116755
|
-
|
|
116756
|
-
|
|
116757
|
-
|
|
116758
|
-
|
|
116759
|
-
|
|
116760
|
-
|
|
116761
|
-
|
|
116762
|
-
|
|
116763
|
-
|
|
116764
|
-
|
|
116765
|
-
|
|
116766
|
-
|
|
116767
|
-
|
|
116768
|
-
|
|
116769
|
-
|
|
116770
|
-
|
|
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
|
-
|
|
116776
|
-
|
|
116777
|
-
|
|
116778
|
-
|
|
116779
|
-
|
|
116780
|
-
|
|
116781
|
-
|
|
116782
|
-
|
|
116783
|
-
|
|
116784
|
-
|
|
116785
|
-
|
|
116786
|
-
|
|
116787
|
-
|
|
116788
|
-
|
|
116789
|
-
|
|
116790
|
-
|
|
116791
|
-
|
|
116792
|
-
|
|
116793
|
-
|
|
116794
|
-
|
|
116795
|
-
|
|
116796
|
-
|
|
116797
|
-
|
|
116798
|
-
|
|
116799
|
-
|
|
116800
|
-
|
|
116801
|
-
|
|
116802
|
-
|
|
116803
|
-
|
|
116804
|
-
|
|
116805
|
-
|
|
116806
|
-
|
|
116807
|
-
|
|
116808
|
-
|
|
116809
|
-
|
|
116810
|
-
|
|
116811
|
-
|
|
116812
|
-
|
|
116813
|
-
|
|
116814
|
-
|
|
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
|
-
|
|
116828
|
-
|
|
116829
|
-
|
|
116830
|
-
|
|
116831
|
-
|
|
116832
|
-
|
|
116833
|
-
|
|
116834
|
-
|
|
116835
|
-
|
|
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
|
-
|
|
116838
|
-
|
|
116839
|
-
|
|
116840
|
-
|
|
116841
|
-
|
|
116842
|
-
|
|
116843
|
-
|
|
116844
|
-
|
|
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
|
-
|
|
116847
|
-
|
|
116848
|
-
|
|
116849
|
-
|
|
116850
|
-
|
|
116851
|
-
|
|
116852
|
-
|
|
116853
|
-
|
|
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
|
-
|
|
116856
|
-
domain
|
|
116857
|
-
|
|
116858
|
-
|
|
116859
|
-
|
|
116860
|
-
|
|
116861
|
-
|
|
116862
|
-
|
|
116863
|
-
|
|
116864
|
-
|
|
116865
|
-
|
|
116866
|
-
|
|
116867
|
-
|
|
116868
|
-
|
|
116869
|
-
|
|
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.
|
|
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
|
-
* 纯函数:根据
|
|
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
|
-
* 通过
|
|
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('
|
|
138603
|
+
var _a, _b;
|
|
138604
|
+
const res = await this.cloudService.request('DescribeEnvInfo', {
|
|
137680
138605
|
EnvId: this.envId
|
|
137681
138606
|
});
|
|
137682
|
-
const
|
|
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: "
|
|
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:
|
|
178333
|
-
|
|
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: ${
|
|
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: ${
|
|
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.
|
|
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
|
|
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
|
-
|
|
199546
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
199612
|
-
|
|
199613
|
-
|
|
199614
|
-
|
|
199615
|
-
|
|
199616
|
-
|
|
199617
|
-
|
|
199618
|
-
|
|
199619
|
-
|
|
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
|
|
199691
|
-
|
|
199692
|
-
|
|
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
|
|
199715
|
-
|
|
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
|
|
199723
|
-
requestId
|
|
199724
|
-
message:
|
|
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
|
|
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
|
|
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
|
|
199779
|
-
|
|
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
|
|
199788
|
-
requestId
|
|
199789
|
-
message:
|
|
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: "
|
|
199800
|
-
description: "
|
|
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
|
-
|
|
199848
|
-
(0, cloudbase_manager_js_1.
|
|
199849
|
-
|
|
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:
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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(
|
|
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
|
|
205080
|
-
|
|
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(
|
|
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
|
|
207399
|
+
else if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400) {
|
|
205776
207400
|
// 处理重定向
|
|
205777
207401
|
if (res.headers.location) {
|
|
205778
|
-
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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: (
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
223312
|
-
|
|
223313
|
-
|
|
223314
|
-
|
|
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 -
|
|
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
|
|
240403
|
-
|
|
240404
|
-
|
|
240405
|
-
|
|
240406
|
-
|
|
240407
|
-
|
|
240408
|
-
|
|
240409
|
-
|
|
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
|
|
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();
|