@cloudbase/cloudbase-mcp 2.36.0 → 2.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -47,6 +47,7 @@ exports.resolveAuthProgressState = resolveAuthProgressState;
47
47
  exports.rejectAuthProgressState = rejectAuthProgressState;
48
48
  exports.resetAuthProgressState = resetAuthProgressState;
49
49
  exports.getCredentialSource = getCredentialSource;
50
+ exports.isEnvCredentialRemovableByLogout = isEnvCredentialRemovableByLogout;
50
51
  exports.peekLoginState = peekLoginState;
51
52
  exports.ensureLogin = ensureLogin;
52
53
  exports.getLoginState = getLoginState;
@@ -480,6 +481,21 @@ function readProjectCredentialFile(projectRoot) {
480
481
  return null;
481
482
  }
482
483
  }
484
+ /**
485
+ * 删掉项目级凭据文件。只删这一个文件,目录留着——删目录会顺带清掉同在里面的
486
+ * `project.json` 等项目配置。
487
+ *
488
+ * 不走 toolbox 的 `logout({cwd})`:那个接口会**无条件先删全局凭据**,而「退出本项目」
489
+ * 不该把用户的账号级登录一起注销掉。
490
+ */
491
+ function deleteProjectCredentialFile(projectRoot) {
492
+ try {
493
+ (0, node_fs_1.unlinkSync)((0, node_path_1.join)((0, toolbox_1.getProjectDir)(projectRoot), "auth.json"));
494
+ }
495
+ catch {
496
+ // 文件本来就不在:登出是幂等的,不报错
497
+ }
498
+ }
483
499
  /**
484
500
  * 项目级凭据优先于账号级全局登录态。
485
501
  *
@@ -537,6 +553,32 @@ function clearAdoptedProjectEnv() {
537
553
  function isAdoptedProjectCredential(apiKey, envId) {
538
554
  return adoptedProjectApiKey !== null && apiKey === adoptedProjectApiKey && envId === adoptedProjectEnvId;
539
555
  }
556
+ /**
557
+ * env 里的这一份凭据能不能被 `logout()` 清掉。
558
+ *
559
+ * 能清掉的只有两种情况:是我们从项目级凭据文件回填的,或者它与当前项目级凭据文件里
560
+ * 那一份相同(`auth(login_by_api_key)` 写进程 env 的同时也把同一个 key 落到项目级)。
561
+ * 这两种都能沿 `logout()` 清掉:删项目级文件、再清 env。
562
+ *
563
+ * 其余情况是用户自己在 shell 或宿主配置里设的变量,进程删不掉,只能提示他去源头改。
564
+ */
565
+ function isEnvCredentialRemovableByLogout() {
566
+ const apiKey = getCloudBaseApiKeyFromEnv();
567
+ const envId = process.env.CLOUDBASE_ENV_ID;
568
+ if (!apiKey || !envId) {
569
+ return false;
570
+ }
571
+ if (isAdoptedProjectCredential(apiKey, envId)) {
572
+ return true;
573
+ }
574
+ try {
575
+ const stored = readProjectCredentialFile((0, project_config_js_1.requireProjectRoot)());
576
+ return stored?.apiKey === apiKey && stored?.envId === envId;
577
+ }
578
+ catch {
579
+ return false;
580
+ }
581
+ }
540
582
  /**
541
583
  * 全局凭据文件 `~/.config/.cloudbase/auth.json` 与 CloudBase CLI 共用一份:用户会在终端里
542
584
  * `tcb login`,宿主连接器的登录按钮执行的也是 `tcb login --flow web --yes`,两者都在
@@ -578,6 +620,8 @@ async function peekLoginState(options) {
578
620
  dropStaleCredentialStoreCache();
579
621
  credentialSource = null;
580
622
  const envVarLoginState = normalizeLoginStateFromEnvVars(options);
623
+ // 回填的那份 key 换不出临时密钥时置位:项目级文件里是同一份,不必再试一次
624
+ let projectCredentialSkipped = false;
581
625
  if (envVarLoginState) {
582
626
  // API Key 模式:需要先用 API Key 换取临时密钥
583
627
  if ('_type' in envVarLoginState && envVarLoginState._type === 'api_key') {
@@ -599,16 +643,28 @@ async function peekLoginState(options) {
599
643
  }
600
644
  catch (e) {
601
645
  (0, logger_js_1.debug)("peekLoginState: API Key login failed", { error: e instanceof Error ? e.message : String(e) });
602
- return null;
646
+ // 我们回填的那份失效(密钥被撤、环境被删):先清掉残留,别让它一直占着 env
647
+ // 把状态查询、登出、重新登录全堵死;项目文件本身不动,用户重新登录会覆盖它。
648
+ // 清完继续往下走,让账号级登录态接上。
649
+ if (isAdoptedProjectCredential(envVarLoginState.apiKey, envVarLoginState.envId)) {
650
+ clearAdoptedProjectEnv();
651
+ projectCredentialSkipped = true;
652
+ }
653
+ else {
654
+ // 用户自己在 shell / 宿主配置里设的变量,登不登得上由他那边决定
655
+ return null;
656
+ }
603
657
  }
604
658
  }
605
- // 腾讯云密钥模式:直接返回
606
- (0, logger_js_1.debug)("loginByApiSecret");
607
- credentialSource = "env";
608
- return envVarLoginState;
659
+ if (!projectCredentialSkipped) {
660
+ // 腾讯云密钥模式:直接返回
661
+ (0, logger_js_1.debug)("loginByApiSecret");
662
+ credentialSource = "env";
663
+ return envVarLoginState;
664
+ }
609
665
  }
610
666
  // 项目级凭据优先于账号级登录态(与 readProjectEnvId 同一原则)
611
- const projectLoginState = await resolveProjectLoginState();
667
+ const projectLoginState = projectCredentialSkipped ? null : await resolveProjectLoginState();
612
668
  if (projectLoginState) {
613
669
  credentialSource = "project";
614
670
  return projectLoginState;
@@ -707,21 +763,44 @@ async function ensureLogin(options) {
707
763
  async function getLoginState(options) {
708
764
  return ensureLogin(options);
709
765
  }
766
+ /**
767
+ * 退出登录:只清「当前生效的那一份」。
768
+ *
769
+ * 项目级凭据优先于账号级登录态,所以项目里有 API Key 时,登出只删项目文件、清掉
770
+ * 回填的环境变量,账号登录原样保留(删完它自动接上)。再调一次才轮到全局。
771
+ *
772
+ * 不把 cwd 交给 toolbox 的 `logout()`:那个接口会**无条件先删全局凭据**,
773
+ * 「退出本项目」不该把用户的账号级登录一起注销掉。
774
+ */
710
775
  async function logout(_options) {
711
776
  // 先同步缓存再删:否则 toolbox 会把本进程启动时的陈旧快照写回盘,
712
777
  // 把外部(CLI / 宿主登录按钮)刚写入的凭据一起抹掉。
713
778
  dropStaleCredentialStoreCache();
714
- let cwd;
779
+ const removed = [];
780
+ let projectRoot;
715
781
  try {
716
- cwd = (0, project_config_js_1.requireProjectRoot)();
782
+ projectRoot = (0, project_config_js_1.requireProjectRoot)();
717
783
  }
718
784
  catch {
719
- cwd = undefined;
785
+ // 宿主配置目录等非法项目根:没有项目级凭据
786
+ projectRoot = undefined;
787
+ }
788
+ const envApiKey = getCloudBaseApiKeyFromEnv();
789
+ const hasProjectCredential = !!(projectRoot && readProjectCredentialFile(projectRoot));
790
+ // env 里那份是用户自己设的(进程删不掉)时,既不算项目级生效,也不该顺手注销账号登录
791
+ const projectInEffect = hasProjectCredential && (!envApiKey || isEnvCredentialRemovableByLogout());
792
+ if (projectInEffect && projectRoot) {
793
+ deleteProjectCredentialFile(projectRoot);
794
+ clearAdoptedProjectEnv();
795
+ removed.push("project");
796
+ }
797
+ else if (!envApiKey) {
798
+ await auth.logout();
799
+ removed.push("global");
720
800
  }
721
- await auth.logout(cwd ? { cwd } : undefined);
722
- clearAdoptedProjectEnv();
723
801
  credentialSource = null;
724
802
  resetAuthProgressState();
803
+ return { removed };
725
804
  }
726
805
 
727
806
 
@@ -2880,7 +2959,7 @@ exports.cloudrun = (0, types_js_1.defineModule)({
2880
2959
  "error.envNotInitialized": "当前环境({envId})尚未初始化云托管(CloudRun Env)。不能直接创建服务(CreateCloudRunServer 在无大租户记录时会默认创建到小租户,产生错误的小租户服务与版本)。\n请先开通云托管环境,再重试部署:\n- MCP:manageCloudRun(action=\"initEnv\", envId=\"{envId}\")(异步开通,幂等;开通完成后可用 queryCloudRun(action=\"envStatus\", envId=\"{envId}\") 查询 Status=normal)\n- 或控制台:环境 → 云托管 → 开通(https://tcb.cloud.tencent.com/dev?envId={envId}#/platform-run)\n初始化完成后重新调用 manageCloudRun(action=\"deploy\")。",
2881
2960
  "error.actionFailed": "[{context}] {baseMessage}",
2882
2961
  "error.buildManage": "[manageCloudRun/{action}] {baseMessage}\n建议:{suggestions}",
2883
- "error.deployTaskRunning": "服务 `{serverName}` 当前已有部署任务在执行,请等待现有任务完成后再重试。",
2962
+ "error.deployTaskRunning": "服务 `{serverName}` 当前已有部署任务在执行。**不要再发起 deploy**:先用 queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\") 读取该任务的真实状态——已结束就重新部署,长时间停在非终态则带上该输出联系 CloudBase 平台排查。",
2884
2963
  "error.deployTaskRunningForce": "注意:`force=true` 仅跳过本工具的确认提示,不会取消或覆盖平台上的发布任务。请先用 queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\") 确认任务真实状态:若任务已结束却仍报此错,或任务长时间停在非终态,请携带该输出联系 CloudBase 平台排查该环境的发布任务。",
2885
2964
  "error.vpcRequired": "CreateCloudRunServer 需要有效 VPC:请传 serverConfig.VpcConf(VpcId+SubnetId),或在 initEnv 时传入 vpcId/subnetIds(环境开通后 deploy 会自动从 EnvBaseInfo 回填)。若平台拒绝系统创建网络,必须指定上海地域 VPC。",
2886
2965
  "error.genericRetry": "请检查服务状态、部署参数和目标目录后重试。",
@@ -3070,7 +3149,7 @@ exports.cloudrun = (0, types_js_1.defineModule)({
3070
3149
  "error.envNotInitialized": "The current environment ({envId}) has not initialized CloudRun (CloudRun Env). You cannot create services directly (without a large-tenant record, CreateCloudRunServer defaults to the small tenant, producing wrong small-tenant services and versions).\nProvision the CloudRun env first, then retry the deploy:\n- MCP: manageCloudRun(action=\"initEnv\", envId=\"{envId}\") (async, idempotent; once done, query Status=normal via queryCloudRun(action=\"envStatus\", envId=\"{envId}\"))\n- Or console: Environment → CloudRun → Provision (https://tcb.cloud.tencent.com/dev?envId={envId}#/platform-run)\nAfter initialization, call manageCloudRun(action=\"deploy\") again.",
3071
3150
  "error.actionFailed": "[{context}] {baseMessage}",
3072
3151
  "error.buildManage": "[manageCloudRun/{action}] {baseMessage}\nSuggestions: {suggestions}",
3073
- "error.deployTaskRunning": "Service `{serverName}` already has a deploy task running. Wait for the current task to finish before retrying.",
3152
+ "error.deployTaskRunning": "Service `{serverName}` already has a deploy task running. Do NOT call deploy again: first read the real task state with queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\") - redeploy once it has finished, or contact the CloudBase platform with that output if it stays in a non-terminal state for a long time.",
3074
3153
  "error.deployTaskRunningForce": "Note: `force=true` only skips this tool's confirmation prompt; it does not cancel or override the platform-side deploy task. First confirm the real task state with queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\"): if the task has already finished but this error persists, or it stays in a non-terminal state for a long time, contact the CloudBase platform with that output to investigate this environment's deploy task.",
3075
3154
  "error.vpcRequired": "CreateCloudRunServer requires a valid VPC: pass serverConfig.VpcConf (VpcId+SubnetId), or pass vpcId/subnetIds during initEnv (after provisioning, deploy auto-fills from EnvBaseInfo). If the platform rejects system-created networks, you must specify a Shanghai-region VPC.",
3076
3155
  "error.genericRetry": "Check the service status, deploy parameters, and target directory, then retry.",
@@ -4674,9 +4753,12 @@ exports.env = (0, types_js_1.defineModule)({
4674
4753
  "auth.envReadyWithHint": "环境设置成功,当前环境: {envId}{regionHint}",
4675
4754
  "auth.regionHint": ",地域: {region}",
4676
4755
  "auth.regionHintUnverified": "。未在当前探测地域中确认该 envId,已按唯一 ID 直绑;若后续接口仍指向错误地域,请设置 TCB_REGION 或 queryEnv(action=\"list\", region=...)",
4677
- "auth.logoutNotAllowedApiKey": "当前使用 API Key 认证模式,不支持退出登录。如需切换认证方式,请移除 CLOUDBASE_API_KEY(或兼容的 CLOUDBASE_APIKEY)环境变量后重启。",
4756
+ "auth.logoutNotAllowedApiKey": "当前是 API Key 认证模式,凭据来自环境变量 {varName}。这个变量不是本进程设的、也不由本进程清除,所以没有执行登出。要切换认证方式:在 shell 里 unset {varName},或从启动本 MCP 的客户端配置里移除它,然后重启。",
4678
4757
  "auth.logoutConfirmRequired": "action=logout 时必须传 confirm=\"yes\"",
4679
4758
  "auth.loggedOut": "✅ 已退出登录",
4759
+ "auth.loggedOutProject": "✅ 已移除本项目的 API Key 登录。这台机器上若还有账号登录,它仍然有效;要一起退出请再执行一次 logout。",
4760
+ "auth.apiKeySwitchHintRemovable": "如需改用账号登录,先执行 logout 退出本项目 API Key,再 start_auth 授权。",
4761
+ "auth.apiKeySwitchHintExternal": "这个 key 来自外部环境变量,MCP 无法退出它;如需改用账号登录,先在 shell 或客户端配置里移除 {varName} 并重启。",
4680
4762
  "auth.tempCredNotLoggedIn": "当前未登录,请先完成管理端认证后再获取临时密钥。",
4681
4763
  "auth.tempCredConfirmRequired": "action=get_temp_credentials 时必须显式传 confirm=\"yes\",以确认你要导出当前管理端临时密钥。",
4682
4764
  "auth.tempCredUnsupportedType": "当前登录态不是可导出的临时密钥。仅支持通过 Web / device 登录得到的临时密钥,永久密钥登录不允许导出。",
@@ -4937,9 +5019,12 @@ exports.env = (0, types_js_1.defineModule)({
4937
5019
  "auth.envReadyWithHint": "Environment bound successfully. Current env: {envId}{regionHint}",
4938
5020
  "auth.regionHint": ", region: {region}",
4939
5021
  "auth.regionHintUnverified": ". This envId was not confirmed in the probed regions, so it was bound directly by its unique ID; if later APIs still target the wrong region, set TCB_REGION or use queryEnv(action=\"list\", region=...)",
4940
- "auth.logoutNotAllowedApiKey": "API Key authentication mode is in use and logout is not supported. To switch the authentication method, remove the CLOUDBASE_API_KEY (or the compatible CLOUDBASE_APIKEY) environment variable and restart.",
5022
+ "auth.logoutNotAllowedApiKey": "API Key authentication mode is in use, and the credential comes from the {varName} environment variable. This process neither set nor can remove that variable, so logout was skipped. To switch authentication methods, unset {varName} in your shell or remove it from the MCP client configuration, then restart.",
4941
5023
  "auth.logoutConfirmRequired": "confirm=\"yes\" must be passed when action=logout",
4942
5024
  "auth.loggedOut": "✅ Logged out",
5025
+ "auth.loggedOutProject": "✅ Removed this project's API Key login. An account login on this machine is still active; call logout again to sign out of it.",
5026
+ "auth.apiKeySwitchHintRemovable": "To switch to account login, run logout to remove this project's API Key, then start_auth.",
5027
+ "auth.apiKeySwitchHintExternal": "This key comes from an external environment variable and cannot be removed by MCP. To switch to account login, unset {varName} in your shell or client configuration and restart.",
4943
5028
  "auth.tempCredNotLoggedIn": "Not logged in. Complete management-plane authentication before fetching temporary credentials.",
4944
5029
  "auth.tempCredConfirmRequired": "confirm=\"yes\" must be passed explicitly when action=get_temp_credentials, to confirm that you want to export the current management-plane temporary credentials.",
4945
5030
  "auth.tempCredUnsupportedType": "The current login state is not an exportable temporary credential. Only temporary credentials obtained via Web / device login are supported; permanent key logins cannot be exported.",
@@ -5318,9 +5403,12 @@ exports.functions = (0, types_js_1.defineModule)({
5318
5403
  "\n- `limit`: 分页数量,默认值由后端决定" +
5319
5404
  "\n- `offset`: 分页偏移,从 0 开始" +
5320
5405
  "\n- 示例:`queryFunctions(action=\"listFunctions\", offset=10, limit=10)`" +
5321
- "\n\n**查询 CloudBase 云函数日志**:使用 `action=\"listFunctionLogs\"`,需要提供 `functionName` 参数。" +
5406
+ "\n\n**查询 CloudBase 云函数日志**:`action=\"listFunctionLogs\"`,需要提供 `functionName`。日志从 CLS 检索(底层 GetFunctionLogs 接口已下线)。" +
5322
5407
  "\n- 示例:`queryFunctions(action=\"listFunctionLogs\", functionName=\"my-function\")`" +
5323
- "\n- 如需查看日志详情:`queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
5408
+ "\n- 不传 startTime/endTime 时默认查最近 1 天;跨度不能超过 1 天,单次最多返回 100 条" +
5409
+ "\n- 返回 `hasMore=true` 表示还有更早的日志:把返回的 `context` 原样传回取下一页(`offset` 只在前 100 条内生效)" +
5410
+ "\n- `logs[]` 已解析成 requestId / log / statusCode / retCode / durationMs / memoryMb / timestamp" +
5411
+ "\n- 想看某一次调用的完整过程(START、业务日志、REPORT):`queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
5324
5412
  "\n\n**定时任务 / cron / 定时跑**:使用 `listFunctionTriggers` 查询函数的 timer 触发器配置。" +
5325
5413
  "\n\n**版本与流量路由**:`listVersionByFunction` 列出已发布版本(对齐 tcb fn list-function-versions);`getFunctionAlias` 查看别名/灰度配置(对齐 tcb fn get-route,aliasName 默认 `$DEFAULT`)。" +
5326
5414
  "\n\n**层(Layer)说明**:" +
@@ -5390,8 +5478,8 @@ exports.functions = (0, types_js_1.defineModule)({
5390
5478
  endTimeInvalid: "endTime 格式错误: \"{value}\"。必须使用 YYYY-MM-DD HH:mm:ss 格式(如 2024-01-01 23:59:59)",
5391
5479
  logRangeInvalidDatetime: "startTime 和 endTime 必须是有效的日期时间字符串",
5392
5480
  logRangeTooLong: "startTime 和 endTime 间隔不能超过一天",
5393
- logsInvalidParamTips: "常见原因:\n1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n2. startTime 和 endTime 间隔超过一天\n3. functionName 不存在或格式不正确\n建议:不传 startTime/endTime 时默认查询最近一天的日志。",
5394
- logDetailInvalidParamTips: "常见原因:\n1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n2. startTime 和 endTime 间隔超过一天\n建议:不传 startTime/endTime 时默认查询最近一天的日志。",
5481
+ logsInvalidParamTips: "常见原因:\n1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n2. startTime 和 endTime 间隔超过一天\n3. functionName 与日志里的 function_name 不一致(大小写敏感),或 qualifier 不匹配\n建议:不传 startTime/endTime 时默认查询最近一天的日志。",
5482
+ logDetailInvalidParamTips: "常见原因:\n1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n2. startTime 和 endTime 间隔超过一天\n3. requestId 不在该时间窗口内(日志按请求开始时间归档)\n建议:先用 listFunctionLogs 定位到 requestId 再查详情。",
5395
5483
  paramRequired: "{action} 操作时,{param} 参数是必需的",
5396
5484
  "deployStatus.cloudMode": "{action} 在 cloud mode 下不可用:异步部署任务只由 buildStrategy=cloud/local 的真实部署创建,而这两种策略的真实执行在 cloud mode 下都不支持(需要读取本地构建上下文或本地 Docker),因此不会存在任何 taskId。buildStrategy=image 走同步部署,直接在 manageFunctions 的返回里拿结果,也不需要查询部署状态。如需异步镜像构建部署,请改用本地 MCP 模式。",
5397
5485
  deployTaskNotFound: "未找到部署任务 {taskId};部署任务不存在,可能已过期、不属于当前环境 {envId},或 MCP Server 已重启(任务仅保存在 MCP 进程内存中)。",
@@ -5406,7 +5494,11 @@ exports.functions = (0, types_js_1.defineModule)({
5406
5494
  "reason.viewLogs": "查看该函数的执行日志",
5407
5495
  "reason.updateConfig": "更新该函数配置",
5408
5496
  "reason.checkGateway": "查看该函数是否已暴露网关访问入口",
5409
- "reason.logDetail": "按 requestId 查看单条日志详情",
5497
+ "reason.logDetail": "按 requestId 查看该次调用的完整日志",
5498
+ "reason.nextLogPage": "带上 context 取下一页(更早的)日志",
5499
+ "reason.widenLogRange": "放宽时间范围后重新查询该函数的日志",
5500
+ "reason.createLogService": "开通环境日志服务后再查询函数日志",
5501
+ "reason.searchClsFallback": "改用 queryLogs 直接按同一条 CLS 语句检索",
5410
5502
  "reason.attachLayer": "为该函数追加绑定层",
5411
5503
  "reason.reorderLayers": "整体调整层顺序或绑定列表",
5412
5504
  "reason.layerVersions": "查看某个层的版本列表",
@@ -5451,6 +5543,10 @@ exports.functions = (0, types_js_1.defineModule)({
5451
5543
  gotFunctionDetail: "已获取函数 {fnName} 的详情",
5452
5544
  gotFunctionLogs: "已获取函数 {fnName} 的日志列表",
5453
5545
  gotLogDetail: "已获取 requestId={requestId} 的日志详情",
5546
+ "logs.emptyByFunction": "在 {start} ~ {end} 之间没有查到函数 {fnName} 的日志。常见原因:该时间段内函数没有被调用、日志已过保留期、或 functionName / qualifier 不匹配。可以放宽时间范围后重试。",
5547
+ "logs.emptyByRequest": "在 {start} ~ {end} 之间没有查到 requestId={requestId} 的日志。请确认时间范围覆盖了这次调用(日志按请求开始时间归档),或先用 listFunctionLogs 定位到的 requestId。",
5548
+ "logs.serviceNotEnabled": "当前环境还没有开通日志服务,云函数日志查不到。请先调用 manageLogs(action=\"createLogService\", confirm=true) 开通,再重新查询。",
5549
+ "logs.queryFailed": "查询云函数日志失败:{message}",
5454
5550
  gotFunctionLayers: "已获取函数 {fnName} 当前绑定的层",
5455
5551
  gotLayers: "已获取 {count} 条层记录",
5456
5552
  gotLayerVersions: "已获取层 {layerName} 的版本列表",
@@ -5531,16 +5627,17 @@ exports.functions = (0, types_js_1.defineModule)({
5531
5627
  "schema.create.layers": "Layer 配置",
5532
5628
  "schema.manageLayer.name": "层名称",
5533
5629
  "schema.manageLayer.version": "层版本号",
5534
- "schema.query.action": "只读操作类型:\n- `listFunctions`: 列出所有 CloudBase 云函数\n- `getFunctionDetail`: 获取 CloudBase 云函数详情(需要 functionName)\n- `listFunctionLogs`: 查询 CloudBase 云函数执行日志(需要 functionName)\n- `getFunctionLogDetail`: 获取日志详情(需要 requestId)\n- `listFunctionLayers`: 列出函数绑定的层\n- `listLayers`: 列出所有层(账号级视图,含其他环境创建的层)\n- `listLayerVersions`: 列出层的版本(注意:是 Versions 不是 Version;账号级视图)\n- `getLayerVersionDetail`: 获取层版本详情(账号级视图)\n- `listFunctionTriggers`: 列出函数触发器(用于查看定时任务 / cron / timer 配置)\n- `getFunctionDownloadUrl`: 获取函数代码下载地址\n- `getFunctionDeployStatus`: 按 taskId 查询异步部署状态、阶段进度和最终结果。返回 data.build(构建子状态)、data.deploy(部署子状态)、data.progress(阶段事件);status=running 时 data.result 与 data.error 一律为 null,不得报告部署完成。调用方必须持续轮询直到 status=succeeded/failed;status=expired 表示任务超过最长保留时间(2 小时)被终结,云端可能仍在部署,需用 getFunctionDetail 确认。任务只保存在 MCP 进程内存中,过期或 MCP Server 重启后返回 errorCode=DEPLOY_TASK_NOT_FOUND;任务按环境隔离,只能查到当前环境自己发起的部署。cloud mode 下本 action 不可用:异步任务只由 buildStrategy=cloud/local 的真实部署创建,而这两种策略在 cloud mode 下都不支持真实执行,image 策略则走同步部署不产生 taskId。\n- `listVersionByFunction`: 列出函数已发布版本(对齐 tcb fn list-function-versions / SDK listVersionByFunction;需要 functionName)\n- `getFunctionAlias`: 查询函数别名与流量路由(对齐 tcb fn get-route / SDK getFunctionAlias;需要 functionName;aliasName 默认 $DEFAULT)\n- `getFunctionUploadUrl`: 获取函数代码包的 COS 预签名上传地址(ZIP 两段式部署阶段 A):PUT 代码 zip 到 uploadUrl(uploadHeaders 非空时须随请求携带对应请求头),再调用 manageFunctions 的 createFunction/updateFunctionCode 并传 code 三元组(阶段 B)。functionName 可选,仅用于生成上传对象 key。返回的 uploadUrl 含凭据签名,不得写入日志或持久化;阶段 B 会校验三元组的桶与地域必须是本环境自有存储桶,因此该地址只能用于取到它的那个环境;地址默认 300 秒内有效",
5630
+ "schema.query.action": "只读操作类型:\n- `listFunctions`: 列出所有 CloudBase 云函数\n- `getFunctionDetail`: 获取 CloudBase 云函数详情(需要 functionName)\n- `listFunctionLogs`: 查询云函数执行日志(需要 functionName;走 CLS 检索,默认最近 1 天,单次最多 100 条,翻页传 context)\n- `getFunctionLogDetail`: 获取某次调用的完整日志序列(需要 requestId,取自 listFunctionLogs 的 logs[].requestId)\n- `listFunctionLayers`: 列出函数绑定的层\n- `listLayers`: 列出所有层(账号级视图,含其他环境创建的层)\n- `listLayerVersions`: 列出层的版本(注意:是 Versions 不是 Version;账号级视图)\n- `getLayerVersionDetail`: 获取层版本详情(账号级视图)\n- `listFunctionTriggers`: 列出函数触发器(用于查看定时任务 / cron / timer 配置)\n- `getFunctionDownloadUrl`: 获取函数代码下载地址\n- `getFunctionDeployStatus`: 按 taskId 查询异步部署状态、阶段进度和最终结果。返回 data.build(构建子状态)、data.deploy(部署子状态)、data.progress(阶段事件);status=running 时 data.result 与 data.error 一律为 null,不得报告部署完成。调用方必须持续轮询直到 status=succeeded/failed;status=expired 表示任务超过最长保留时间(2 小时)被终结,云端可能仍在部署,需用 getFunctionDetail 确认。任务只保存在 MCP 进程内存中,过期或 MCP Server 重启后返回 errorCode=DEPLOY_TASK_NOT_FOUND;任务按环境隔离,只能查到当前环境自己发起的部署。cloud mode 下本 action 不可用:异步任务只由 buildStrategy=cloud/local 的真实部署创建,而这两种策略在 cloud mode 下都不支持真实执行,image 策略则走同步部署不产生 taskId。\n- `listVersionByFunction`: 列出函数已发布版本(对齐 tcb fn list-function-versions / SDK listVersionByFunction;需要 functionName)\n- `getFunctionAlias`: 查询函数别名与流量路由(对齐 tcb fn get-route / SDK getFunctionAlias;需要 functionName;aliasName 默认 $DEFAULT)\n- `getFunctionUploadUrl`: 获取函数代码包的 COS 预签名上传地址(ZIP 两段式部署阶段 A):PUT 代码 zip 到 uploadUrl(uploadHeaders 非空时须随请求携带对应请求头),再调用 manageFunctions 的 createFunction/updateFunctionCode 并传 code 三元组(阶段 B)。functionName 可选,仅用于生成上传对象 key。返回的 uploadUrl 含凭据签名,不得写入日志或持久化;阶段 B 会校验三元组的桶与地域必须是本环境自有存储桶,因此该地址只能用于取到它的那个环境;地址默认 300 秒内有效",
5535
5631
  "schema.query.functionName": "CloudBase 云函数名称。`getFunctionDetail`、`listFunctionLogs`、`listFunctionLayers`、`listFunctionTriggers`、`getFunctionDownloadUrl`、`listVersionByFunction`、`getFunctionAlias` 时必填;`getFunctionUploadUrl` 可选(仅用于生成上传对象 key)",
5536
- "schema.query.limit": "分页数量(limit)。列表类 action 可选,默认值由后端决定",
5537
- "schema.query.offset": "分页偏移(offset)。列表类 action 可选,默认 0",
5632
+ "schema.query.limit": "分页数量(limit)。列表类 action 可选,默认值由后端决定;日志类 action 单次最多 100 条,默认 20",
5633
+ "schema.query.offset": "分页偏移(offset)。列表类 action 可选,默认 0。日志类 action 只在前 100 条内生效,更早的日志请用返回的 context 翻页",
5538
5634
  "schema.query.codeSecret": "代码保护密钥,用于解密函数代码",
5539
5635
  "schema.query.revealEnvValues": "getFunctionDetail / listFunctionTriggers / listFunctionLayers 时是否返回环境变量明文值。默认 false:Value 脱敏为 ***,仅保留 Key 与 ValueLength,足以确认配置了哪些变量及变更是否生效;true 时返回明文,敏感变量会进入模型上下文,谨慎使用。如需查看明文,建议优先使用控制台或 CLI",
5540
- "schema.query.startTime": "日志查询开始时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00)。与 endTime 间隔不能超过一天。不传时默认查询最近一天",
5541
- "schema.query.endTime": "日志查询结束时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 23:59:59)。与 startTime 间隔不能超过一天。不传时默认为当前时间",
5542
- "schema.query.requestId": "日志请求 ID。`getFunctionLogDetail` 操作必填,可从 `listFunctionLogs` 结果中获取",
5543
- "schema.query.qualifier": "函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选",
5636
+ "schema.query.startTime": "日志查询开始时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00,按北京时间理解)。与 endTime 间隔不能超过一天。不传时默认查询最近一天",
5637
+ "schema.query.endTime": "日志查询结束时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 23:59:59,按北京时间理解)。与 startTime 间隔不能超过一天。不传时默认为当前时间",
5638
+ "schema.query.requestId": "日志请求 ID。`getFunctionLogDetail` 操作必填;取自 `listFunctionLogs` 返回的 logs[].requestId,也可传具体业务日志里出现的 RequestId",
5639
+ "schema.query.qualifier": "函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选,作为 CLS 检索条件",
5640
+ "schema.query.context": "日志翻页游标。把上一次 `listFunctionLogs` 返回的 `context` 原样传回即可取下一页;不要自己构造,null 表示没有更多",
5544
5641
  "schema.query.runtime": "层查询的运行时筛选,如 Nodejs18.15",
5545
5642
  "schema.query.searchKey": "层名称搜索关键字",
5546
5643
  "schema.query.layerName": "层名称。`listLayerVersions`、`getLayerVersionDetail` 操作必填。层为账号级共享命名空间;推荐固定格式 `{layerName}_{当前envId}`(如 common_cloud1-d9ghadgak3edf6b36)",
@@ -5599,9 +5696,12 @@ exports.functions = (0, types_js_1.defineModule)({
5599
5696
  "\n- `limit`: page size, the default is decided by the backend" +
5600
5697
  "\n- `offset`: pagination offset, starting from 0" +
5601
5698
  "\n- Example: `queryFunctions(action=\"listFunctions\", offset=10, limit=10)`" +
5602
- "\n\n**Querying cloud function logs**: use `action=\"listFunctionLogs\"`, which requires the `functionName` parameter." +
5699
+ "\n\n**Querying cloud function logs**: use `action=\"listFunctionLogs\"`, which requires `functionName`. Logs are retrieved from CLS (the underlying GetFunctionLogs API has been retired)." +
5603
5700
  "\n- Example: `queryFunctions(action=\"listFunctionLogs\", functionName=\"my-function\")`" +
5604
- "\n- To view log details: `queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
5701
+ "\n- Without startTime/endTime it queries the last day; the span must not exceed 1 day and a single call returns at most 100 entries" +
5702
+ "\n- `hasMore=true` means older logs exist: pass the returned `context` back unchanged to fetch the next page (`offset` only works within the first 100 entries)" +
5703
+ "\n- Each `logs[]` item is parsed into requestId / log / statusCode / retCode / durationMs / memoryMb / timestamp" +
5704
+ "\n- To see the full sequence of one invocation (START, business logs, REPORT): `queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
5605
5705
  "\n\n**Scheduled tasks / cron / timers**: use `listFunctionTriggers` to query a function's timer trigger configuration." +
5606
5706
  "\n\n**Versions and traffic routing**: `listVersionByFunction` lists published versions (aligns with tcb fn list-function-versions); `getFunctionAlias` views alias / canary config (aligns with tcb fn get-route; aliasName defaults to `$DEFAULT`)." +
5607
5707
  "\n\n**Layers**:" +
@@ -5671,8 +5771,8 @@ exports.functions = (0, types_js_1.defineModule)({
5671
5771
  endTimeInvalid: "Invalid endTime: \"{value}\". It must use the YYYY-MM-DD HH:mm:ss format (e.g. 2024-01-01 23:59:59)",
5672
5772
  logRangeInvalidDatetime: "startTime and endTime must be valid datetime strings",
5673
5773
  logRangeTooLong: "The interval between startTime and endTime must not exceed one day",
5674
- logsInvalidParamTips: "Common causes:\n1. startTime/endTime format error; it must be YYYY-MM-DD HH:mm:ss (e.g. 2024-01-01 00:00:00); ISO 8601 or timestamps are not supported\n2. The interval between startTime and endTime exceeds one day\n3. functionName does not exist or is malformed\nTip: without startTime/endTime, logs from the most recent day are queried by default.",
5675
- logDetailInvalidParamTips: "Common causes:\n1. startTime/endTime format error; it must be YYYY-MM-DD HH:mm:ss (e.g. 2024-01-01 00:00:00); ISO 8601 or timestamps are not supported\n2. The interval between startTime and endTime exceeds one day\nTip: without startTime/endTime, logs from the most recent day are queried by default.",
5774
+ logsInvalidParamTips: "Common causes:\n1. startTime/endTime format error; it must be YYYY-MM-DD HH:mm:ss (e.g. 2024-01-01 00:00:00); ISO 8601 or timestamps are not supported\n2. The interval between startTime and endTime exceeds one day\n3. functionName does not match function_name in the logs (case-sensitive), or qualifier does not match\nTip: without startTime/endTime, logs from the most recent day are queried by default.",
5775
+ logDetailInvalidParamTips: "Common causes:\n1. startTime/endTime format error; it must be YYYY-MM-DD HH:mm:ss (e.g. 2024-01-01 00:00:00); ISO 8601 or timestamps are not supported\n2. The interval between startTime and endTime exceeds one day\n3. requestId falls outside that window (logs are filed by request start time)\nTip: locate the requestId with listFunctionLogs first.",
5676
5776
  paramRequired: "The {param} parameter is required for the {action} action",
5677
5777
  "deployStatus.cloudMode": "{action} is unavailable in cloud mode: async deployment tasks are only created by real deployments with buildStrategy=cloud/local, and real execution of both strategies is unsupported in cloud mode (they need the local build context or local Docker), so no taskId can exist. buildStrategy=image uses synchronous deployment and returns the result directly from manageFunctions, so no deployment status query is needed. For async image build deployment, switch to local MCP mode.",
5678
5778
  deployTaskNotFound: "Deployment task {taskId} not found; the task does not exist — it may have expired, belong to a different environment than {envId}, or the MCP Server was restarted (tasks are only kept in MCP process memory).",
@@ -5687,7 +5787,11 @@ exports.functions = (0, types_js_1.defineModule)({
5687
5787
  "reason.viewLogs": "View the execution logs of this function",
5688
5788
  "reason.updateConfig": "Update this function's configuration",
5689
5789
  "reason.checkGateway": "Check whether this function has an exposed gateway access URL",
5690
- "reason.logDetail": "View a single log entry by requestId",
5790
+ "reason.logDetail": "View the full logs of that invocation by requestId",
5791
+ "reason.nextLogPage": "Pass context back to fetch the next (older) page of logs",
5792
+ "reason.widenLogRange": "Query this function's logs again with a wider time range",
5793
+ "reason.createLogService": "Enable the environment log service before querying function logs",
5794
+ "reason.searchClsFallback": "Search CLS directly with queryLogs using the same query string",
5691
5795
  "reason.attachLayer": "Attach an additional layer to this function",
5692
5796
  "reason.reorderLayers": "Adjust layer order or the binding list as a whole",
5693
5797
  "reason.layerVersions": "View the version list of a layer",
@@ -5732,6 +5836,10 @@ exports.functions = (0, types_js_1.defineModule)({
5732
5836
  gotFunctionDetail: "Retrieved details of function {fnName}",
5733
5837
  gotFunctionLogs: "Retrieved the log list of function {fnName}",
5734
5838
  gotLogDetail: "Retrieved the log detail for requestId={requestId}",
5839
+ "logs.emptyByFunction": "No logs found for function {fnName} between {start} and {end}. Common causes: the function was not invoked in that window, the logs have passed their retention period, or functionName / qualifier does not match. Widen the time range and retry.",
5840
+ "logs.emptyByRequest": "No logs found for requestId={requestId} between {start} and {end}. Make sure the window covers that invocation (logs are filed by request start time), or use a requestId located through listFunctionLogs.",
5841
+ "logs.serviceNotEnabled": "The log service is not enabled for this environment, so function logs cannot be read. Call manageLogs(action=\"createLogService\", confirm=true) first, then query again.",
5842
+ "logs.queryFailed": "Failed to query function logs: {message}",
5735
5843
  gotFunctionLayers: "Retrieved the layers currently bound to function {fnName}",
5736
5844
  gotLayers: "Retrieved {count} layer records",
5737
5845
  gotLayerVersions: "Retrieved the version list of layer {layerName}",
@@ -5812,16 +5920,17 @@ exports.functions = (0, types_js_1.defineModule)({
5812
5920
  "schema.create.layers": "Layer configuration",
5813
5921
  "schema.manageLayer.name": "Layer name",
5814
5922
  "schema.manageLayer.version": "Layer version number",
5815
- "schema.query.action": "Read-only action type:\n- `listFunctions`: list all CloudBase cloud functions\n- `getFunctionDetail`: get function details (requires functionName)\n- `listFunctionLogs`: query function execution logs (requires functionName)\n- `getFunctionLogDetail`: get log details (requires requestId)\n- `listFunctionLayers`: list layers bound to a function\n- `listLayers`: list all layers (account-level view, including layers from other environments)\n- `listLayerVersions`: list layer versions (Versions, not Version; account-level view)\n- `getLayerVersionDetail`: get layer version details (account-level view)\n- `listFunctionTriggers`: list function triggers (scheduled task / cron / timer configuration)\n- `getFunctionDownloadUrl`: get the function code download URL\n- `getFunctionDeployStatus`: query async deployment status, stage progress, and final result by taskId. Returns data.build, data.deploy, and data.progress. While status=running, data.result and data.error are null and deployment must not be reported as complete. Keep polling until status=succeeded/failed. status=expired means the task exceeded the 2-hour retention limit; cloud deployment may still be running, so confirm with getFunctionDetail. Tasks exist only in MCP process memory; expired tasks or a restarted MCP Server return errorCode=DEPLOY_TASK_NOT_FOUND. Tasks are isolated by environment. This action is unavailable in cloud mode because real cloud/local builds cannot run there, while image deployment is synchronous and creates no taskId.\n- `listVersionByFunction`: list published function versions (aligns with tcb fn list-function-versions / SDK listVersionByFunction; requires functionName)\n- `getFunctionAlias`: get function alias and traffic routing (aligns with tcb fn get-route / SDK getFunctionAlias; requires functionName; aliasName defaults to $DEFAULT)\n- `getFunctionUploadUrl`: get a COS presigned upload URL for a function code package (two-phase ZIP deployment phase A): PUT the code zip to uploadUrl (include the corresponding request headers when uploadHeaders is non-empty), then call manageFunctions createFunction/updateFunctionCode with the code triplet (phase B). functionName is optional and only shapes the upload object key. The uploadUrl carries credential-bound signatures; never log or persist it; phase B verifies that the triplet's bucket and region are this environment's own bucket, so an address is only usable in the environment it was minted for; the address is valid for 300 seconds by default",
5923
+ "schema.query.action": "Read-only action type:\n- `listFunctions`: list all CloudBase cloud functions\n- `getFunctionDetail`: get function details (requires functionName)\n- `listFunctionLogs`: query function execution logs (requires functionName; retrieved via CLS, defaults to the last day, at most 100 entries per call, pass context to page)\n- `getFunctionLogDetail`: get the full log sequence of one invocation (requires requestId, taken from logs[].requestId of listFunctionLogs)\n- `listFunctionLayers`: list layers bound to a function\n- `listLayers`: list all layers (account-level view, including layers from other environments)\n- `listLayerVersions`: list layer versions (Versions, not Version; account-level view)\n- `getLayerVersionDetail`: get layer version details (account-level view)\n- `listFunctionTriggers`: list function triggers (scheduled task / cron / timer configuration)\n- `getFunctionDownloadUrl`: get the function code download URL\n- `getFunctionDeployStatus`: query async deployment status, stage progress, and final result by taskId. Returns data.build, data.deploy, and data.progress. While status=running, data.result and data.error are null and deployment must not be reported as complete. Keep polling until status=succeeded/failed. status=expired means the task exceeded the 2-hour retention limit; cloud deployment may still be running, so confirm with getFunctionDetail. Tasks exist only in MCP process memory; expired tasks or a restarted MCP Server return errorCode=DEPLOY_TASK_NOT_FOUND. Tasks are isolated by environment. This action is unavailable in cloud mode because real cloud/local builds cannot run there, while image deployment is synchronous and creates no taskId.\n- `listVersionByFunction`: list published function versions (aligns with tcb fn list-function-versions / SDK listVersionByFunction; requires functionName)\n- `getFunctionAlias`: get function alias and traffic routing (aligns with tcb fn get-route / SDK getFunctionAlias; requires functionName; aliasName defaults to $DEFAULT)\n- `getFunctionUploadUrl`: get a COS presigned upload URL for a function code package (two-phase ZIP deployment phase A): PUT the code zip to uploadUrl (include the corresponding request headers when uploadHeaders is non-empty), then call manageFunctions createFunction/updateFunctionCode with the code triplet (phase B). functionName is optional and only shapes the upload object key. The uploadUrl carries credential-bound signatures; never log or persist it; phase B verifies that the triplet's bucket and region are this environment's own bucket, so an address is only usable in the environment it was minted for; the address is valid for 300 seconds by default",
5816
5924
  "schema.query.functionName": "CloudBase cloud function name. Required for `getFunctionDetail`, `listFunctionLogs`, `listFunctionLayers`, `listFunctionTriggers`, `getFunctionDownloadUrl`, `listVersionByFunction`, and `getFunctionAlias`. Optional for `getFunctionUploadUrl` (only used to generate the upload object key).",
5817
- "schema.query.limit": "Page size (limit). Optional for list actions; the backend determines the default.",
5818
- "schema.query.offset": "Pagination offset. Optional for list actions; defaults to 0.",
5925
+ "schema.query.limit": "Page size (limit). Optional for list actions; the backend determines the default. Log actions return at most 100 entries and default to 20.",
5926
+ "schema.query.offset": "Pagination offset. Optional for list actions; defaults to 0. For log actions it only works within the first 100 entries — use the returned context to page further back.",
5819
5927
  "schema.query.codeSecret": "Code protection secret used to decrypt function code.",
5820
5928
  "schema.query.revealEnvValues": "Whether `getFunctionDetail` / `listFunctionTriggers` / `listFunctionLayers` returns plaintext environment variable values. Defaults to false: Value is masked as ***, while Key and ValueLength remain, which is sufficient to verify configured variables and changes. true returns plaintext and may expose secrets to the model context; use cautiously. Prefer the console or CLI for plaintext values.",
5821
- "schema.query.startTime": "Log query start time in YYYY-MM-DD HH:mm:ss format (for example, 2024-01-01 00:00:00). The interval to endTime must not exceed one day. Defaults to the most recent day when omitted.",
5822
- "schema.query.endTime": "Log query end time in YYYY-MM-DD HH:mm:ss format (for example, 2024-01-01 23:59:59). The interval from startTime must not exceed one day. Defaults to the current time when omitted.",
5823
- "schema.query.requestId": "Log request ID. Required for `getFunctionLogDetail`; obtain it from `listFunctionLogs`.",
5824
- "schema.query.qualifier": "Function version alias, such as $LATEST or $DEFAULT. Optional for log queries.",
5929
+ "schema.query.startTime": "Log query start time in YYYY-MM-DD HH:mm:ss format (for example, 2024-01-01 00:00:00, interpreted as Beijing time). The interval to endTime must not exceed one day. Defaults to the most recent day when omitted.",
5930
+ "schema.query.endTime": "Log query end time in YYYY-MM-DD HH:mm:ss format (for example, 2024-01-01 23:59:59, interpreted as Beijing time). The interval from startTime must not exceed one day. Defaults to the current time when omitted.",
5931
+ "schema.query.requestId": "Log request ID. Required for `getFunctionLogDetail`; take it from logs[].requestId returned by `listFunctionLogs`, or use the RequestId printed in a business log line.",
5932
+ "schema.query.qualifier": "Function version alias, such as $LATEST or $DEFAULT. Optional for log queries; used as a CLS filter.",
5933
+ "schema.query.context": "Log pagination cursor. Pass the `context` returned by the previous `listFunctionLogs` call back unchanged to fetch the next page; never construct it yourself. null means there are no more results.",
5825
5934
  "schema.query.runtime": "Runtime filter for layer queries, such as Nodejs18.15.",
5826
5935
  "schema.query.searchKey": "Layer name search keyword.",
5827
5936
  "schema.query.layerName": "Layer name. Required for `listLayerVersions` and `getLayerVersionDetail`. Layers use an account-level shared namespace; use the fixed format `{layerName}_{current envId}` (for example, common_cloud1-d9ghadgak3edf6b36).",
@@ -5887,9 +5996,9 @@ exports.gateway = void 0;
5887
5996
  const types_js_1 = __webpack_require__(9921);
5888
5997
  exports.gateway = (0, types_js_1.defineModule)({
5889
5998
  "query.title": "查询 CloudBase 网关",
5890
- "query.description": "CloudBase HTTP 网关统一只读入口(Domain/Route)。查询域名下路径路由及其上游:WEB_SCF/SCF=云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。主键为 Domain + Path;listRoutes / getRoute / listCustomDomains / getPrivilege。getPrivilege 查询 HTTP 网关总开关(enableService)与访问鉴权(enableAuth)状态。实现自定义域名访问前,先 listCustomDomains:若已有自定义域名,优先 createRoute 挂路由(无需证书 ID);仅在没有可用自定义域名时才 bindCustomDomain。",
5999
+ "query.description": "CloudBase HTTP 网关统一只读入口(Domain/Route)。查询域名下路径路由及其上游:WEB_SCF/SCF=云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器。主键为 Domain + Path;listRoutes / getRoute / listCustomDomains / getPrivilege / cachePurgeTask。getPrivilege 查询 HTTP 网关总开关(enableService)与访问鉴权(enableAuth)状态。cachePurgeTask 查询域名缓存清除任务(先 manageGateway purgeCache 拿 TaskId)。实现自定义域名访问前,先 listCustomDomains:若已有自定义域名,优先 createRoute 挂路由(无需证书 ID);仅在没有可用自定义域名时才 bindCustomDomain。",
5891
6000
  "manage.title": "管理 CloudBase 网关",
5892
- "manage.description": "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path 转到上游;enableRoute/disableRoute 启用或禁用已有路由(底层 ModifyHTTPServiceRoute 的 Routes[].Enable,不是 ModifyGatewayRoute)。未传 domain 时用 DomainType=HTTPSERVICE 的 IsDefault 默认 HTTP 域名(形如 *.{region}.app.tcloudbase.com),不会使用静态托管 CDN 域名(*.tcloudbaseapp.com,DomainType=STATIC_STORE)。这是网关默认域上的路径路由,不是 STATIC_STORE 上游绑定;STATIC_STORE 上游必须显式传 upstreamResourceType=STATIC_STORE。关闭静态托管默认域名(*.tcloudbaseapp.com):先 queryGateway(listRoutes) 找到 DomainType=STATIC_STORE 且 IsDefault=true 的 domain,再 manageGateway(action=\"disableRoute\", domain=该域名, path=\"/\");勿用 manageHosting。创建后可用 queryGateway(action=\"listRoutes\") 核对 Domain / DomainType / Path / UpstreamResourceType。上游类型只用一个参数 upstreamResourceType(也可写在 route.upstreamResourceType,route 优先):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器;配合 targetName 或 route.serviceName(云函数名/云托管服务名/静态托管实例名,常见 staticstore)。createRoute 只建网关入口,不改上游权限。enablePathTransmission:默认 false 剥触发路径前缀;true 透传完整路径(CBR 多路由、WEB_SCF 自管子路径常需 true;STATIC_STORE 自定义触发路径映射站点根通常 false)。⚠️ 自定义域名访问:若环境已有自定义域名(先 queryGateway listCustomDomains),优先 createRoute 并显式传入该 domain,无需 certificateId;仅首次绑定全新自定义域名时用 bindCustomDomain(certificateId 可选:未传时按域名自动检索证书,单证书自动选用、多证书返回选择指引)。createRoute / bindCustomDomain 创建前会调用 VerifyHTTPServiceRoute 做归属权等预检(探测→创建);失败时返回 data.checks 与 DNS TXT 指引,配置后重试。CORS/安全域名(浏览器跨域白名单,与本工具的网关自定义域名无关)用 manageEnv(action=addSecurityDomain/removeSecurityDomain)。enableService/authSwitch:HTTP 网关总开关与访问鉴权开关;createRoute 后若访问报 HTTPSERVICE_NONACTIVATED,通常是总开关未开启(用 queryGateway getPrivilege 查询、enableService 开启)。",
6001
+ "manage.description": "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path 转到上游;enableRoute/disableRoute 启用或禁用已有路由(底层 ModifyHTTPServiceRoute 的 Routes[].Enable,不是 ModifyGatewayRoute)。未传 domain 时用 DomainType=HTTPSERVICE 的 IsDefault 默认 HTTP 域名(形如 *.{region}.app.tcloudbase.com),不会使用静态托管 CDN 域名(*.tcloudbaseapp.com,DomainType=STATIC_STORE)。这是网关默认域上的路径路由,不是 STATIC_STORE 上游绑定;STATIC_STORE 上游必须显式传 upstreamResourceType=STATIC_STORE。关闭静态托管默认域名(*.tcloudbaseapp.com):先 queryGateway(listRoutes) 找到 DomainType=STATIC_STORE 且 IsDefault=true 的 domain,再 manageGateway(action=\"disableRoute\", domain=该域名, path=\"/\");勿用 manageHosting。创建后可用 queryGateway(action=\"listRoutes\") 核对 Domain / DomainType / Path / UpstreamResourceType。上游类型只用一个参数 upstreamResourceType(也可写在 route.upstreamResourceType,route 优先):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器;配合 targetName 或 route.serviceName(云函数名/云托管服务名/静态托管实例名,常见 staticstore/云存储实例名)。createRoute 只建网关入口,不改上游权限。enablePathTransmission:默认 false 剥触发路径前缀;true 透传完整路径(CBR 多路由、WEB_SCF 自管子路径常需 true;STATIC_STORE 自定义触发路径映射站点根通常 false)。⚠️ 自定义域名访问:若环境已有自定义域名(先 queryGateway listCustomDomains),优先 createRoute 并显式传入该 domain,无需 certificateId;仅首次绑定全新自定义域名时用 bindCustomDomain(certificateId 可选:未传时按域名自动检索证书,单证书自动选用、多证书返回选择指引)。createRoute / bindCustomDomain 创建前会调用 VerifyHTTPServiceRoute 做归属权等预检(探测→创建);失败时返回 data.checks 与 DNS TXT 指引,配置后重试。CORS/安全域名(浏览器跨域白名单,与本工具的网关自定义域名无关)用 manageEnv(action=addSecurityDomain/removeSecurityDomain)。enableService/authSwitch:HTTP 网关总开关与访问鉴权开关;createRoute 后若访问报 HTTPSERVICE_NONACTIVATED,通常是总开关未开启(用 queryGateway getPrivilege 查询、enableService 开启)。purgeCache:清除域名缓存(CDN/EO,按 URL/前缀/整站),返回 TaskId 后用 queryGateway cachePurgeTask 查进度。路由级还支持 enableSafeDomain(跨域/合法域名校验)、pathRewrite(路径重写)、qpsPolicy(限频)、extension(自定义请求/响应头与缓存规则);域名级支持 protocol 与 domainExtension。updateRoute 只传要改的字段即可,未传的 CORS/重写/限频/扩展配置会保持原值。",
5893
6002
  "verify.failWithDns": "HTTP 服务域名配置预检未通过:域名归属权校验失败。请按 data.checks 中 ownership.dnsRecords 配置 DNS TXT 记录后重试。",
5894
6003
  "verify.fail": "HTTP 服务域名配置预检未通过。请根据 data.checks 修复问题后重试。",
5895
6004
  "verify.failWithDnsReason": "配置 DNS TXT(主机记录/记录类型/记录值见 data.checks[].dnsRecords)后重新调用本 action",
@@ -5907,7 +6016,7 @@ exports.gateway = (0, types_js_1.defineModule)({
5907
6016
  "privilege.nonActivatedHint": ";若路由创建成功但访问报 HTTPSERVICE_NONACTIVATED(403),请先开启 HTTP 网关",
5908
6017
  "privilege.enableServiceReason": "开启 HTTP 网关总开关(EnableService),开启后路由即可通过默认域访问",
5909
6018
  "defaultDomain.notReady": "环境默认 HTTP 访问域名未就绪或未开通。请先在控制台开通 HTTP 访问服务,或用 queryGateway(action=\"listRoutes\") 确认 Domains 中是否存在 DomainType=HTTPSERVICE 且 IsDefault=true 的域名(形如 *.{region}.app.tcloudbase.com);不要使用静态托管域名(*.tcloudbaseapp.com)。也可以显式传入 domain 后重试 createRoute/updateRoute/deleteRoute。",
5910
- "upstreamTypeRequired": "必须提供 upstreamResourceType(或 route.upstreamResourceType):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。禁止仅凭 targetName/serviceName 推断上游类型。",
6019
+ "upstreamTypeRequired": "必须提供 upstreamResourceType(或 route.upstreamResourceType):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器。禁止仅凭 targetName/serviceName 推断上游类型。",
5911
6020
  "upstreamNameRequired": "必须提供 targetName 或 route.serviceName 作为上游资源名称:云函数名、云托管服务名,或静态托管实例名(常见为 staticstore)。",
5912
6021
  "toggle.pathRequired": "action=enableRoute/disableRoute 时必须提供 path 或 route.path(例如 \"/\" 或 \"/api\")",
5913
6022
  "toggle.notFound": "未找到路径 {path} 的路由{domainSuffix}{upstreamSuffix}。请先用 queryGateway(action=\"listRoutes\") 确认 Domain / Path,再调用 enableRoute/disableRoute。关闭静态托管默认域名(*.tcloudbaseapp.com)时,请显式传 domain=该 STATIC_STORE IsDefault 域名,并通常 path=\"/\"。",
@@ -5973,14 +6082,32 @@ exports.gateway = (0, types_js_1.defineModule)({
5973
6082
  "authSwitch.onMessage": "HTTP 访问服务鉴权开启成功",
5974
6083
  "authSwitch.offMessage": "HTTP 访问服务鉴权关闭成功",
5975
6084
  "authSwitch.verifyReason": "复核 HTTP 网关总开关与访问鉴权状态",
6085
+ "purgeCache.domainRequired": "action=purgeCache 时必须提供 domain",
6086
+ "purgeCache.targetsRequired": "action=purgeCache 时必须提供 targets:PURGE_URL 传完整 URL、PURGE_PREFIX 传路径前缀、PURGE_HOST 传待刷新的域名",
6087
+ "purgeCache.message": "已提交 {count} 个目标的缓存清除任务;CDN/EO 刷新通常数秒到数分钟,请用 queryGateway(action=\"cachePurgeTask\") 按 TaskId 查进度",
6088
+ "purgeCache.verifyReason": "查询缓存清除任务状态",
6089
+ "query.cachePurgeTask": "已获取 {count} 条缓存清除任务记录",
6090
+ "query.cachePurgeTaskDomainRequired": "action=cachePurgeTask 时必须提供 domain",
6091
+ "cache.domainNotFound": "未找到域名 {domain} 的接入配置,无法确定缓存层。请先用 queryGateway(action=\"listRoutes\") 确认域名。",
6092
+ "cache.notManaged": "域名 {domain} 的接入方式是 {accessType},CloudBase 侧没有该域名的 CDN/EO 缓存,缓存刷新不适用(DIRECT 直连与 CUSTOM 自有 CDN 的缓存不由平台托管)。如需刷新,请先把域名切到 CDN 或 EO 接入。",
6093
+ "cache.typeMismatch": "域名 {domain} 的接入方式是 {accessType},不能按 cacheType={requested} 刷新。省略 cacheType 就会按接入方式自动匹配。",
5976
6094
  // Input schema parameter descriptions
5977
- "schema.query.action": "只读操作类型:listRoutes、getRoute、listCustomDomains、getPrivilege。" +
6095
+ "schema.query.action": "只读操作类型:listRoutes、getRoute、listCustomDomains、getPrivilege、cachePurgeTask。" +
5978
6096
  "getPrivilege 无需其他参数,直接返回 HTTP 网关总开关与访问鉴权状态。" +
6097
+ "cachePurgeTask 需要 domain(可选 cacheType/taskId/purgeType/startTime/endTime/offset/limit)。" +
5979
6098
  "自定义域名访问场景先 listCustomDomains 确认是否已有域名可复用。",
5980
6099
  "schema.query.targetName": "上游资源名过滤(UpstreamResourceName):云函数名、云托管服务名,或静态托管实例名(常见 staticstore)。",
5981
6100
  "schema.query.routeId": "路由 ID。getRoute 时可选",
5982
6101
  "schema.query.path": "路由路径。getRoute / listRoutes 过滤时可选",
5983
- "schema.query.domain": "域名。getRoute / listRoutes 过滤时可选",
6102
+ "schema.query.domain": "域名。getRoute / listRoutes 过滤时可选;cachePurgeTask 时必填",
6103
+ "schema.query.cacheType": "缓存层(仅 cachePurgeTask):CDN 或 EO。省略时按域名接入方式自动匹配(EO→EO、CDN→CDN);" +
6104
+ "DIRECT / CUSTOM / NONE 的域没有 CloudBase 托管的缓存,会直接拒绝并说明原因。",
6105
+ "schema.query.taskId": "缓存清除任务 ID(仅 cachePurgeTask,可选过滤)",
6106
+ "schema.query.purgeType": "清除方式过滤(仅 cachePurgeTask):PURGE_URL=按 URL、PURGE_PREFIX=按前缀、PURGE_HOST=整站",
6107
+ "schema.query.startTime": "任务开始时间(仅 cachePurgeTask,可选)。格式 YYYY-MM-DD HH:mm:ss",
6108
+ "schema.query.endTime": "任务结束时间(仅 cachePurgeTask,可选)。格式 YYYY-MM-DD HH:mm:ss",
6109
+ "schema.query.offset": "分页偏移(仅 cachePurgeTask,可选)",
6110
+ "schema.query.limit": "返回数量上限(仅 cachePurgeTask,可选)",
5984
6111
  "schema.manage.action": "写操作:createRoute/updateRoute/deleteRoute 管理路由;enableRoute/disableRoute 启用或禁用已有路由(需 path,建议显式传 domain);" +
5985
6112
  "bindCustomDomain/deleteCustomDomain 管理自定义域名;" +
5986
6113
  "enableService/authSwitch 开关 HTTP 网关总开关与访问鉴权(需配合 enable 参数)。" +
@@ -5990,13 +6117,15 @@ exports.gateway = (0, types_js_1.defineModule)({
5990
6117
  "已有自定义域名时优先 createRoute(domain=已有域名) 实现访问,不必再次 bindCustomDomain / 传入 certificateId;" +
5991
6118
  "bindCustomDomain 仅用于首次绑定新域名(certificateId 可选,未传则按域名自动检索;可选 accessType=DIRECT|CDN|CUSTOM,CUSTOM 需 customCname;普通场景用默认 DIRECT)。" +
5992
6119
  "createRoute/bindCustomDomain 创建前会 VerifyHTTPServiceRoute 预检;失败时按返回的 DNS TXT 指引配置后重试。" +
6120
+ "purgeCache 清除域名缓存:需 domain 与 targets(配 purgeType=PURGE_URL|PURGE_PREFIX|PURGE_HOST,可选 cacheType=CDN|EO)。" +
5993
6121
  "接入说明:https://docs.cloudbase.net/service/custom-domain",
5994
6122
  "schema.manage.targetName": "上游资源名称(UpstreamResourceName),与 route.serviceName 二选一(route 优先)。" +
5995
6123
  "云函数=函数名;云托管=服务名;静态托管=实例名(常见 staticstore)。不会自动推断上游类型。",
5996
6124
  "schema.manage.path": "触发路径(网关匹配前缀),默认 /{上游名}。例:云函数 /api/hello、云托管 /api、静态托管 / 或 /app。" +
5997
6125
  "只建网关入口;与 enablePathTransmission 共同决定上游实际收到的路径。",
5998
6126
  "schema.manage.upstreamResourceType": "上游类型(与 route.upstreamResourceType 二选一,route 优先)。" +
5999
- "WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
6127
+ "WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器。" +
6128
+ "STORAGE 的 UpstreamResourceName 是云存储实例名,可先用 queryStorage 查实例。" +
6000
6129
  "createRoute/updateRoute 必填其一;勿把 manageFunctions 的 type=HTTP|Event 传到本字段。",
6001
6130
  "schema.manage.auth": "网关路径鉴权(EnableAuth)。匿名/浏览器公网访问通常 false。" +
6002
6131
  "只控制网关入口;云函数安全规则、云托管鉴权、静态托管权限需各自工具另行配置。",
@@ -6005,6 +6134,30 @@ exports.gateway = (0, types_js_1.defineModule)({
6005
6134
  "CBR 云托管(Express 等自管子路由)与 WEB_SCF 多路径函数常需 true;" +
6006
6135
  "STATIC_STORE 把触发路径映射到站点根目录(如 /app → 托管 /)时通常 false;" +
6007
6136
  "单入口/根路径处理保持 false。也可用 route.enablePathTransmission(route 优先)。",
6137
+ "schema.manage.enableSafeDomain": "跨域/合法域名校验(EnableSafeDomain),即控制台的「跨域设置」。true 时校验请求来源域名," +
6138
+ "false 时不校验(浏览器直连调用 API 的常见选择)。也可用 route.enableSafeDomain(route 优先)。",
6139
+ "schema.manage.pathRewrite": "路径重写(PathRewrite),形如 {prefix:\"/v1\", staticStorePrefix:\"app\"}。" +
6140
+ "prefix=命中路径前缀后替换为的内容;staticStorePrefix=静态托管场景下的目标前缀。" +
6141
+ "不需要改写就省略;也可用 route.pathRewrite(route 优先)。",
6142
+ "schema.manage.qpsPolicy": "限频(QPSPolicy),即控制台的「频限」。{total:总 QPS, perClient:{limitBy:\"ClientIP\"|\"UserID\", limitValue:单客户端 QPS}}。" +
6143
+ "不传则不限频;也可用 route.qpsPolicy(route 优先)。",
6144
+ "schema.manage.extension": "路由级扩展(Extension),可配自定义请求/响应头与缓存规则。" +
6145
+ "{headersHandler:{requestHeadersToAdd,requestHeadersToRemove,responseHeadersToAdd,responseHeadersToRemove}," +
6146
+ "cache:{rules:[{description,enable,condition:{target,matchType,values},actions:[{type:\"Cache\"|\"CacheKey\",cache,cacheKey}]}]}}。" +
6147
+ "⚠️ 默认域(*.{region}.app.tcloudbase.com)不支持自定义响应头,后端会拒绝(实测 2026-10-21),需要自定义域名才能配。" +
6148
+ "也可用 route.extension(route 优先)。",
6149
+ "schema.manage.extension.header.key": "请求/响应头名称,如 X-Frame-Options",
6150
+ "schema.manage.extension.header.value": "请求/响应头取值,如 SAMEORIGIN",
6151
+ "schema.manage.extension.header.action": "写入策略(可选):APPEND_IF_EXISTS_OR_ADD 追加;ADD_IF_ABSENT 不存在才加;" +
6152
+ "OVERWRITE_IF_EXISTS_OR_ADD 覆盖或新增;OVERWRITE_IF_EXISTS 仅覆盖已存在的",
6153
+ "schema.manage.extension.cacheCondition": "缓存规则匹配条件:target=url_path/file_extension/full_uri,matchType=prefix/suffix/contains/exact,values=匹配值数组",
6154
+ "schema.manage.extension.cacheActions": "缓存动作数组:type=Cache 配 cache{followOrigin,noCache,cacheTime,maxAgeTime}(三者互斥,需开启其一);" +
6155
+ "type=CacheKey 配 cacheKey{fullUrlCache,queryStringSwitch,queryStringAction,queryStringValues}",
6156
+ "schema.manage.domainExtension": "域名级扩展(Extension),作用于整个域名(含该域名下所有路径)的自定义头与缓存规则,结构同 extension。" +
6157
+ "缓存规则通常配在域名级;仅 createRoute/updateRoute/bindCustomDomain 生效。" +
6158
+ "⚠️ 同 extension:默认域不支持自定义响应头,需自定义域名。",
6159
+ "schema.manage.protocol": "域名协议(Protocol),仅 createRoute/updateRoute/bindCustomDomain 生效:" +
6160
+ "HTTP / HTTPS / HTTP_AND_HTTPS / HTTP_TO_HTTPS / HTTPS_TO_HTTP。省略则沿用域名现有配置。",
6008
6161
  "schema.manage.route.serviceName": "上游实例名:云函数名 / 云托管服务名 / 静态托管实例名(常见 staticstore)/ LH 实例。优先于顶层 targetName。",
6009
6162
  "schema.manage.route.upstreamResourceType": "同顶层 upstreamResourceType。route 内设置时优先于顶层。",
6010
6163
  "schema.manage.route.enablePathTransmission": "同顶层 enablePathTransmission。route 内设置时优先于顶层。",
@@ -6012,6 +6165,10 @@ exports.gateway = (0, types_js_1.defineModule)({
6012
6165
  "enable=false 禁用该 Domain+Path(访问返回 GATEWAY_ROUTE_DISABLED);" +
6013
6166
  "enable=true 重新启用。updateRoute 也可用顶层 enable 表达同一语义;" +
6014
6167
  "也可用专用 action enableRoute/disableRoute。route.enable 优先于顶层 enable。",
6168
+ "schema.manage.route.enableSafeDomain": "同顶层 enableSafeDomain(跨域/合法域名校验)。route 内设置时优先于顶层。",
6169
+ "schema.manage.route.pathRewrite": "同顶层 pathRewrite。route 内设置时优先于顶层。",
6170
+ "schema.manage.route.qpsPolicy": "同顶层 qpsPolicy(限频)。route 内设置时优先于顶层。",
6171
+ "schema.manage.route.extension": "同顶层 extension(路由级自定义头与缓存规则)。route 内设置时优先于顶层。",
6015
6172
  "schema.manage.route": "路由对象(可选写法)。例:云函数 {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"};" +
6016
6173
  "云托管 {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"};" +
6017
6174
  "静态托管 {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"};" +
@@ -6036,11 +6193,15 @@ exports.gateway = (0, types_js_1.defineModule)({
6036
6193
  "updateRoute 可选:映射到 Routes[].Enable(也可用 route.enable,route 优先;" +
6037
6194
  "也可用专用 action enableRoute/disableRoute)。" +
6038
6195
  "省略或非布尔值在 enableService/authSwitch 会返回参数错误。",
6196
+ "schema.manage.cacheType": "缓存层(仅 purgeCache):CDN 或 EO。省略时按域名接入方式自动匹配(EO→EO、CDN→CDN);" +
6197
+ "DIRECT / CUSTOM / NONE 的域没有 CloudBase 托管的缓存,会直接拒绝并说明原因。",
6198
+ "schema.manage.purgeType": "清除方式(仅 purgeCache,默认 PURGE_URL):PURGE_URL=按完整 URL、PURGE_PREFIX=按路径前缀、PURGE_HOST=整个域名",
6199
+ "schema.manage.targets": "清除目标(仅 purgeCache,必填):PURGE_URL 传 https://域名/路径 形式;PURGE_PREFIX 传 /static/ 这样的前缀;PURGE_HOST 传域名本身",
6039
6200
  }, {
6040
6201
  "query.title": "Query CloudBase Gateway",
6041
- "query.description": "Unified read-only entry for the CloudBase HTTP gateway (Domain/Route). Query path routes under a domain and their upstreams: WEB_SCF/SCF=cloud functions, CBR=CloudRun, STATIC_STORE=static hosting, LH=Lighthouse. The primary key is Domain + Path; listRoutes / getRoute / listCustomDomains / getPrivilege. getPrivilege queries the HTTP gateway master switch (enableService) and access auth (enableAuth) status. Before setting up custom domain access, run listCustomDomains first: if a custom domain already exists, prefer createRoute to attach a route (no certificate ID needed); use bindCustomDomain only when no usable custom domain exists.",
6202
+ "query.description": "Unified read-only entry for the CloudBase HTTP gateway (Domain/Route). Query path routes under a domain and their upstreams: WEB_SCF/SCF=cloud functions, CBR=CloudRun, STATIC_STORE=static hosting, STORAGE=cloud storage, LH=Lighthouse. The primary key is Domain + Path; listRoutes / getRoute / listCustomDomains / getPrivilege / cachePurgeTask. getPrivilege queries the HTTP gateway master switch (enableService) and access auth (enableAuth) status. cachePurgeTask queries cache purge tasks for a domain (use the TaskId from manageGateway purgeCache). Before setting up custom domain access, run listCustomDomains first: if a custom domain already exists, prefer createRoute to attach a route (no certificate ID needed); use bindCustomDomain only when no usable custom domain exists.",
6042
6203
  "manage.title": "Manage CloudBase Gateway",
6043
- "manage.description": "Unified write entry for the CloudBase HTTP gateway (Domain/Route). createRoute/updateRoute/deleteRoute forward a path under a domain to an upstream; enableRoute/disableRoute enable or disable existing routes (Routes[].Enable of ModifyHTTPServiceRoute underneath, not ModifyGatewayRoute). When domain is omitted, the IsDefault default HTTP domain with DomainType=HTTPSERVICE (like *.{region}.app.tcloudbase.com) is used; static-hosting CDN domains (*.tcloudbaseapp.com, DomainType=STATIC_STORE) are never used. These are path routes on the gateway default domain, not STATIC_STORE upstream bindings; a STATIC_STORE upstream must pass upstreamResourceType=STATIC_STORE explicitly. To disable the static-hosting default domain (*.tcloudbaseapp.com): first queryGateway(listRoutes) to find the domain with DomainType=STATIC_STORE and IsDefault=true, then manageGateway(action=\"disableRoute\", domain=that domain, path=\"/\"); do not use manageHosting. After creation, verify Domain / DomainType / Path / UpstreamResourceType via queryGateway(action=\"listRoutes\"). The upstream type uses a single parameter upstreamResourceType (or route.upstreamResourceType, route wins): WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, LH=Lighthouse; together with targetName or route.serviceName (function name / CloudRun service name / static-hosting instance name, commonly staticstore). createRoute only creates the gateway entry and does not change upstream permissions. enablePathTransmission: default false strips the trigger path prefix; true forwards the full path (CBR multi-route and WEB_SCF self-managed sub-paths often need true; STATIC_STORE custom trigger path mapping to site root usually false). ⚠️ Custom domain access: if the env already has a custom domain (queryGateway listCustomDomains first), prefer createRoute with that domain explicitly, no certificateId needed; use bindCustomDomain only when binding a brand-new custom domain for the first time (certificateId optional: when omitted, certificates are auto-searched by domain — a single cert is auto-selected, multiple certs return selection guidance). createRoute / bindCustomDomain call VerifyHTTPServiceRoute for ownership pre-checks before creation (probe → create); on failure, data.checks and DNS TXT guidance are returned; configure and retry. CORS/security domains (browser cross-origin allowlist, unrelated to the gateway custom domains of this tool) use manageEnv(action=addSecurityDomain/removeSecurityDomain). enableService/authSwitch: HTTP gateway master switch and access auth switch; if access returns HTTPSERVICE_NONACTIVATED after createRoute, the master switch is usually off (query via queryGateway getPrivilege, enable via enableService).",
6204
+ "manage.description": "Unified write entry for the CloudBase HTTP gateway (Domain/Route). createRoute/updateRoute/deleteRoute forward a path under a domain to an upstream; enableRoute/disableRoute enable or disable existing routes (Routes[].Enable of ModifyHTTPServiceRoute underneath, not ModifyGatewayRoute). When domain is omitted, the IsDefault default HTTP domain with DomainType=HTTPSERVICE (like *.{region}.app.tcloudbase.com) is used; static-hosting CDN domains (*.tcloudbaseapp.com, DomainType=STATIC_STORE) are never used. These are path routes on the gateway default domain, not STATIC_STORE upstream bindings; a STATIC_STORE upstream must pass upstreamResourceType=STATIC_STORE explicitly. To disable the static-hosting default domain (*.tcloudbaseapp.com): first queryGateway(listRoutes) to find the domain with DomainType=STATIC_STORE and IsDefault=true, then manageGateway(action=\"disableRoute\", domain=that domain, path=\"/\"); do not use manageHosting. After creation, verify Domain / DomainType / Path / UpstreamResourceType via queryGateway(action=\"listRoutes\"). The upstream type uses a single parameter upstreamResourceType (or route.upstreamResourceType, route wins): WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, STORAGE=cloud storage, LH=Lighthouse; together with targetName or route.serviceName (function name / CloudRun service name / static-hosting instance name, commonly staticstore / cloud storage instance name). createRoute only creates the gateway entry and does not change upstream permissions. enablePathTransmission: default false strips the trigger path prefix; true forwards the full path (CBR multi-route and WEB_SCF self-managed sub-paths often need true; STATIC_STORE custom trigger path mapping to site root usually false). ⚠️ Custom domain access: if the env already has a custom domain (queryGateway listCustomDomains first), prefer createRoute with that domain explicitly, no certificateId needed; use bindCustomDomain only when binding a brand-new custom domain for the first time (certificateId optional: when omitted, certificates are auto-searched by domain — a single cert is auto-selected, multiple certs return selection guidance). createRoute / bindCustomDomain call VerifyHTTPServiceRoute for ownership pre-checks before creation (probe → create); on failure, data.checks and DNS TXT guidance are returned; configure and retry. CORS/security domains (browser cross-origin allowlist, unrelated to the gateway custom domains of this tool) use manageEnv(action=addSecurityDomain/removeSecurityDomain). enableService/authSwitch: HTTP gateway master switch and access auth switch; if access returns HTTPSERVICE_NONACTIVATED after createRoute, the master switch is usually off (query via queryGateway getPrivilege, enable via enableService). purgeCache clears domain cache (CDN/EO by URL/prefix/host) and returns a TaskId; check progress with queryGateway cachePurgeTask. Route-level extras: enableSafeDomain (CORS / legit-domain check), pathRewrite (path rewriting), qpsPolicy (rate limiting), extension (custom request/response headers and cache rules); domain-level: protocol and domainExtension. updateRoute only needs the fields you want to change — untouched CORS/rewrite/QPS/extension settings are carried over from the live route.",
6044
6205
  "verify.failWithDns": "HTTP service domain pre-check failed: domain ownership verification failed. Configure the DNS TXT records per ownership.dnsRecords in data.checks, then retry.",
6045
6206
  "verify.fail": "HTTP service domain pre-check failed. Fix the issues per data.checks, then retry.",
6046
6207
  "verify.failWithDnsReason": "Configure the DNS TXT records (host/record type/record value in data.checks[].dnsRecords), then call this action again",
@@ -6058,7 +6219,7 @@ exports.gateway = (0, types_js_1.defineModule)({
6058
6219
  "privilege.nonActivatedHint": "; if the route is created successfully but access returns HTTPSERVICE_NONACTIVATED (403), enable the HTTP gateway first",
6059
6220
  "privilege.enableServiceReason": "Turn on the HTTP gateway master switch (EnableService); once on, routes are accessible via the default domain",
6060
6221
  "defaultDomain.notReady": "The env default HTTP access domain is not ready or not provisioned. Enable the HTTP access service in the console first, or use queryGateway(action=\"listRoutes\") to confirm that Domains contains a domain with DomainType=HTTPSERVICE and IsDefault=true (shaped like *.{region}.app.tcloudbase.com); do not use static-hosting domains (*.tcloudbaseapp.com). You may also pass domain explicitly and retry createRoute/updateRoute/deleteRoute.",
6061
- "upstreamTypeRequired": "upstreamResourceType (or route.upstreamResourceType) is required: WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, LH=Lighthouse. Do not infer the upstream type from targetName/serviceName alone.",
6222
+ "upstreamTypeRequired": "upstreamResourceType (or route.upstreamResourceType) is required: WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, STORAGE=cloud storage, LH=Lighthouse. Do not infer the upstream type from targetName/serviceName alone.",
6062
6223
  "upstreamNameRequired": "targetName or route.serviceName is required as the upstream resource name: a cloud function name, CloudRun service name, or static-hosting instance name (commonly staticstore).",
6063
6224
  "toggle.pathRequired": "path or route.path is required for action=enableRoute/disableRoute (e.g. \"/\" or \"/api\")",
6064
6225
  "toggle.notFound": "No route found for path {path}{domainSuffix}{upstreamSuffix}. Use queryGateway(action=\"listRoutes\") first to confirm Domain / Path, then call enableRoute/disableRoute. When disabling the static-hosting default domain (*.tcloudbaseapp.com), pass domain=that STATIC_STORE IsDefault domain explicitly, usually with path=\"/\".",
@@ -6124,14 +6285,32 @@ exports.gateway = (0, types_js_1.defineModule)({
6124
6285
  "authSwitch.onMessage": "HTTP access service auth enabled successfully",
6125
6286
  "authSwitch.offMessage": "HTTP access service auth disabled successfully",
6126
6287
  "authSwitch.verifyReason": "Verify the HTTP gateway master switch and access auth status",
6288
+ "purgeCache.domainRequired": "domain is required for action=purgeCache",
6289
+ "purgeCache.targetsRequired": "targets is required for action=purgeCache: full URLs for PURGE_URL, path prefixes for PURGE_PREFIX, or the domain itself for PURGE_HOST",
6290
+ "purgeCache.message": "Submitted a cache purge task for {count} target(s); CDN/EO purges usually take seconds to minutes — check progress with queryGateway(action=\"cachePurgeTask\") by TaskId",
6291
+ "purgeCache.verifyReason": "Query the cache purge task status",
6292
+ "query.cachePurgeTask": "Retrieved {count} cache purge task record(s)",
6293
+ "query.cachePurgeTaskDomainRequired": "domain is required for action=cachePurgeTask",
6294
+ "cache.domainNotFound": "No access configuration found for domain {domain}, so the cache layer is undetermined. Confirm the domain with queryGateway(action=\"listRoutes\") first.",
6295
+ "cache.notManaged": "Domain {domain} uses access type {accessType}; CloudBase does not host a CDN/EO cache for it, so cache purging does not apply (DIRECT and CUSTOM own-CDN caches are not managed by the platform). Switch the domain to CDN or EO access first.",
6296
+ "cache.typeMismatch": "Domain {domain} uses access type {accessType} and cannot be purged with cacheType={requested}. Omit cacheType to match the access type automatically.",
6127
6297
  // Input schema parameter descriptions
6128
- "schema.query.action": "Read-only action: listRoutes, getRoute, listCustomDomains, getPrivilege. " +
6298
+ "schema.query.action": "Read-only action: listRoutes, getRoute, listCustomDomains, getPrivilege, cachePurgeTask. " +
6129
6299
  "getPrivilege needs no other parameter and returns the HTTP gateway master switch and access auth status directly. " +
6300
+ "cachePurgeTask requires domain (optional cacheType/taskId/purgeType/startTime/endTime/offset/limit). " +
6130
6301
  "For custom domain access, run listCustomDomains first to check whether an existing domain can be reused.",
6131
6302
  "schema.query.targetName": "Upstream resource name filter (UpstreamResourceName): a cloud function name, CloudRun service name, or static-hosting instance name (commonly staticstore).",
6132
6303
  "schema.query.routeId": "Route ID. Optional for getRoute",
6133
6304
  "schema.query.path": "Route path. Optional as a getRoute / listRoutes filter",
6134
- "schema.query.domain": "Domain. Optional as a getRoute / listRoutes filter",
6305
+ "schema.query.domain": "Domain. Optional as a getRoute / listRoutes filter; required for cachePurgeTask",
6306
+ "schema.query.cacheType": "Cache layer (cachePurgeTask only): CDN or EO. When omitted it is matched from the domain access type (EO→EO, CDN→CDN); " +
6307
+ "DIRECT / CUSTOM / NONE domains have no CloudBase-hosted cache and are rejected with an explanation.",
6308
+ "schema.query.taskId": "Cache purge task ID (cachePurgeTask only, optional filter)",
6309
+ "schema.query.purgeType": "Purge type filter (cachePurgeTask only): PURGE_URL=by URL, PURGE_PREFIX=by path prefix, PURGE_HOST=whole domain",
6310
+ "schema.query.startTime": "Task start time (cachePurgeTask only, optional). Format YYYY-MM-DD HH:mm:ss",
6311
+ "schema.query.endTime": "Task end time (cachePurgeTask only, optional). Format YYYY-MM-DD HH:mm:ss",
6312
+ "schema.query.offset": "Pagination offset (cachePurgeTask only, optional)",
6313
+ "schema.query.limit": "Max records to return (cachePurgeTask only, optional)",
6135
6314
  "schema.manage.action": "Write actions: createRoute/updateRoute/deleteRoute manage routes; enableRoute/disableRoute enable or disable an existing route (path required, passing domain explicitly is recommended); " +
6136
6315
  "bindCustomDomain/deleteCustomDomain manage custom domains; " +
6137
6316
  "enableService/authSwitch toggle the HTTP gateway master switch and access auth (requires the enable parameter). " +
@@ -6141,13 +6320,15 @@ exports.gateway = (0, types_js_1.defineModule)({
6141
6320
  "When a custom domain already exists, prefer createRoute(domain=existing domain) for access instead of calling bindCustomDomain / passing certificateId again; " +
6142
6321
  "bindCustomDomain is only for binding a new domain the first time (certificateId is optional — when omitted, certificates are searched by domain; optional accessType=DIRECT|CDN|CUSTOM, CUSTOM requires customCname; use the default DIRECT for normal cases). " +
6143
6322
  "createRoute/bindCustomDomain run a VerifyHTTPServiceRoute pre-check before creation; on failure, follow the returned DNS TXT guidance and retry. " +
6323
+ "purgeCache clears domain cache: requires domain and targets (with purgeType=PURGE_URL|PURGE_PREFIX|PURGE_HOST, optional cacheType=CDN|EO). " +
6144
6324
  "Setup guide: https://docs.cloudbase.net/service/custom-domain",
6145
6325
  "schema.manage.targetName": "Upstream resource name (UpstreamResourceName); use either this or route.serviceName (route wins). " +
6146
6326
  "Cloud function=function name; CloudRun=service name; static hosting=instance name (commonly staticstore). The upstream type is never inferred automatically.",
6147
6327
  "schema.manage.path": "Trigger path (the gateway match prefix), defaults to /{upstream name}. Examples: cloud function /api/hello, CloudRun /api, static hosting / or /app. " +
6148
6328
  "This only creates the gateway entry; together with enablePathTransmission it determines the path the upstream actually receives.",
6149
6329
  "schema.manage.upstreamResourceType": "Upstream type (use either this or route.upstreamResourceType, route wins). " +
6150
- "WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, LH=Lighthouse. " +
6330
+ "WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, STORAGE=cloud storage, LH=Lighthouse. " +
6331
+ "For STORAGE, UpstreamResourceName is the cloud storage instance name — look it up with queryStorage first. " +
6151
6332
  "One of them is required for createRoute/updateRoute; do not pass the manageFunctions type=HTTP|Event value into this field.",
6152
6333
  "schema.manage.auth": "Gateway path auth (EnableAuth). Usually false for anonymous/browser public access. " +
6153
6334
  "It only controls the gateway entry; cloud function security rules, CloudRun auth, and static hosting permissions must be configured with their own tools.",
@@ -6156,6 +6337,31 @@ exports.gateway = (0, types_js_1.defineModule)({
6156
6337
  "CBR CloudRun (Express and similar self-managed sub-routes) and multi-path WEB_SCF functions often need true; " +
6157
6338
  "STATIC_STORE mapping a trigger path to the site root (e.g. /app → hosting /) is usually false; " +
6158
6339
  "keep false for single-entry/root-path handling. route.enablePathTransmission also works (route wins).",
6340
+ "schema.manage.enableSafeDomain": "CORS / legit-domain check (EnableSafeDomain), the console's \"cross-origin settings\". " +
6341
+ "true validates the request origin; false skips the check (common for browser-to-API calls). " +
6342
+ "route.enableSafeDomain also works (route wins).",
6343
+ "schema.manage.pathRewrite": "Path rewriting (PathRewrite), e.g. {prefix:\"/v1\", staticStorePrefix:\"app\"}. " +
6344
+ "prefix replaces the matched path prefix; staticStorePrefix is the target prefix for static hosting. " +
6345
+ "Omit when no rewriting is needed; route.pathRewrite also works (route wins).",
6346
+ "schema.manage.qpsPolicy": "Rate limiting (QPSPolicy), the console's \"frequency limit\". {total: total QPS, perClient:{limitBy:\"ClientIP\"|\"UserID\", limitValue: per-client QPS}}. " +
6347
+ "Omit for no limit; route.qpsPolicy also works (route wins).",
6348
+ "schema.manage.extension": "Route-level extension (Extension): custom request/response headers and cache rules. " +
6349
+ "{headersHandler:{requestHeadersToAdd,requestHeadersToRemove,responseHeadersToAdd,responseHeadersToRemove}," +
6350
+ "cache:{rules:[{description,enable,condition:{target,matchType,values},actions:[{type:\"Cache\"|\"CacheKey\",cache,cacheKey}]}]}}. " +
6351
+ "⚠️ The default domain (*.{region}.app.tcloudbase.com) rejects custom response headers (verified 2026-10-21); a custom domain is required. " +
6352
+ "route.extension also works (route wins).",
6353
+ "schema.manage.extension.header.key": "Header name, e.g. X-Frame-Options",
6354
+ "schema.manage.extension.header.value": "Header value, e.g. SAMEORIGIN",
6355
+ "schema.manage.extension.header.action": "Write strategy (optional): APPEND_IF_EXISTS_OR_ADD appends; ADD_IF_ABSENT only adds when missing; " +
6356
+ "OVERWRITE_IF_EXISTS_OR_ADD overwrites or adds; OVERWRITE_IF_EXISTS only overwrites existing",
6357
+ "schema.manage.extension.cacheCondition": "Cache rule match condition: target=url_path/file_extension/full_uri, matchType=prefix/suffix/contains/exact, values=match value list",
6358
+ "schema.manage.extension.cacheActions": "Cache action list: type=Cache with cache{followOrigin,noCache,cacheTime,maxAgeTime} (mutually exclusive — enable one); " +
6359
+ "type=CacheKey with cacheKey{fullUrlCache,queryStringSwitch,queryStringAction,queryStringValues}",
6360
+ "schema.manage.domainExtension": "Domain-level extension (Extension) applied to the whole domain (all paths): custom headers and cache rules, same shape as extension. " +
6361
+ "Cache rules usually belong here; only effective for createRoute/updateRoute/bindCustomDomain. " +
6362
+ "⚠️ Same as extension: the default domain rejects custom response headers; a custom domain is required.",
6363
+ "schema.manage.protocol": "Domain protocol (Protocol), effective for createRoute/updateRoute/bindCustomDomain only: " +
6364
+ "HTTP / HTTPS / HTTP_AND_HTTPS / HTTP_TO_HTTPS / HTTPS_TO_HTTP. Omit to keep the domain's current setting.",
6159
6365
  "schema.manage.route.serviceName": "Upstream instance name: cloud function name / CloudRun service name / static-hosting instance name (commonly staticstore) / LH instance. Takes precedence over the top-level targetName.",
6160
6366
  "schema.manage.route.upstreamResourceType": "Same as the top-level upstreamResourceType. When set inside route, it takes precedence over the top level.",
6161
6367
  "schema.manage.route.enablePathTransmission": "Same as the top-level enablePathTransmission. When set inside route, it takes precedence over the top level.",
@@ -6163,6 +6369,10 @@ exports.gateway = (0, types_js_1.defineModule)({
6163
6369
  "enable=false disables this Domain+Path (access returns GATEWAY_ROUTE_DISABLED); " +
6164
6370
  "enable=true re-enables it. updateRoute can express the same semantics with the top-level enable; " +
6165
6371
  "the dedicated actions enableRoute/disableRoute also work. route.enable takes precedence over the top-level enable.",
6372
+ "schema.manage.route.enableSafeDomain": "Same as the top-level enableSafeDomain (CORS / legit-domain check). When set inside route, it takes precedence.",
6373
+ "schema.manage.route.pathRewrite": "Same as the top-level pathRewrite. When set inside route, it takes precedence.",
6374
+ "schema.manage.route.qpsPolicy": "Same as the top-level qpsPolicy (rate limiting). When set inside route, it takes precedence.",
6375
+ "schema.manage.route.extension": "Same as the top-level extension (route-level custom headers and cache rules). When set inside route, it takes precedence.",
6166
6376
  "schema.manage.route": "Route object (an optional form). Examples: cloud function {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"}; " +
6167
6377
  "CloudRun {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"}; " +
6168
6378
  "static hosting {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"}; " +
@@ -6187,6 +6397,10 @@ exports.gateway = (0, types_js_1.defineModule)({
6187
6397
  "optional for updateRoute: maps to Routes[].Enable (route.enable also works and wins; " +
6188
6398
  "the dedicated actions enableRoute/disableRoute are available too). " +
6189
6399
  "Omitting it or passing a non-boolean returns a parameter error for enableService/authSwitch.",
6400
+ "schema.manage.cacheType": "Cache layer (purgeCache only): CDN or EO. When omitted it is matched from the domain access type (EO→EO, CDN→CDN); " +
6401
+ "DIRECT / CUSTOM / NONE domains have no CloudBase-hosted cache and are rejected with an explanation.",
6402
+ "schema.manage.purgeType": "Purge type (purgeCache only, default PURGE_URL): PURGE_URL=by full URL, PURGE_PREFIX=by path prefix, PURGE_HOST=whole domain",
6403
+ "schema.manage.targets": "Purge targets (purgeCache only, required): full https://domain/path URLs for PURGE_URL; prefixes such as /static/ for PURGE_PREFIX; the domain itself for PURGE_HOST",
6190
6404
  });
6191
6405
 
6192
6406
 
@@ -26499,42 +26713,68 @@ function registerEnvTools(server) {
26499
26713
  // API Key mode: exchange for temporary credentials
26500
26714
  const apiKeyFromEnv = (0, auth_js_1.getCloudBaseApiKeyFromEnv)();
26501
26715
  if (apiKeyFromEnv && process.env.CLOUDBASE_ENV_ID) {
26716
+ // 项目级 key 与账号登录并存时,项目级优先——设备码/网页授权即使走完也不生效,
26717
+ // 所以短路段必须说清「怎么切」,否则用户会以为换号成功
26718
+ const removable = (0, auth_js_1.isEnvCredentialRemovableByLogout)();
26719
+ const apiKeyEnvId = process.env.CLOUDBASE_ENV_ID;
26720
+ const switchHint = "\n" +
26721
+ (removable
26722
+ ? (0, index_js_1.t)("env.auth.apiKeySwitchHintRemovable", undefined, outLang)
26723
+ : (0, index_js_1.t)("env.auth.apiKeySwitchHintExternal", {
26724
+ varName: process.env.CLOUDBASE_API_KEY
26725
+ ? "CLOUDBASE_API_KEY"
26726
+ : "CLOUDBASE_APIKEY",
26727
+ }, outLang));
26728
+ const logoutNextStep = removable
26729
+ ? (0, tool_result_js_1.buildAuthNextStep)("logout", {
26730
+ suggestedArgs: { action: "logout", confirm: "yes" },
26731
+ })
26732
+ : undefined;
26733
+ // peek 发现回填的那份已失效时会把 env 清掉:那就不是「登录失败」,
26734
+ // 而是「旧 key 已经作废」,直接往下走正常登录流程
26735
+ let adoptedKeyDropped = false;
26502
26736
  try {
26503
26737
  const existingLoginState = await (0, auth_js_1.peekLoginState)();
26504
26738
  if (existingLoginState) {
26505
26739
  return (0, tool_result_js_1.buildJsonToolResult)({
26506
26740
  ok: true,
26507
26741
  code: "AUTH_READY",
26508
- message: await appendApiKeyCamWarningIfNeeded(existingLoginState, (0, index_js_1.t)("env.auth.apiKeyAutoLogin", undefined, outLang)),
26742
+ message: (await appendApiKeyCamWarningIfNeeded(existingLoginState, (0, index_js_1.t)("env.auth.apiKeyAutoLogin", undefined, outLang))) + switchHint,
26509
26743
  auth_mode: "api_key",
26510
- envId: process.env.CLOUDBASE_ENV_ID,
26744
+ envId: apiKeyEnvId,
26745
+ ...(logoutNextStep ? { next_step: logoutNextStep } : {}),
26511
26746
  });
26512
26747
  }
26748
+ adoptedKeyDropped = !(0, auth_js_1.getCloudBaseApiKeyFromEnv)();
26513
26749
  }
26514
26750
  catch (e) {
26515
26751
  // peekLoginState threw; log and fall through to diagnostic
26516
26752
  (0, logger_js_1.debug)("start_auth: peekLoginState threw", { error: e instanceof Error ? e.message : String(e) });
26517
26753
  }
26518
- // API Key exchange failed: return diagnostic details
26519
- const exchangeRegion = (0, site_map_js_1.resolveApiKeyExchangeRegion)();
26520
- const endpoint = process.env.CLOUDBASE_API_ENDPOINT ||
26521
- `https://${process.env.CLOUDBASE_ENV_ID}.${exchangeRegion ?? "ap-shanghai"}.tcb-api.tencentcloudapi.com`;
26522
- const diagMessage = (0, index_js_1.t)("env.auth.apiKeyEnvExchangeFailed", undefined, outLang) +
26523
- (0, index_js_1.t)("env.auth.apiKeyDiagEnv", {
26524
- envId: String(process.env.CLOUDBASE_ENV_ID),
26525
- apiKeyPrefix: apiKeyFromEnv.slice(0, 20),
26526
- site: process.env.TCB_SITE || (0, index_js_1.t)("env.auth.siteUnset", undefined, outLang),
26527
- gatewayRegion: exchangeRegion ?? (0, index_js_1.t)("env.auth.gatewayRegionDefault", undefined, outLang),
26754
+ if (!adoptedKeyDropped) {
26755
+ // API Key exchange failed: return diagnostic details
26756
+ const exchangeRegion = (0, site_map_js_1.resolveApiKeyExchangeRegion)();
26757
+ const endpoint = process.env.CLOUDBASE_API_ENDPOINT ||
26758
+ `https://${apiKeyEnvId}.${exchangeRegion ?? "ap-shanghai"}.tcb-api.tencentcloudapi.com`;
26759
+ const diagMessage = (0, index_js_1.t)("env.auth.apiKeyEnvExchangeFailed", undefined, outLang) +
26760
+ (0, index_js_1.t)("env.auth.apiKeyDiagEnv", {
26761
+ envId: String(apiKeyEnvId),
26762
+ apiKeyPrefix: apiKeyFromEnv.slice(0, 20),
26763
+ site: process.env.TCB_SITE || (0, index_js_1.t)("env.auth.siteUnset", undefined, outLang),
26764
+ gatewayRegion: exchangeRegion ?? (0, index_js_1.t)("env.auth.gatewayRegionDefault", undefined, outLang),
26765
+ endpoint,
26766
+ }, outLang) +
26767
+ switchHint;
26768
+ return (0, tool_result_js_1.buildJsonToolResult)({
26769
+ ok: false,
26770
+ code: "API_KEY_AUTH_FAILED",
26771
+ message: diagMessage,
26772
+ auth_mode: "api_key",
26773
+ envId: apiKeyEnvId,
26528
26774
  endpoint,
26529
- }, outLang);
26530
- return (0, tool_result_js_1.buildJsonToolResult)({
26531
- ok: false,
26532
- code: "API_KEY_AUTH_FAILED",
26533
- message: diagMessage,
26534
- auth_mode: "api_key",
26535
- envId: process.env.CLOUDBASE_ENV_ID,
26536
- endpoint,
26537
- });
26775
+ ...(logoutNextStep ? { next_step: logoutNextStep } : {}),
26776
+ });
26777
+ }
26538
26778
  }
26539
26779
  const region = server.cloudBaseOptions?.region || process.env.TCB_REGION;
26540
26780
  const auth = toolbox_1.AuthSupervisor.getInstance({});
@@ -26863,12 +27103,19 @@ function registerEnvTools(server) {
26863
27103
  });
26864
27104
  }
26865
27105
  if (action === "logout") {
26866
- // Disallow logout in API Key mode
26867
- if ((0, auth_js_1.getCloudBaseApiKeyFromEnv)() && process.env.CLOUDBASE_ENV_ID) {
27106
+ // 用户自己在 shell / 宿主配置里设的 API Key 变量,进程内删不掉,只能提示去源头改;
27107
+ // 按项目保存的那一份(我们回填的,或 login_by_api_key 写下的)能清,走正常登出。
27108
+ if ((0, auth_js_1.getCloudBaseApiKeyFromEnv)() &&
27109
+ process.env.CLOUDBASE_ENV_ID &&
27110
+ !(0, auth_js_1.isEnvCredentialRemovableByLogout)()) {
26868
27111
  return (0, tool_result_js_1.buildJsonToolResult)({
26869
27112
  ok: false,
26870
27113
  code: "LOGOUT_NOT_ALLOWED",
26871
- message: (0, index_js_1.t)("env.auth.logoutNotAllowedApiKey", undefined, outLang),
27114
+ message: (0, index_js_1.t)("env.auth.logoutNotAllowedApiKey", {
27115
+ varName: process.env.CLOUDBASE_API_KEY
27116
+ ? "CLOUDBASE_API_KEY"
27117
+ : "CLOUDBASE_APIKEY",
27118
+ }, outLang),
26872
27119
  auth_mode: "api_key",
26873
27120
  });
26874
27121
  }
@@ -26882,12 +27129,15 @@ function registerEnvTools(server) {
26882
27129
  }),
26883
27130
  });
26884
27131
  }
26885
- await (0, auth_js_1.logout)();
27132
+ const { removed } = await (0, auth_js_1.logout)();
26886
27133
  (0, cloudbase_manager_js_1.resetCloudBaseManagerCache)();
26887
27134
  return (0, tool_result_js_1.buildJsonToolResult)({
26888
27135
  ok: true,
26889
27136
  code: "LOGGED_OUT",
26890
- message: (0, index_js_1.t)("env.auth.loggedOut", undefined, outLang),
27137
+ removed_scopes: removed,
27138
+ message: removed.includes("project")
27139
+ ? (0, index_js_1.t)("env.auth.loggedOutProject", undefined, outLang)
27140
+ : (0, index_js_1.t)("env.auth.loggedOut", undefined, outLang),
26891
27141
  });
26892
27142
  }
26893
27143
  if (action === "get_temp_credentials") {
@@ -29662,8 +29912,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
29662
29912
  return (mod && mod.__esModule) ? mod : { "default": mod };
29663
29913
  };
29664
29914
  Object.defineProperty(exports, "__esModule", ({ value: true }));
29665
- exports.CUSTOM_IMAGE_RUNTIME = exports.MASKED_ENV_VALUE = exports.LAYER_SOFT_WARN = exports.DEFAULT_FUNCTION_ALIAS = exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = exports.FUNCTION_UPLOAD_TARGET_NOT_FOUND_ERROR_CODE = void 0;
29915
+ exports.CUSTOM_IMAGE_RUNTIME = exports.MASKED_ENV_VALUE = exports.LAYER_SOFT_WARN = exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE = exports.FUNCTION_LOG_QUERY_ERROR_CODE = exports.DEFAULT_FUNCTION_ALIAS = exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = exports.FUNCTION_UPLOAD_TARGET_NOT_FOUND_ERROR_CODE = void 0;
29666
29916
  exports.formatRuntimeList = formatRuntimeList;
29917
+ exports.formatClsTime = formatClsTime;
29918
+ exports.buildFunctionLogClsQuery = buildFunctionLogClsQuery;
29919
+ exports.resolveFunctionLogWindow = resolveFunctionLogWindow;
29920
+ exports.parseFunctionLogItem = parseFunctionLogItem;
29921
+ exports.isLogServiceNotEnabledMessage = isLogServiceNotEnabledMessage;
29667
29922
  exports.layerNameIncludesEnvId = layerNameIncludesEnvId;
29668
29923
  exports.buildCreateLayerNameWarning = buildCreateLayerNameWarning;
29669
29924
  exports.maskFunctionDetailEnvValues = maskFunctionDetailEnvValues;
@@ -29827,6 +30082,125 @@ exports.MANAGE_FUNCTION_ACTIONS = [
29827
30082
  ];
29828
30083
  /** Default SCF traffic alias used by CloudBase CLI `fn get-route` / `fn config-route`. */
29829
30084
  exports.DEFAULT_FUNCTION_ALIAS = "$DEFAULT";
30085
+ /**
30086
+ * 云函数日志的 CLS 检索底座。
30087
+ *
30088
+ * 底层 `GetFunctionLogs` / `GetFunctionLogDetail` 已下线(`@cloudbase/manager-node` 5.8.8 起
30089
+ * `getFunctionLogsV2` / `getFunctionLogDetail` 直接抛错),云函数日志只能从 CLS 取。CLS 里每条
30090
+ * 云函数日志是按请求聚合的 JSON,`function_name` / `request_id` / `qualifier` / `src` 都是扁平
30091
+ * 字段,可以直接做检索条件,不需要再扫全量日志。
30092
+ */
30093
+ const FUNCTION_LOG_CLS_SOURCE = "(src:app OR src:system)";
30094
+ /** SearchClsLog 单次返回上限。 */
30095
+ const FUNCTION_LOG_MAX_LIMIT = 100;
30096
+ const FUNCTION_LOG_DEFAULT_LIMIT = 20;
30097
+ const FUNCTION_LOG_WINDOW_MS = 24 * 60 * 60 * 1000;
30098
+ /**
30099
+ * CLS 的时间入参固定按北京时间理解(与返回的 `Timestamp` 一致),
30100
+ * 托管形态的服务进程可能是 UTC,所以这里显式加偏移,不依赖进程时区。
30101
+ */
30102
+ const CLS_TIME_OFFSET_MS = 8 * 60 * 60 * 1000;
30103
+ /** CLS 检索语法里 `key:"value"` 的值需要转义反斜杠与双引号。 */
30104
+ function escapeClsValue(value) {
30105
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
30106
+ }
30107
+ /** CLS 入参/展示用的时间格式:YYYY-MM-DD HH:mm:ss(北京时间)。 */
30108
+ function formatClsTime(time) {
30109
+ return new Date(time + CLS_TIME_OFFSET_MS).toISOString().slice(0, 19).replace("T", " ");
30110
+ }
30111
+ function buildFunctionLogClsQuery(input) {
30112
+ const clauses = [FUNCTION_LOG_CLS_SOURCE];
30113
+ if (input.functionName) {
30114
+ clauses.push(`function_name:"${escapeClsValue(input.functionName)}"`);
30115
+ }
30116
+ if (input.qualifier) {
30117
+ clauses.push(`qualifier:"${escapeClsValue(input.qualifier)}"`);
30118
+ }
30119
+ if (input.requestId) {
30120
+ clauses.push(`request_id:"${escapeClsValue(input.requestId)}"`);
30121
+ }
30122
+ return clauses.join(" AND ");
30123
+ }
30124
+ /** 只传一端时把窗口收敛到 24 小时以内,CLS 与旧接口都限制单次跨度。 */
30125
+ function resolveFunctionLogWindow(startTime, endTime, now = Date.now()) {
30126
+ if (startTime && endTime) {
30127
+ return { StartTime: startTime, EndTime: endTime };
30128
+ }
30129
+ if (startTime) {
30130
+ const start = new Date(startTime).getTime();
30131
+ // 单一 startTime 时至少给出一分钟窗口:起止相同(或 start 在未来)时 CLS 会直接判非法。
30132
+ const end = Number.isFinite(start)
30133
+ ? Math.max(start + 60_000, Math.min(now, start + FUNCTION_LOG_WINDOW_MS))
30134
+ : now;
30135
+ return { StartTime: startTime, EndTime: formatClsTime(end) };
30136
+ }
30137
+ if (endTime) {
30138
+ const end = new Date(endTime).getTime();
30139
+ const start = Number.isFinite(end) ? end - FUNCTION_LOG_WINDOW_MS : now - FUNCTION_LOG_WINDOW_MS;
30140
+ return { StartTime: formatClsTime(start), EndTime: endTime };
30141
+ }
30142
+ return { StartTime: formatClsTime(now - FUNCTION_LOG_WINDOW_MS), EndTime: formatClsTime(now) };
30143
+ }
30144
+ /**
30145
+ * 把 CLS 的日志条目还原成调用方关心的字段。
30146
+ *
30147
+ * 解析失败时(例如用户自己打的纯文本日志)保留原文放进 `log`,不让一条脏数据毁掉整页结果。
30148
+ */
30149
+ function parseFunctionLogItem(item) {
30150
+ const content = typeof item.Content === "string" ? item.Content : "";
30151
+ let parsed = null;
30152
+ try {
30153
+ const value = JSON.parse(content);
30154
+ if (value && typeof value === "object" && !Array.isArray(value)) {
30155
+ parsed = value;
30156
+ }
30157
+ }
30158
+ catch {
30159
+ parsed = null;
30160
+ }
30161
+ const timestamp = item.Timestamp ?? "";
30162
+ if (!parsed) {
30163
+ return {
30164
+ requestId: "",
30165
+ functionName: "",
30166
+ qualifier: "",
30167
+ source: "",
30168
+ log: content,
30169
+ statusCode: "",
30170
+ retCode: "",
30171
+ durationMs: null,
30172
+ memoryMb: null,
30173
+ memUsageBytes: null,
30174
+ requestSource: "",
30175
+ timestamp,
30176
+ };
30177
+ }
30178
+ const str = (value) => value === undefined || value === null ? "" : String(value);
30179
+ const num = (value) => {
30180
+ const parsedNumber = Number(value);
30181
+ return Number.isFinite(parsedNumber) ? parsedNumber : null;
30182
+ };
30183
+ return {
30184
+ requestId: str(parsed.request_id),
30185
+ functionName: str(parsed.function_name),
30186
+ qualifier: str(parsed.qualifier || parsed.alias),
30187
+ source: str(parsed.src),
30188
+ log: str(parsed.log || parsed.tcb_log),
30189
+ statusCode: str(parsed.status_code),
30190
+ retCode: str(parsed.ret_code),
30191
+ durationMs: num(parsed.duration),
30192
+ memoryMb: num(parsed.memory),
30193
+ memUsageBytes: num(parsed.mem_usage),
30194
+ requestSource: str(parsed.request_source),
30195
+ timestamp,
30196
+ };
30197
+ }
30198
+ exports.FUNCTION_LOG_QUERY_ERROR_CODE = "FUNCTION_LOG_QUERY_FAILED";
30199
+ exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE = "LOG_SERVICE_NOT_ENABLED";
30200
+ /** 未开通日志服务时 CLS 报的是 topic 不存在,按这个特征把它翻译成可执行的下一步。 */
30201
+ function isLogServiceNotEnabledMessage(message) {
30202
+ return /TopicNotExist|TopicIsolated|topic[\s\S]{0,20}not\s*exist|日志服务未开通/i.test(message);
30203
+ }
29830
30204
  /** Layer soft-warn copy — account-scoped SCF LayerName guidance (no hard fail). */
29831
30205
  exports.LAYER_SOFT_WARN = {
29832
30206
  createNameFormat: (envId) => (0, index_js_1.t)("functions.layerWarn.createNameFormat", { envId }),
@@ -30294,6 +30668,81 @@ function registerFunctionTools(server) {
30294
30668
  }
30295
30669
  }
30296
30670
  };
30671
+ /**
30672
+ * 云函数日志统一走 CLS。查询失败时分两类给出可执行的下一步:
30673
+ * 日志服务没开通 ⇒ 引导开通;其它失败 ⇒ 引导到 queryLogs 用同一条检索语句重试。
30674
+ */
30675
+ const throwLogQueryError = (error, scope) => {
30676
+ const message = error instanceof Error ? error.message : String(error);
30677
+ if (isLogServiceNotEnabledMessage(message)) {
30678
+ (0, tool_result_js_1.throwToolPayloadError)({
30679
+ success: false,
30680
+ errorCode: exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE,
30681
+ data: { ...scope },
30682
+ message: (0, index_js_1.t)("functions.logs.serviceNotEnabled"),
30683
+ nextActions: [
30684
+ {
30685
+ tool: "manageLogs",
30686
+ action: "createLogService",
30687
+ reason: (0, index_js_1.t)("functions.reason.createLogService"),
30688
+ suggested_args: { action: "createLogService", confirm: true },
30689
+ },
30690
+ ],
30691
+ });
30692
+ }
30693
+ (0, tool_result_js_1.throwToolPayloadError)({
30694
+ success: false,
30695
+ errorCode: exports.FUNCTION_LOG_QUERY_ERROR_CODE,
30696
+ data: { ...scope, rawError: message },
30697
+ message: (0, index_js_1.t)("functions.logs.queryFailed", { message }),
30698
+ nextActions: [
30699
+ {
30700
+ tool: "queryLogs",
30701
+ action: "searchLogs",
30702
+ reason: (0, index_js_1.t)("functions.reason.searchClsFallback"),
30703
+ suggested_args: {
30704
+ action: "searchLogs",
30705
+ service: "tcb",
30706
+ queryString: buildFunctionLogClsQuery({
30707
+ functionName: scope.functionName,
30708
+ requestId: scope.requestId,
30709
+ }),
30710
+ startTime: scope.window.StartTime,
30711
+ endTime: scope.window.EndTime,
30712
+ },
30713
+ },
30714
+ ],
30715
+ });
30716
+ };
30717
+ /**
30718
+ * 空结果不等于「函数没被调用」:日志服务没开通时 CLS 同样返回空。
30719
+ * 这里只在真的查到空结果时多做一次开销很小的探活,避免把「没开通」误报成「没日志」。
30720
+ */
30721
+ const assertLogServiceEnabled = async (logService) => {
30722
+ try {
30723
+ if (await logService.checkLogServiceEnabled()) {
30724
+ return;
30725
+ }
30726
+ }
30727
+ catch {
30728
+ // 探活失败不制造新的错误源:保持「该时间段没有日志」的原始结论。
30729
+ return;
30730
+ }
30731
+ (0, tool_result_js_1.throwToolPayloadError)({
30732
+ success: false,
30733
+ errorCode: exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE,
30734
+ data: { enabled: false },
30735
+ message: (0, index_js_1.t)("functions.logs.serviceNotEnabled"),
30736
+ nextActions: [
30737
+ {
30738
+ tool: "manageLogs",
30739
+ action: "createLogService",
30740
+ reason: (0, index_js_1.t)("functions.reason.createLogService"),
30741
+ suggested_args: { action: "createLogService", confirm: true },
30742
+ },
30743
+ ],
30744
+ });
30745
+ };
30297
30746
  const normalizeManageLayers = (layers) => normalizeFunctionLayers((layers ?? []).map((layer) => ({
30298
30747
  LayerName: layer.layerName ?? layer.LayerName,
30299
30748
  LayerVersion: layer.layerVersion ?? layer.LayerVersion,
@@ -30415,39 +30864,86 @@ function registerFunctionTools(server) {
30415
30864
  }
30416
30865
  validateLogRange(input.startTime, input.endTime, input.offset, input.limit);
30417
30866
  const cloudbase = await getManager();
30418
- let result;
30867
+ const window = resolveFunctionLogWindow(input.startTime, input.endTime);
30868
+ const limit = Math.min(Math.max(input.limit ?? FUNCTION_LOG_DEFAULT_LIMIT, 1), FUNCTION_LOG_MAX_LIMIT);
30869
+ const offset = Math.max(input.offset ?? 0, 0);
30870
+ // SearchClsLog 单次最多 100 条且没有 offset 入参:不带游标时多取 offset+limit 再本地
30871
+ // 切片,带游标(context)时游标本身已经记录了偏移,直接返回当页。
30872
+ const fetchLimit = Math.min(offset + limit, FUNCTION_LOG_MAX_LIMIT);
30873
+ const queryString = buildFunctionLogClsQuery({
30874
+ functionName: input.functionName,
30875
+ requestId: input.requestId,
30876
+ qualifier: input.qualifier,
30877
+ });
30878
+ let logResults = {};
30419
30879
  try {
30420
- result = await cloudbase.functions.getFunctionLogsV2({
30421
- name: input.functionName,
30422
- offset: input.offset,
30423
- limit: input.limit,
30424
- startTime: input.startTime,
30425
- endTime: input.endTime,
30426
- requestId: input.requestId,
30427
- qualifier: input.qualifier,
30880
+ const result = await cloudbase.log.searchClsLog({
30881
+ queryString,
30882
+ StartTime: window.StartTime,
30883
+ EndTime: window.EndTime,
30884
+ Limit: fetchLimit,
30885
+ Context: input.context,
30886
+ Sort: "desc",
30887
+ service: "tcb",
30428
30888
  });
30889
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
30890
+ logResults = result.LogResults ?? {};
30429
30891
  }
30430
30892
  catch (error) {
30431
- const errMsg = error instanceof Error ? error.message : String(error);
30432
- if (/invalid parameter/i.test(errMsg)) {
30433
- throw new Error(`${errMsg}\n\n${(0, index_js_1.t)("functions.logsInvalidParamTips")}`);
30434
- }
30435
- throw error;
30893
+ throwLogQueryError(error, { functionName: input.functionName, window });
30436
30894
  }
30437
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
30895
+ const entries = (logResults.Results ?? []).map(parseFunctionLogItem);
30896
+ const logs = input.context ? entries : entries.slice(offset, offset + limit);
30897
+ if (logs.length === 0) {
30898
+ await assertLogServiceEnabled(cloudbase.log);
30899
+ }
30900
+ const hasMore = !logResults.ListOver && Boolean(logResults.Context);
30438
30901
  return buildEnvelope({
30439
30902
  action: input.action,
30440
30903
  functionName: input.functionName,
30441
- logs: result.LogList || [],
30442
- requestId: result.RequestId,
30443
- raw: result,
30444
- }, (0, index_js_1.t)("functions.gotFunctionLogs", { fnName: input.functionName }), [
30445
- {
30446
- tool: "queryFunctions",
30447
- action: "getFunctionLogDetail",
30448
- reason: (0, index_js_1.t)("functions.reason.logDetail"),
30449
- },
30450
- ]);
30904
+ service: "tcb",
30905
+ queryString,
30906
+ startTime: window.StartTime,
30907
+ endTime: window.EndTime,
30908
+ logs,
30909
+ count: logs.length,
30910
+ context: logResults.Context ?? null,
30911
+ hasMore,
30912
+ }, logs.length > 0
30913
+ ? (0, index_js_1.t)("functions.gotFunctionLogs", { fnName: input.functionName })
30914
+ : (0, index_js_1.t)("functions.logs.emptyByFunction", {
30915
+ fnName: input.functionName,
30916
+ start: window.StartTime,
30917
+ end: window.EndTime,
30918
+ }), hasMore
30919
+ ? [
30920
+ {
30921
+ tool: "queryFunctions",
30922
+ action: "listFunctionLogs",
30923
+ reason: (0, index_js_1.t)("functions.reason.nextLogPage"),
30924
+ suggested_args: {
30925
+ action: "listFunctionLogs",
30926
+ functionName: input.functionName,
30927
+ context: logResults.Context,
30928
+ startTime: window.StartTime,
30929
+ endTime: window.EndTime,
30930
+ limit,
30931
+ },
30932
+ },
30933
+ ]
30934
+ : [
30935
+ {
30936
+ tool: "queryFunctions",
30937
+ action: "getFunctionLogDetail",
30938
+ reason: (0, index_js_1.t)("functions.reason.logDetail"),
30939
+ suggested_args: {
30940
+ action: "getFunctionLogDetail",
30941
+ requestId: logs[0]?.requestId || "<日志里的 requestId>",
30942
+ startTime: window.StartTime,
30943
+ endTime: window.EndTime,
30944
+ },
30945
+ },
30946
+ ]);
30451
30947
  }
30452
30948
  case "getFunctionLogDetail": {
30453
30949
  if (!input.requestId) {
@@ -30455,27 +30951,67 @@ function registerFunctionTools(server) {
30455
30951
  }
30456
30952
  validateLogRange(input.startTime, input.endTime);
30457
30953
  const cloudbase = await getManager();
30458
- let result;
30954
+ const window = resolveFunctionLogWindow(input.startTime, input.endTime);
30955
+ const queryString = buildFunctionLogClsQuery({
30956
+ functionName: input.functionName,
30957
+ requestId: input.requestId,
30958
+ qualifier: input.qualifier,
30959
+ });
30960
+ let logResults = {};
30459
30961
  try {
30460
- result = await cloudbase.functions.getFunctionLogDetail({
30461
- startTime: input.startTime,
30462
- endTime: input.endTime,
30463
- logRequestId: input.requestId,
30962
+ const result = await cloudbase.log.searchClsLog({
30963
+ queryString,
30964
+ StartTime: window.StartTime,
30965
+ EndTime: window.EndTime,
30966
+ Limit: FUNCTION_LOG_MAX_LIMIT,
30967
+ Sort: "asc",
30968
+ service: "tcb",
30464
30969
  });
30970
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
30971
+ logResults = result.LogResults ?? {};
30465
30972
  }
30466
30973
  catch (error) {
30467
- const errMsg = error instanceof Error ? error.message : String(error);
30468
- if (/invalid parameter/i.test(errMsg)) {
30469
- throw new Error(`${errMsg}\n\n${(0, index_js_1.t)("functions.logDetailInvalidParamTips")}`);
30470
- }
30471
- throw error;
30974
+ throwLogQueryError(error, { requestId: input.requestId, window });
30975
+ }
30976
+ // 一次调用在 CLS 里是多条记录(START / 业务日志 / REPORT),按时间升序拼回完整过程。
30977
+ const entries = (logResults.Results ?? []).map(parseFunctionLogItem);
30978
+ if (entries.length === 0) {
30979
+ await assertLogServiceEnabled(cloudbase.log);
30980
+ return buildEnvelope({
30981
+ action: input.action,
30982
+ requestId: input.requestId,
30983
+ queryString,
30984
+ startTime: window.StartTime,
30985
+ endTime: window.EndTime,
30986
+ logs: [],
30987
+ count: 0,
30988
+ }, (0, index_js_1.t)("functions.logs.emptyByRequest", {
30989
+ requestId: input.requestId,
30990
+ start: window.StartTime,
30991
+ end: window.EndTime,
30992
+ }), [
30993
+ {
30994
+ tool: "queryFunctions",
30995
+ action: "listFunctionLogs",
30996
+ reason: (0, index_js_1.t)("functions.reason.widenLogRange"),
30997
+ suggested_args: {
30998
+ action: "listFunctionLogs",
30999
+ functionName: input.functionName || "<函数名>",
31000
+ startTime: window.StartTime,
31001
+ endTime: window.EndTime,
31002
+ },
31003
+ },
31004
+ ]);
30472
31005
  }
30473
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
30474
31006
  return buildEnvelope({
30475
31007
  action: input.action,
30476
31008
  requestId: input.requestId,
30477
- logDetail: result,
30478
- raw: result,
31009
+ functionName: entries[0]?.functionName || input.functionName || null,
31010
+ queryString,
31011
+ startTime: window.StartTime,
31012
+ endTime: window.EndTime,
31013
+ logs: entries,
31014
+ count: entries.length,
30479
31015
  }, (0, index_js_1.t)("functions.gotLogDetail", { requestId: input.requestId }));
30480
31016
  }
30481
31017
  case "listFunctionLayers": {
@@ -31851,6 +32387,7 @@ function registerFunctionTools(server) {
31851
32387
  .optional()
31852
32388
  .describe("functions.schema.query.requestId"),
31853
32389
  qualifier: zod_1.z.string().optional().describe("functions.schema.query.qualifier"),
32390
+ context: zod_1.z.string().optional().describe("functions.schema.query.context"),
31854
32391
  runtime: zod_1.z.string().optional().describe("functions.schema.query.runtime"),
31855
32392
  searchKey: zod_1.z.string().optional().describe("functions.schema.query.searchKey"),
31856
32393
  layerName: zod_1.z
@@ -31977,6 +32514,7 @@ const QUERY_GATEWAY_ACTIONS = [
31977
32514
  "getRoute",
31978
32515
  "listCustomDomains",
31979
32516
  "getPrivilege",
32517
+ "cachePurgeTask",
31980
32518
  ];
31981
32519
  const MANAGE_GATEWAY_ACTIONS = [
31982
32520
  "createRoute",
@@ -31988,6 +32526,7 @@ const MANAGE_GATEWAY_ACTIONS = [
31988
32526
  "deleteCustomDomain",
31989
32527
  "enableService",
31990
32528
  "authSwitch",
32529
+ "purgeCache",
31991
32530
  ];
31992
32531
  /** DomainType of the environment default HTTP service (gateway) domain. */
31993
32532
  const HTTP_SERVICE_DOMAIN_TYPE = "HTTPSERVICE";
@@ -31997,6 +32536,24 @@ const UPSTREAM_RESOURCE_TYPES = [
31997
32536
  "CBR",
31998
32537
  "STATIC_STORE",
31999
32538
  "LH",
32539
+ "STORAGE",
32540
+ ];
32541
+ /** Domain-level protocol accepted by HTTPServiceDomainParam. */
32542
+ const DOMAIN_PROTOCOLS = [
32543
+ "HTTP",
32544
+ "HTTPS",
32545
+ "HTTP_AND_HTTPS",
32546
+ "HTTP_TO_HTTPS",
32547
+ "HTTPS_TO_HTTP",
32548
+ ];
32549
+ /** Cache layer that owns the purged objects (CDN vs EdgeOne). */
32550
+ const CACHE_TYPES = ["CDN", "EO"];
32551
+ const PURGE_TYPES = ["PURGE_URL", "PURGE_PREFIX", "PURGE_HOST"];
32552
+ const HEADER_ADD_ACTIONS = [
32553
+ "APPEND_IF_EXISTS_OR_ADD",
32554
+ "ADD_IF_ABSENT",
32555
+ "OVERWRITE_IF_EXISTS_OR_ADD",
32556
+ "OVERWRITE_IF_EXISTS",
32000
32557
  ];
32001
32558
  function normalizeAccessPath(path) {
32002
32559
  if (!path) {
@@ -32004,6 +32561,225 @@ function normalizeAccessPath(path) {
32004
32561
  }
32005
32562
  return path.startsWith("/") ? path : `/${path}`;
32006
32563
  }
32564
+ // ---------------------------------------------------------------------------
32565
+ // Route/Domain extension schemas (aligned with manager-node HTTPServiceRouteParam)
32566
+ // ---------------------------------------------------------------------------
32567
+ const headerToAddSchema = zod_1.z.object({
32568
+ key: zod_1.z.string().describe("gateway.schema.manage.extension.header.key"),
32569
+ value: zod_1.z.string().describe("gateway.schema.manage.extension.header.value"),
32570
+ action: zod_1.z
32571
+ .enum(HEADER_ADD_ACTIONS)
32572
+ .optional()
32573
+ .describe("gateway.schema.manage.extension.header.action"),
32574
+ });
32575
+ const headersHandlerSchema = zod_1.z.object({
32576
+ requestHeadersToAdd: zod_1.z.array(headerToAddSchema).optional(),
32577
+ requestHeadersToRemove: zod_1.z.array(zod_1.z.string()).optional(),
32578
+ responseHeadersToAdd: zod_1.z.array(headerToAddSchema).optional(),
32579
+ responseHeadersToRemove: zod_1.z.array(zod_1.z.string()).optional(),
32580
+ });
32581
+ const cacheRuleSchema = zod_1.z.object({
32582
+ description: zod_1.z.string().optional(),
32583
+ enable: zod_1.z.boolean().optional(),
32584
+ condition: zod_1.z
32585
+ .object({
32586
+ target: zod_1.z.enum(["url_path", "file_extension", "full_uri"]).optional(),
32587
+ matchType: zod_1.z.enum(["prefix", "suffix", "contains", "exact"]).optional(),
32588
+ values: zod_1.z.array(zod_1.z.string()).optional(),
32589
+ })
32590
+ .describe("gateway.schema.manage.extension.cacheCondition"),
32591
+ actions: zod_1.z
32592
+ .array(zod_1.z.object({
32593
+ type: zod_1.z.enum(["Cache", "CacheKey"]).optional(),
32594
+ cache: zod_1.z
32595
+ .object({
32596
+ followOrigin: zod_1.z.boolean().optional(),
32597
+ noCache: zod_1.z.boolean().optional(),
32598
+ cacheTime: zod_1.z.number().int().min(0).optional(),
32599
+ maxAgeTime: zod_1.z.number().int().min(0).optional(),
32600
+ })
32601
+ .optional(),
32602
+ cacheKey: zod_1.z
32603
+ .object({
32604
+ fullUrlCache: zod_1.z.enum(["on", "off"]).optional(),
32605
+ queryStringSwitch: zod_1.z.enum(["on", "off"]).optional(),
32606
+ queryStringAction: zod_1.z.enum(["includeCustom", "excludeCustom"]).optional(),
32607
+ queryStringValues: zod_1.z.array(zod_1.z.string()).optional(),
32608
+ })
32609
+ .optional(),
32610
+ }))
32611
+ .describe("gateway.schema.manage.extension.cacheActions"),
32612
+ });
32613
+ const extensionSchema = zod_1.z.object({
32614
+ headersHandler: headersHandlerSchema.optional(),
32615
+ cache: zod_1.z.object({ rules: zod_1.z.array(cacheRuleSchema).optional() }).optional(),
32616
+ });
32617
+ const pathRewriteSchema = zod_1.z.object({
32618
+ prefix: zod_1.z.string().optional(),
32619
+ staticStorePrefix: zod_1.z.string().optional(),
32620
+ });
32621
+ const qpsPolicySchema = zod_1.z.object({
32622
+ total: zod_1.z.number().int().min(0).optional(),
32623
+ perClient: zod_1.z
32624
+ .object({
32625
+ limitBy: zod_1.z.enum(["ClientIP", "UserID"]).optional(),
32626
+ limitValue: zod_1.z.number().int().min(1).optional(),
32627
+ })
32628
+ .optional(),
32629
+ });
32630
+ function toHeadersHandler(input) {
32631
+ if (!input) {
32632
+ return undefined;
32633
+ }
32634
+ const output = {};
32635
+ const toHeader = (item) => item.action
32636
+ ? { Key: item.key, Value: item.value, Action: item.action }
32637
+ : { Key: item.key, Value: item.value };
32638
+ if (input.requestHeadersToAdd?.length) {
32639
+ output.RequestHeadersToAdd = input.requestHeadersToAdd.map(toHeader);
32640
+ }
32641
+ if (input.requestHeadersToRemove?.length) {
32642
+ output.RequestHeadersToRemove = input.requestHeadersToRemove;
32643
+ }
32644
+ if (input.responseHeadersToAdd?.length) {
32645
+ output.ResponseHeadersToAdd = input.responseHeadersToAdd.map(toHeader);
32646
+ }
32647
+ if (input.responseHeadersToRemove?.length) {
32648
+ output.ResponseHeadersToRemove = input.responseHeadersToRemove;
32649
+ }
32650
+ return Object.keys(output).length ? output : undefined;
32651
+ }
32652
+ function toCacheSet(input) {
32653
+ if (!input?.rules?.length) {
32654
+ return undefined;
32655
+ }
32656
+ return {
32657
+ Rules: input.rules.map((rule) => ({
32658
+ ...(rule.description !== undefined ? { Description: rule.description } : {}),
32659
+ ...(rule.enable !== undefined ? { Enable: rule.enable } : {}),
32660
+ Condition: {
32661
+ ...(rule.condition?.target ? { Target: rule.condition.target } : {}),
32662
+ ...(rule.condition?.matchType ? { MatchType: rule.condition.matchType } : {}),
32663
+ ...(rule.condition?.values ? { Values: rule.condition.values } : {}),
32664
+ },
32665
+ Actions: (rule.actions ?? []).map((action) => ({
32666
+ ...(action.type ? { Type: action.type } : {}),
32667
+ ...(action.cache
32668
+ ? {
32669
+ Cache: {
32670
+ ...(action.cache.followOrigin !== undefined
32671
+ ? { FollowOrigin: action.cache.followOrigin }
32672
+ : {}),
32673
+ ...(action.cache.noCache !== undefined
32674
+ ? { NoCache: action.cache.noCache }
32675
+ : {}),
32676
+ ...(action.cache.cacheTime !== undefined
32677
+ ? { CacheTime: action.cache.cacheTime }
32678
+ : {}),
32679
+ ...(action.cache.maxAgeTime !== undefined
32680
+ ? { MaxAgeTime: action.cache.maxAgeTime }
32681
+ : {}),
32682
+ },
32683
+ }
32684
+ : {}),
32685
+ ...(action.cacheKey
32686
+ ? {
32687
+ CacheKey: {
32688
+ ...(action.cacheKey.fullUrlCache
32689
+ ? { FullURLCache: action.cacheKey.fullUrlCache }
32690
+ : {}),
32691
+ ...(action.cacheKey.queryStringSwitch
32692
+ ? { QueryStringSwitch: action.cacheKey.queryStringSwitch }
32693
+ : {}),
32694
+ ...(action.cacheKey.queryStringAction
32695
+ ? { QueryStringAction: action.cacheKey.queryStringAction }
32696
+ : {}),
32697
+ ...(action.cacheKey.queryStringValues
32698
+ ? { QueryStringValues: action.cacheKey.queryStringValues }
32699
+ : {}),
32700
+ },
32701
+ }
32702
+ : {}),
32703
+ })),
32704
+ })),
32705
+ };
32706
+ }
32707
+ function toExtension(input) {
32708
+ if (!input) {
32709
+ return undefined;
32710
+ }
32711
+ const output = {};
32712
+ const headersHandler = toHeadersHandler(input.headersHandler);
32713
+ if (headersHandler) {
32714
+ output.HeadersHandler = headersHandler;
32715
+ }
32716
+ const cache = toCacheSet(input.cache);
32717
+ if (cache) {
32718
+ output.Cache = cache;
32719
+ }
32720
+ return Object.keys(output).length ? output : undefined;
32721
+ }
32722
+ function toPathRewrite(input) {
32723
+ if (!input) {
32724
+ return undefined;
32725
+ }
32726
+ const output = {};
32727
+ if (typeof input.prefix === "string") {
32728
+ output.Prefix = input.prefix;
32729
+ }
32730
+ if (typeof input.staticStorePrefix === "string") {
32731
+ output.StaticStorePrefix = input.staticStorePrefix;
32732
+ }
32733
+ return Object.keys(output).length ? output : undefined;
32734
+ }
32735
+ function toQpsPolicy(input) {
32736
+ if (!input) {
32737
+ return undefined;
32738
+ }
32739
+ const output = {};
32740
+ if (typeof input.total === "number") {
32741
+ output.QPSTotal = input.total;
32742
+ }
32743
+ if (input.perClient) {
32744
+ const perClient = {};
32745
+ if (input.perClient.limitBy) {
32746
+ perClient.LimitBy = input.perClient.limitBy;
32747
+ }
32748
+ if (typeof input.perClient.limitValue === "number") {
32749
+ perClient.LimitValue = input.perClient.limitValue;
32750
+ }
32751
+ if (Object.keys(perClient).length) {
32752
+ output.QPSPerClient = perClient;
32753
+ }
32754
+ }
32755
+ return Object.keys(output).length ? output : undefined;
32756
+ }
32757
+ /**
32758
+ * Route fields that ModifyHTTPServiceRoute replaces wholesale. When an update
32759
+ * omits one of them, the existing value must be carried over — otherwise a
32760
+ * partial update (e.g. only bumping QPS) would silently wipe CORS / rewrite /
32761
+ * headers / cache rules.
32762
+ */
32763
+ const PRESERVED_ROUTE_FIELDS = [
32764
+ "EnableAuth",
32765
+ "EnableSafeDomain",
32766
+ "EnablePathTransmission",
32767
+ "PathRewrite",
32768
+ "QPSPolicy",
32769
+ "Extension",
32770
+ "Enable",
32771
+ ];
32772
+ function carryOverRouteFields(route, existing) {
32773
+ if (!existing) {
32774
+ return route;
32775
+ }
32776
+ for (const field of PRESERVED_ROUTE_FIELDS) {
32777
+ if (route[field] === undefined && existing[field] !== undefined) {
32778
+ route[field] = existing[field];
32779
+ }
32780
+ }
32781
+ return route;
32782
+ }
32007
32783
  /**
32008
32784
  * Collect FAIL items from VerifyHTTPServiceRoute, mapping DNS records to
32009
32785
  * lowercase field names for MCP structured output (aligned with CLI 3.8.1).
@@ -32446,16 +33222,13 @@ function registerGatewayTools(server) {
32446
33222
  });
32447
33223
  const path = normalizeAccessPath(input.route?.path ?? input.path ?? `/${upstreamResourceName}`);
32448
33224
  const domain = await resolveRouteDomain(input.domain);
32449
- const enableAuth = input.route?.auth !== undefined
32450
- ? input.route.auth
32451
- : input.auth !== undefined
32452
- ? input.auth
32453
- : undefined;
32454
- const enablePathTransmission = input.route?.enablePathTransmission !== undefined
32455
- ? input.route.enablePathTransmission
32456
- : input.enablePathTransmission !== undefined
32457
- ? input.enablePathTransmission
32458
- : undefined;
33225
+ const pick = (routeValue, topValue) => routeValue !== undefined ? routeValue : topValue;
33226
+ const enableAuth = pick(input.route?.auth, input.auth);
33227
+ const enableSafeDomain = pick(input.route?.enableSafeDomain, input.enableSafeDomain);
33228
+ const enablePathTransmission = pick(input.route?.enablePathTransmission, input.enablePathTransmission);
33229
+ const routePathRewrite = toPathRewrite(pick(input.route?.pathRewrite, input.pathRewrite));
33230
+ const routeQpsPolicy = toQpsPolicy(pick(input.route?.qpsPolicy, input.qpsPolicy));
33231
+ const routeExtension = toExtension(pick(input.route?.extension, input.extension));
32459
33232
  // Route enable: prefer route.enable. For updateRoute only, top-level
32460
33233
  // enable also maps to Route.Enable (enableService/authSwitch use the
32461
33234
  // same field name but different actions).
@@ -32469,18 +33242,35 @@ function registerGatewayTools(server) {
32469
33242
  Path: path,
32470
33243
  UpstreamResourceType: upstreamResourceType,
32471
33244
  UpstreamResourceName: upstreamResourceName,
32472
- EnableAuth: enableAuth,
32473
33245
  };
33246
+ if (enableAuth !== undefined) {
33247
+ route.EnableAuth = enableAuth;
33248
+ }
33249
+ if (enableSafeDomain !== undefined) {
33250
+ route.EnableSafeDomain = enableSafeDomain;
33251
+ }
32474
33252
  if (enablePathTransmission !== undefined) {
32475
33253
  route.EnablePathTransmission = enablePathTransmission;
32476
33254
  }
33255
+ if (routePathRewrite) {
33256
+ route.PathRewrite = routePathRewrite;
33257
+ }
33258
+ if (routeQpsPolicy) {
33259
+ route.QPSPolicy = routeQpsPolicy;
33260
+ }
33261
+ if (routeExtension) {
33262
+ route.Extension = routeExtension;
33263
+ }
32477
33264
  if (routeEnable !== undefined) {
32478
33265
  route.Enable = routeEnable;
32479
33266
  }
33267
+ const domainExtension = toExtension(input.domainExtension);
32480
33268
  return {
32481
33269
  EnvId: await resolveEnvId(),
32482
33270
  Domain: {
32483
33271
  Domain: domain,
33272
+ ...(input.protocol ? { Protocol: input.protocol } : {}),
33273
+ ...(domainExtension ? { Extension: domainExtension } : {}),
32484
33274
  Routes: [route],
32485
33275
  },
32486
33276
  resolved: {
@@ -32489,6 +33279,7 @@ function registerGatewayTools(server) {
32489
33279
  upstreamResourceType,
32490
33280
  upstreamResourceName,
32491
33281
  enableAuth,
33282
+ enableSafeDomain,
32492
33283
  enablePathTransmission,
32493
33284
  enable: routeEnable,
32494
33285
  },
@@ -32537,6 +33328,57 @@ function registerGatewayTools(server) {
32537
33328
  }
32538
33329
  return matches[0];
32539
33330
  };
33331
+ /**
33332
+ * Locate the live record (domain + route) so partial updates can carry over
33333
+ * fields the caller did not touch.
33334
+ */
33335
+ const findExistingRouteRecord = async (domain, path) => {
33336
+ const result = await listHttpServiceRoutes({ domain });
33337
+ const domainRecord = (result.Domains ?? []).find((item) => item.Domain === domain);
33338
+ const route = (domainRecord?.Routes ?? []).find((item) => normalizeAccessPath(String(item.Path ?? "")) ===
33339
+ path);
33340
+ return {
33341
+ domainRecord,
33342
+ route: route,
33343
+ };
33344
+ };
33345
+ /** CDN vs EO is decided by the domain's access type; default to CDN. */
33346
+ /**
33347
+ * 缓存层由域名的接入方式决定,不能猜。
33348
+ *
33349
+ * - EO / CDN → 对应缓存层
33350
+ * - DIRECT / CUSTOM / NONE → CloudBase 侧没有托管缓存,刷新不适用。
33351
+ * 实测(2026-10-21)对 DIRECT 域发 CDN 刷新,PurgeHTTPServiceCache 会「成功」登记
33352
+ * 一个任务,而 DescribeHTTPServiceCachePurgeTask 随后抛 Third Service Error ——
33353
+ * 即后端对 DIRECT 域缺校验、返回不可读错误。这里提前拦住并说明原因。
33354
+ */
33355
+ const resolveDomainCacheType = async (domain, preferred) => {
33356
+ const findDomain = (result) => (result.Domains ?? []).find((item) => item.Domain === domain);
33357
+ let record = findDomain(await listHttpServiceRoutes({ domain }));
33358
+ if (!record) {
33359
+ // 域名下暂时没有路由时,筛 Domain 可能查不到,退回全量再找一次。
33360
+ record = findDomain(await listHttpServiceRoutes());
33361
+ }
33362
+ if (!record) {
33363
+ throw new Error((0, index_js_1.t)("gateway.cache.domainNotFound", { domain }));
33364
+ }
33365
+ const accessType = String(record.AccessType ?? "");
33366
+ const boundCacheType = accessType === "EO" ? "EO" : accessType === "CDN" ? "CDN" : undefined;
33367
+ if (!boundCacheType) {
33368
+ throw new Error((0, index_js_1.t)("gateway.cache.notManaged", {
33369
+ domain,
33370
+ accessType: accessType || "UNKNOWN",
33371
+ }));
33372
+ }
33373
+ if (preferred && preferred !== boundCacheType) {
33374
+ throw new Error((0, index_js_1.t)("gateway.cache.typeMismatch", {
33375
+ domain,
33376
+ accessType: boundCacheType,
33377
+ requested: preferred,
33378
+ }));
33379
+ }
33380
+ return boundCacheType;
33381
+ };
32540
33382
  const routeMutationNextActions = (targetName) => [
32541
33383
  {
32542
33384
  tool: "queryGateway",
@@ -32649,6 +33491,34 @@ function registerGatewayTools(server) {
32649
33491
  },
32650
33492
  ]);
32651
33493
  }
33494
+ case "cachePurgeTask": {
33495
+ if (!input.domain) {
33496
+ throw new Error((0, index_js_1.t)("gateway.query.cachePurgeTaskDomainRequired"));
33497
+ }
33498
+ const cacheType = await resolveDomainCacheType(input.domain, input.cacheType);
33499
+ const cloudbase = await getManager();
33500
+ const result = await cloudbase.env.describeHttpServiceCachePurgeTask({
33501
+ EnvId: await resolveEnvId(),
33502
+ Domain: input.domain,
33503
+ CacheType: cacheType,
33504
+ ...(input.taskId ? { TaskId: input.taskId } : {}),
33505
+ ...(input.purgeType ? { PurgeType: input.purgeType } : {}),
33506
+ ...(input.startTime ? { StartTime: input.startTime } : {}),
33507
+ ...(input.endTime ? { EndTime: input.endTime } : {}),
33508
+ ...(input.offset !== undefined ? { Offset: input.offset } : {}),
33509
+ ...(input.limit !== undefined ? { Limit: input.limit } : {}),
33510
+ });
33511
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
33512
+ const tasks = Array.isArray(result?.Tasks) ? result.Tasks : [];
33513
+ return buildEnvelope({
33514
+ action: input.action,
33515
+ domain: input.domain,
33516
+ cacheType,
33517
+ tasks,
33518
+ total: result?.TotalCount ?? tasks.length,
33519
+ raw: result,
33520
+ }, (0, index_js_1.t)("gateway.query.cachePurgeTask", { count: tasks.length }));
33521
+ }
32652
33522
  default:
32653
33523
  throw new Error((0, index_js_1.t)("gateway.error.unsupportedAction", { action: input.action }));
32654
33524
  }
@@ -32757,6 +33627,7 @@ function registerGatewayTools(server) {
32757
33627
  upstreamResourceType: payload.resolved.upstreamResourceType,
32758
33628
  upstreamResourceName: payload.resolved.upstreamResourceName,
32759
33629
  auth: payload.resolved.enableAuth ?? null,
33630
+ enableSafeDomain: payload.resolved.enableSafeDomain ?? null,
32760
33631
  enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
32761
33632
  accessUrl: `https://${payload.resolved.domain}${payload.resolved.path}`,
32762
33633
  accessUrls: [`https://${payload.resolved.domain}${payload.resolved.path}`],
@@ -32785,6 +33656,14 @@ function registerGatewayTools(server) {
32785
33656
  case "updateRoute": {
32786
33657
  const cloudbase = await getManager();
32787
33658
  const payload = await normalizeRoutePayload(input);
33659
+ // Preserve untouched route/domain fields from the live record: the
33660
+ // backend replaces the whole route entry, so a partial update would
33661
+ // otherwise wipe CORS / rewrite / QPS / headers / cache settings.
33662
+ const existing = await findExistingRouteRecord(payload.resolved.domain, payload.resolved.path);
33663
+ carryOverRouteFields(payload.Domain.Routes[0], existing.route);
33664
+ if (!payload.Domain.Protocol && existing.domainRecord?.Protocol) {
33665
+ payload.Domain.Protocol = existing.domainRecord.Protocol;
33666
+ }
32788
33667
  const result = await cloudbase.env.modifyHttpServiceRoute({
32789
33668
  EnvId: payload.EnvId,
32790
33669
  Domain: payload.Domain,
@@ -32802,6 +33681,7 @@ function registerGatewayTools(server) {
32802
33681
  upstreamResourceType: payload.resolved.upstreamResourceType,
32803
33682
  upstreamResourceName: payload.resolved.upstreamResourceName,
32804
33683
  auth: payload.resolved.enableAuth ?? null,
33684
+ enableSafeDomain: payload.resolved.enableSafeDomain ?? null,
32805
33685
  enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
32806
33686
  enable: payload.resolved.enable ?? null,
32807
33687
  ...(accessUrl
@@ -32883,6 +33763,11 @@ function registerGatewayTools(server) {
32883
33763
  if (enablePathTransmission !== undefined) {
32884
33764
  route.EnablePathTransmission = enablePathTransmission;
32885
33765
  }
33766
+ if (input.enableSafeDomain !== undefined) {
33767
+ route.EnableSafeDomain = input.enableSafeDomain;
33768
+ }
33769
+ // Toggling Enable must not drop CORS / rewrite / QPS / headers / cache.
33770
+ carryOverRouteFields(route, existing);
32886
33771
  const cloudbase = await getManager();
32887
33772
  const result = await cloudbase.env.modifyHttpServiceRoute({
32888
33773
  EnvId: await resolveEnvId(),
@@ -32977,6 +33862,7 @@ function registerGatewayTools(server) {
32977
33862
  CertId: input.certificateId || "",
32978
33863
  AccessType: accessType,
32979
33864
  Enable: input.enable !== undefined ? input.enable : true,
33865
+ ...(input.protocol ? { Protocol: input.protocol } : {}),
32980
33866
  ...(input.customCname ? { CustomCname: input.customCname } : {}),
32981
33867
  };
32982
33868
  const verifyFailure = await verifyHttpServiceRouteOrFail({
@@ -32998,6 +33884,7 @@ function registerGatewayTools(server) {
32998
33884
  Domain: input.domain,
32999
33885
  CertId: certificateId,
33000
33886
  AccessType: accessType,
33887
+ ...(input.protocol ? { Protocol: input.protocol } : {}),
33001
33888
  ...(input.enable !== undefined
33002
33889
  ? { Enable: input.enable }
33003
33890
  : {}),
@@ -33128,6 +34015,43 @@ function registerGatewayTools(server) {
33128
34015
  },
33129
34016
  ]);
33130
34017
  }
34018
+ case "purgeCache": {
34019
+ if (!input.domain) {
34020
+ throw new Error((0, index_js_1.t)("gateway.purgeCache.domainRequired"));
34021
+ }
34022
+ const targets = (input.targets ?? [])
34023
+ .map((item) => (typeof item === "string" ? item.trim() : ""))
34024
+ .filter(Boolean);
34025
+ if (!targets.length) {
34026
+ throw new Error((0, index_js_1.t)("gateway.purgeCache.targetsRequired"));
34027
+ }
34028
+ const purgeType = input.purgeType ?? "PURGE_URL";
34029
+ const cacheType = await resolveDomainCacheType(input.domain, input.cacheType);
34030
+ const cloudbase = await getManager();
34031
+ const result = await cloudbase.env.purgeHttpServiceCache({
34032
+ EnvId: await resolveEnvId(),
34033
+ Domain: input.domain,
34034
+ CacheType: cacheType,
34035
+ PurgeType: purgeType,
34036
+ Targets: targets,
34037
+ });
34038
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
34039
+ return buildEnvelope({
34040
+ action: input.action,
34041
+ domain: input.domain,
34042
+ cacheType,
34043
+ purgeType,
34044
+ targets,
34045
+ taskId: result?.TaskId ?? null,
34046
+ raw: result,
34047
+ }, (0, index_js_1.t)("gateway.purgeCache.message", { count: targets.length }), [
34048
+ {
34049
+ tool: "queryGateway",
34050
+ action: "cachePurgeTask",
34051
+ reason: (0, index_js_1.t)("gateway.purgeCache.verifyReason"),
34052
+ },
34053
+ ]);
34054
+ }
33131
34055
  default:
33132
34056
  throw new Error((0, index_js_1.t)("gateway.error.unsupportedAction", { action: input.action }));
33133
34057
  }
@@ -33146,6 +34070,19 @@ function registerGatewayTools(server) {
33146
34070
  routeId: zod_1.z.string().optional().describe("gateway.schema.query.routeId"),
33147
34071
  path: zod_1.z.string().optional().describe("gateway.schema.query.path"),
33148
34072
  domain: zod_1.z.string().optional().describe("gateway.schema.query.domain"),
34073
+ cacheType: zod_1.z
34074
+ .enum(CACHE_TYPES)
34075
+ .optional()
34076
+ .describe("gateway.schema.query.cacheType"),
34077
+ taskId: zod_1.z.string().optional().describe("gateway.schema.query.taskId"),
34078
+ purgeType: zod_1.z
34079
+ .enum(PURGE_TYPES)
34080
+ .optional()
34081
+ .describe("gateway.schema.query.purgeType"),
34082
+ startTime: zod_1.z.string().optional().describe("gateway.schema.query.startTime"),
34083
+ endTime: zod_1.z.string().optional().describe("gateway.schema.query.endTime"),
34084
+ offset: zod_1.z.number().int().min(0).optional().describe("gateway.schema.query.offset"),
34085
+ limit: zod_1.z.number().int().min(1).optional().describe("gateway.schema.query.limit"),
33149
34086
  },
33150
34087
  annotations: {
33151
34088
  readOnlyHint: true,
@@ -33175,6 +34112,26 @@ function registerGatewayTools(server) {
33175
34112
  .boolean()
33176
34113
  .optional()
33177
34114
  .describe("gateway.schema.manage.enablePathTransmission"),
34115
+ enableSafeDomain: zod_1.z
34116
+ .boolean()
34117
+ .optional()
34118
+ .describe("gateway.schema.manage.enableSafeDomain"),
34119
+ pathRewrite: pathRewriteSchema
34120
+ .optional()
34121
+ .describe("gateway.schema.manage.pathRewrite"),
34122
+ qpsPolicy: qpsPolicySchema
34123
+ .optional()
34124
+ .describe("gateway.schema.manage.qpsPolicy"),
34125
+ extension: extensionSchema
34126
+ .optional()
34127
+ .describe("gateway.schema.manage.extension"),
34128
+ domainExtension: extensionSchema
34129
+ .optional()
34130
+ .describe("gateway.schema.manage.domainExtension"),
34131
+ protocol: zod_1.z
34132
+ .enum(DOMAIN_PROTOCOLS)
34133
+ .optional()
34134
+ .describe("gateway.schema.manage.protocol"),
33178
34135
  route: zod_1.z
33179
34136
  .object({
33180
34137
  path: zod_1.z.string().optional(),
@@ -33191,6 +34148,19 @@ function registerGatewayTools(server) {
33191
34148
  .boolean()
33192
34149
  .optional()
33193
34150
  .describe("gateway.schema.manage.route.enablePathTransmission"),
34151
+ enableSafeDomain: zod_1.z
34152
+ .boolean()
34153
+ .optional()
34154
+ .describe("gateway.schema.manage.route.enableSafeDomain"),
34155
+ pathRewrite: pathRewriteSchema
34156
+ .optional()
34157
+ .describe("gateway.schema.manage.route.pathRewrite"),
34158
+ qpsPolicy: qpsPolicySchema
34159
+ .optional()
34160
+ .describe("gateway.schema.manage.route.qpsPolicy"),
34161
+ extension: extensionSchema
34162
+ .optional()
34163
+ .describe("gateway.schema.manage.route.extension"),
33194
34164
  enable: zod_1.z
33195
34165
  .boolean()
33196
34166
  .optional()
@@ -33212,6 +34182,18 @@ function registerGatewayTools(server) {
33212
34182
  .optional()
33213
34183
  .describe("gateway.schema.manage.customCname"),
33214
34184
  enable: zod_1.z.boolean().optional().describe("gateway.schema.manage.enable"),
34185
+ cacheType: zod_1.z
34186
+ .enum(CACHE_TYPES)
34187
+ .optional()
34188
+ .describe("gateway.schema.manage.cacheType"),
34189
+ purgeType: zod_1.z
34190
+ .enum(PURGE_TYPES)
34191
+ .optional()
34192
+ .describe("gateway.schema.manage.purgeType"),
34193
+ targets: zod_1.z
34194
+ .array(zod_1.z.string())
34195
+ .optional()
34196
+ .describe("gateway.schema.manage.targets"),
33215
34197
  },
33216
34198
  annotations: {
33217
34199
  readOnlyHint: false,
@@ -42278,7 +43260,7 @@ class TelemetryReporter {
42278
43260
  const arch = os_1.default.arch(); // 系统架构
42279
43261
  // Build-time injected version; guard for vitest / non-webpack runtimes.
42280
43262
  const mcpVersion = process.env.npm_package_version ||
42281
- ( true ? "2.36.0" : 0);
43263
+ ( true ? "2.36.1" : 0);
42282
43264
  return {
42283
43265
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
42284
43266
  deviceId: this.deviceId,
@@ -43233,7 +44215,7 @@ function isReturnedFailureResult(result) {
43233
44215
  * 所以两条路径对同一个底层错误得到的文本逐字相同(🆔 RequestId + 📦 版本页脚都在这里)。
43234
44216
  */
43235
44217
  function buildFailureClientText(errorMessage, requestId, server) {
43236
- const mcpVersion = true ? "2.36.0" : 0;
44218
+ const mcpVersion = true ? "2.36.1" : 0;
43237
44219
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
43238
44220
  return `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n${contributionHint(server)}`;
43239
44221
  }