@cloudbase/cloudbase-mcp 2.34.2 → 2.34.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -201380,6 +201380,7 @@ exports.apps = (0, types_js_1.defineModule)({
201380
201380
  getSuccess: "CloudBase 应用详情查询成功",
201381
201381
  listVersionsSuccess: "CloudBase 应用版本列表查询成功",
201382
201382
  buildIdRequired: "action=getBuildLog 时必须提供 buildId",
201383
+ buildIdMustBeNumeric: "buildId 必须是数字(云 API DescribeCloudBaseRunBuildLog 的 BuildId 为整数)。收到的值:{buildId}。请使用部署回执或 getAppVersion 返回的 BuildId。",
201383
201384
  buildLogFound: "查询到 {count} 条构建日志",
201384
201385
  buildLogEmpty: "暂无构建日志",
201385
201386
  nextStepQueryBuildLog: "查询构建日志",
@@ -201451,6 +201452,7 @@ exports.apps = (0, types_js_1.defineModule)({
201451
201452
  getSuccess: "CloudBase app details retrieved successfully",
201452
201453
  listVersionsSuccess: "CloudBase app version list retrieved successfully",
201453
201454
  buildIdRequired: "buildId is required when action=getBuildLog",
201455
+ buildIdMustBeNumeric: "buildId must be numeric (the BuildId of the cloud API DescribeCloudBaseRunBuildLog is an integer). Received: {buildId}. Use the BuildId returned by the deploy response or getAppVersion.",
201454
201456
  buildLogFound: "Retrieved {count} build log entries",
201455
201457
  buildLogEmpty: "No build logs yet",
201456
201458
  nextStepQueryBuildLog: "Query build logs",
@@ -203585,6 +203587,9 @@ exports.gateway = (0, types_js_1.defineModule)({
203585
203587
  "create.backendError": "为目标资源配置访问路由失败(后端内部错误)。请确保:1) 目标云函数已成功创建并处于 Active 状态;2) 环境默认 HTTP 域名已完成初始化(IsDefault);3) 该访问路径未被占用。",
203586
203588
  "create.backendErrorScfHint": "此外注意:HTTP 云函数必须用 upstreamResourceType=WEB_SCF,Event 云函数必须用 SCF;互标会导致此错误。",
203587
203589
  "create.backendErrorSuffix": " 原始错误:{message}",
203590
+ "create.upstreamNotFound": "createRoute 未执行:上游目标不存在。upstreamResourceType={type}、targetName={name} 在环境中未找到,未创建任何路由。请先创建该资源(或核对名称/类型),再重新调用 createRoute。",
203591
+ "create.upstreamCandidates": "当前环境已有的 {type} 目标(最多 10 个,供核对名称):{candidates}",
203592
+ "create.upstreamNotFoundReason": "核对上游资源名称与 upstreamResourceType 后重试 createRoute",
203588
203593
  "create.privilegeOffHint": "⚠️ HTTP 网关总开关未开启,路由创建成功后访问仍将返回 HTTPSERVICE_NONACTIVATED(403);请先调用 manageGateway(action=\"enableService\", enable=true) 开启,再立刻探测 accessUrl(通常数秒到约 30 秒内生效,勿盲等 60 秒以上)。",
203589
203594
  "create.privilegeOffReason": "开启 HTTP 网关总开关(EnableService),否则访问路由会报 HTTPSERVICE_NONACTIVATED(403)",
203590
203595
  "create.privilegeUnknownHint": "(无法确认 HTTP 网关开关状态;若访问报 HTTPSERVICE_NONACTIVATED,请用 queryGateway(action=\"getPrivilege\") 查询后用 manageGateway(action=\"enableService\") 开启)",
@@ -203670,6 +203675,9 @@ exports.gateway = (0, types_js_1.defineModule)({
203670
203675
  "create.backendError": "Failed to configure the access route for the target resource (backend internal error). Ensure: 1) the target cloud function is created and Active; 2) the env default HTTP domain is initialized (IsDefault); 3) the access path is not taken.",
203671
203676
  "create.backendErrorScfHint": "Also note: HTTP cloud functions must use upstreamResourceType=WEB_SCF, Event cloud functions must use SCF; swapping them causes this error.",
203672
203677
  "create.backendErrorSuffix": " Original error: {message}",
203678
+ "create.upstreamNotFound": "createRoute skipped: the upstream target does not exist. upstreamResourceType={type}, targetName={name} was not found in this environment; no route was created. Create the resource (or fix the name/type), then call createRoute again.",
203679
+ "create.upstreamCandidates": "Existing {type} targets in this environment (up to 10, for name verification): {candidates}",
203680
+ "create.upstreamNotFoundReason": "Verify the upstream resource name and upstreamResourceType, then retry createRoute",
203673
203681
  "create.privilegeOffHint": "⚠️ The HTTP gateway master switch is off; after the route is created, access will still return HTTPSERVICE_NONACTIVATED (403). Call manageGateway(action=\"enableService\", enable=true) first, then probe accessUrl immediately (usually takes effect within a few seconds to ~30 seconds; do not wait 60+ seconds).",
203674
203682
  "create.privilegeOffReason": "Turn on the HTTP gateway master switch (EnableService), otherwise accessing the route returns HTTPSERVICE_NONACTIVATED (403)",
203675
203683
  "create.privilegeUnknownHint": " (Could not confirm the HTTP gateway switch status; if access returns HTTPSERVICE_NONACTIVATED, query via queryGateway(action=\"getPrivilege\") and enable via manageGateway(action=\"enableService\"))",
@@ -204157,9 +204165,9 @@ exports.permissions = void 0;
204157
204165
  const types_js_1 = __webpack_require__(89921);
204158
204166
  exports.permissions = (0, types_js_1.defineModule)({
204159
204167
  queryTitle: "查询 CloudBase 权限与用户配置",
204160
- queryDescription: "查询 CloudBase 权限与用户配置,支持查询资源权限(数据库/云函数/存储桶等)、角色列表/详情、应用用户列表/详情,以及网关 OPA 授权策略(对齐 CLI `tcb policy list/get`)。\n\n示例:\n- 查询存储桶权限:`action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\"`\n- 列出旧网关策略:`action=\"listPolicy\"`(PG / OPA 引擎环境返回空列表,与 CLI 一致)\n- 读取用户 Rego:`action=\"getPolicy\"`;平台扩展策略:`action=\"getPolicy\", extension=true`\n\n📌 跨后端边界提示:调用前先用 `envQuery(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`。`resourceType=\"noSqlDatabase\"` 查询的是 CloudBase NoSQL 集合规则,与 CloudBase PostgreSQL(PG)表的行级安全(RLS)是两套独立机制——同一个 PG 环境里 NoSQL 集合若仍在使用,对那些集合查询本工具结果**仍然有效**。要查 PG 表 RLS,请改用 `queryPgDatabase(action=\"sql\", sql=\"SELECT * FROM pg_policies WHERE tablename=...\")`。本工具不涉及 MySQL 权限。\n\n⚠️ PostgreSQL 环境:平台 `DescribeResourcePermission` 对 PG 环境会直接拒绝。当 `resourceType=\"function\"` 时,本工具会自动回退到 Manager SDK `describeEnvAuthzConfig`(与 CLI `tcb policy get` 一致,读取 `authz.user.rego`)。显式 OPA 策略请用 `listPolicy` / `getPolicy`。",
204168
+ queryDescription: "查询 CloudBase 权限与用户配置,支持查询资源权限(数据库/云函数/存储桶等)、角色列表/详情、应用用户列表/详情,以及网关 OPA 授权策略(对齐 CLI `tcb policy list/get`)。\n\n示例:\n- 查询存储桶权限:`action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\"`\n- 列出旧网关策略:`action=\"listPolicy\"`(PG / OPA 引擎环境返回空列表,与 CLI 一致)\n- 读取用户 Rego:`action=\"getPolicy\"`;平台扩展策略:`action=\"getPolicy\", extension=true`\n\n📌 跨后端边界提示:调用前先用 `queryEnv(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`。`resourceType=\"noSqlDatabase\"` 查询的是 CloudBase NoSQL 集合规则,与 CloudBase PostgreSQL(PG)表的行级安全(RLS)是两套独立机制——同一个 PG 环境里 NoSQL 集合若仍在使用,对那些集合查询本工具结果**仍然有效**。要查 PG 表 RLS,请改用 `queryPgDatabase(action=\"sql\", sql=\"SELECT * FROM pg_policies WHERE tablename=...\")`。本工具不涉及 MySQL 权限。\n\n⚠️ PostgreSQL 环境:平台 `DescribeResourcePermission` 对 PG 环境会直接拒绝。当 `resourceType=\"function\"` 时,本工具会自动回退到 Manager SDK `describeEnvAuthzConfig`(与 CLI `tcb policy get` 一致,读取 `authz.user.rego`)。显式 OPA 策略请用 `listPolicy` / `getPolicy`。\n\n⚠️ 角色类 action(`listRoles` / `getRole`)在 **PostgreSQL 类型环境**下平台侧一律拒绝(`The current API does not support PostgreSQL type environments.`)——这是平台能力边界,不是配置问题,**不要在 PG 环境反复重试**;PG 环境请改用 RLS(`managePgDatabase(action=\"execute\")` 跑 `CREATE POLICY`)或控制台管理权限。",
204161
204169
  manageTitle: "管理 CloudBase 权限与用户配置",
204162
- manageDescription: "管理 CloudBase 权限与用户配置,支持修改资源权限(数据库/云函数/存储桶等)、角色管理、成员与策略增删、应用用户 CRUD,以及设置网关 OPA Rego 策略(对齐 CLI `tcb policy set`)。\n\n示例:\n- 设置存储桶为私有:`action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"PRIVATE\"`\n- 创建角色:`action=\"createRole\", roleName=\"admin\", roleIdentity=\"admin\"`\n- 放开云函数匿名/未登录访问(PG 会走 OPA,对齐 CLI `tcb policy set`):`action=\"updateResourcePermission\", resourceType=\"function\", resourceId=\"myFn\", permission=\"CUSTOM\", securityRule='{\"invoke\":true}'`\n- 直接设置用户 Rego:`action=\"setPolicy\", regoContent=\"package authz.user\\n\\ndefault allow := false\\n\", confirm=true`(⚠️ 立即禁用旧网关鉴权)\n\n注意:`createUser` / `updateUser` 是环境侧应用用户管理能力,适合测试账号、管理员或预置用户,不应替代浏览器里的 Web SDK 注册表单;前端用户名密码注册应使用 `auth.signUp({ username, password })`,登录应使用 `auth.signInWithPassword({ username, password })`。直接在浏览器里用 `auth.signUp` 创建用户名密码用户取决于 SDK/provider 支持,使用前必须验证;不支持时应走后端或管理端边界,不能在浏览器暴露密钥。`securityRule` 的详细语义取决于 `resourceType`:`doc._openid`、`auth.openid`、查询条件子集校验,以及 `create` / `update` / `delete` JSON 模板仅适用于 `resourceType=\"noSqlDatabase\"` 的文档数据库安全规则;配置 `function` 或 `storage` 时,请参考各自官方安全规则文档,而不是复用 NoSQL 模板。\n\n📌 跨后端边界提示:调用前先用 `envQuery(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`:\n- `resourceType=\"noSqlDatabase\"` 仅作用于 CloudBase NoSQL 文档数据库的集合;CloudBase PostgreSQL(PG)表的行级权限**不**受它控制——PG 表请改用 RLS:`managePgDatabase(action=\"execute\", confirm=true)` 跑 `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` 与 `CREATE POLICY ...`。同一个 PG 环境里如果还有 NoSQL 集合在用,对那些**集合**继续使用 `noSqlDatabase` 规则是正确的——不是\"PG 环境就禁用本工具\"。\n- `resourceType=\"storage\"` 控制的是 NoSQL/COS 存储桶 ACL;PG 的 `pgstore` bucket 不在此 `resourceType` 覆盖范围内。\n- 本工具不涉及 MySQL;MySQL 数据库权限请走 MySQL 自身的 GRANT/REVOKE 语句(通过 `manageMysqlDatabase`)。\n\n⚠️ PostgreSQL 环境:平台 `ModifyResourcePermission` 对 PG 环境会直接拒绝。当 `resourceType=\"function\"` 时,本工具会自动回退到 Manager SDK `modifyEnvAuthzConfig`(与 CLI `tcb policy set` 一致,写入 `authz.user.rego`)。`securityRule` 可传完整 Rego(`package authz.user`)或 `'{\"invoke\":true}'`(自动生成放通 anonymous/unauthenticated 调 functions 的策略)。设置 Rego 后旧网关鉴权会失效,行为与 CLI 相同。显式 OPA 策略请优先用 `action=\"setPolicy\"`。",
204170
+ manageDescription: "管理 CloudBase 权限与用户配置,支持修改资源权限(数据库/云函数/存储桶等)、角色管理、成员与策略增删、应用用户 CRUD,以及设置网关 OPA Rego 策略(对齐 CLI `tcb policy set`)。\n\n示例:\n- 设置存储桶为私有:`action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"PRIVATE\"`\n- 创建角色:`action=\"createRole\", roleName=\"admin\", roleIdentity=\"admin\"`\n- 放开云函数匿名/未登录访问(PG 会走 OPA,对齐 CLI `tcb policy set`):`action=\"updateResourcePermission\", resourceType=\"function\", resourceId=\"myFn\", permission=\"CUSTOM\", securityRule='{\"invoke\":true}'`\n- 直接设置用户 Rego:`action=\"setPolicy\", regoContent=\"package authz.user\\n\\ndefault allow := false\\n\", confirm=true`(⚠️ 立即禁用旧网关鉴权)\n\n注意:`createUser` / `updateUser` 是环境侧应用用户管理能力,适合测试账号、管理员或预置用户,不应替代浏览器里的 Web SDK 注册表单;前端用户名密码注册应使用 `auth.signUp({ username, password })`,登录应使用 `auth.signInWithPassword({ username, password })`。直接在浏览器里用 `auth.signUp` 创建用户名密码用户取决于 SDK/provider 支持,使用前必须验证;不支持时应走后端或管理端边界,不能在浏览器暴露密钥。`securityRule` 的详细语义取决于 `resourceType`:`doc._openid`、`auth.openid`、查询条件子集校验,以及 `create` / `update` / `delete` JSON 模板仅适用于 `resourceType=\"noSqlDatabase\"` 的文档数据库安全规则;配置 `function` 或 `storage` 时,请参考各自官方安全规则文档,而不是复用 NoSQL 模板。\n\n📌 跨后端边界提示:调用前先用 `queryEnv(action=\"info\", envId=...)` 看 `EnvInfo.RuntimeBackends`:\n- `resourceType=\"noSqlDatabase\"` 仅作用于 CloudBase NoSQL 文档数据库的集合;CloudBase PostgreSQL(PG)表的行级权限**不**受它控制——PG 表请改用 RLS:`managePgDatabase(action=\"execute\", confirm=true)` 跑 `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` 与 `CREATE POLICY ...`。同一个 PG 环境里如果还有 NoSQL 集合在用,对那些**集合**继续使用 `noSqlDatabase` 规则是正确的——不是\"PG 环境就禁用本工具\"。\n- `resourceType=\"storage\"` 控制的是 NoSQL/COS 存储桶 ACL;PG 的 `pgstore` bucket 不在此 `resourceType` 覆盖范围内。\n- 本工具不涉及 MySQL;MySQL 数据库权限请走 MySQL 自身的 GRANT/REVOKE 语句(通过 `manageMysqlDatabase`)。\n\n⚠️ PostgreSQL 环境:平台 `ModifyResourcePermission` 对 PG 环境会直接拒绝。当 `resourceType=\"function\"` 时,本工具会自动回退到 Manager SDK `modifyEnvAuthzConfig`(与 CLI `tcb policy set` 一致,写入 `authz.user.rego`)。`securityRule` 可传完整 Rego(`package authz.user`)或 `'{\"invoke\":true}'`(自动生成放通 anonymous/unauthenticated 调 functions 的策略)。设置 Rego 后旧网关鉴权会失效,行为与 CLI 相同。显式 OPA 策略请优先用 `action=\"setPolicy\"`。\n\n⚠️ 角色类 action(`createRole` / `deleteRoles` / `updateRole`)在 **PostgreSQL 类型环境**下平台侧一律拒绝(`The current API does not support PostgreSQL type environments.`)——平台能力边界,不是配置问题,**不要在 PG 环境反复重试**;PG 环境请改用 RLS(`managePgDatabase(action=\"execute\")` 跑 `CREATE POLICY`)或控制台管理权限。",
204163
204171
  writeVerificationHint: "对于 {resourceId} 这类有后端权限控制的集合,前端调用 .doc(id).update() / .doc(id).remove() 后,不能只看是否没有抛异常。请显式检查返回结果中的 updated / deleted 是否大于 0;如果 result.code、result.message 存在,或 updated / deleted 为 0,要把它当作真实失败并向上抛错。",
204164
204172
  permissionPropagationHint: "刚更新完 {resourceId} 的安全规则时,后端权限通常在数秒到约 30 秒内生效。若紧接着的真实写操作仍返回 DATABASE_PERMISSION_DENIED,请先间隔数秒用同一登录态重试同一条 .doc(id).update() / .doc(id).remove();不要盲等数分钟,也不要立刻连续重写规则,更不要在短暂传播窗口里把旧拒绝直接当成规则表达式仍然错误。",
204165
204173
  validateRegoEmpty: "action=setPolicy 需要非空 regoContent(对齐 CLI `tcb policy set <regoContent>`)。",
@@ -204226,9 +204234,9 @@ exports.permissions = (0, types_js_1.defineModule)({
204226
204234
  setPolicySuccess: "用户 OPA Rego 策略设置成功(旧网关鉴权已失效)",
204227
204235
  }, {
204228
204236
  queryTitle: "Query CloudBase permissions and user config",
204229
- queryDescription: "Query CloudBase permissions and user config: resource permissions (database/functions/buckets etc.), role list/detail, app user list/detail, and gateway OPA authorization policies (aligned with CLI `tcb policy list/get`).\n\nExamples:\n- Query bucket permission: `action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\"`\n- List legacy gateway policies: `action=\"listPolicy\"` (returns an empty list on PG / OPA engine environments, consistent with the CLI)\n- Read user Rego: `action=\"getPolicy\"`; platform extension policy: `action=\"getPolicy\", extension=true`\n\n📌 Cross-backend boundary note: before calling, check `EnvInfo.RuntimeBackends` via `envQuery(action=\"info\", envId=...)`. `resourceType=\"noSqlDatabase\"` queries CloudBase NoSQL collection rules, which are a separate mechanism from row-level security (RLS) on CloudBase PostgreSQL (PG) tables — in the same PG environment, if NoSQL collections are still in use, querying them with this tool **remains valid**. To query PG table RLS, use `queryPgDatabase(action=\"sql\", sql=\"SELECT * FROM pg_policies WHERE tablename=...\")` instead. This tool does not cover MySQL permissions.\n\n⚠️ PostgreSQL environments: the platform `DescribeResourcePermission` API rejects PG environments outright. When `resourceType=\"function\"`, this tool automatically falls back to the Manager SDK `describeEnvAuthzConfig` (aligned with CLI `tcb policy get`, reading `authz.user.rego`). For explicit OPA policies use `listPolicy` / `getPolicy`.",
204237
+ queryDescription: "Query CloudBase permissions and user config: resource permissions (database/functions/buckets etc.), role list/detail, app user list/detail, and gateway OPA authorization policies (aligned with CLI `tcb policy list/get`).\n\nExamples:\n- Query bucket permission: `action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\"`\n- List legacy gateway policies: `action=\"listPolicy\"` (returns an empty list on PG / OPA engine environments, consistent with the CLI)\n- Read user Rego: `action=\"getPolicy\"`; platform extension policy: `action=\"getPolicy\", extension=true`\n\n📌 Cross-backend boundary note: before calling, check `EnvInfo.RuntimeBackends` via `queryEnv(action=\"info\", envId=...)`. `resourceType=\"noSqlDatabase\"` queries CloudBase NoSQL collection rules, which are a separate mechanism from row-level security (RLS) on CloudBase PostgreSQL (PG) tables — in the same PG environment, if NoSQL collections are still in use, querying them with this tool **remains valid**. To query PG table RLS, use `queryPgDatabase(action=\"sql\", sql=\"SELECT * FROM pg_policies WHERE tablename=...\")` instead. This tool does not cover MySQL permissions.\n\n⚠️ PostgreSQL environments: the platform `DescribeResourcePermission` API rejects PG environments outright. When `resourceType=\"function\"`, this tool automatically falls back to the Manager SDK `describeEnvAuthzConfig` (aligned with CLI `tcb policy get`, reading `authz.user.rego`). For explicit OPA policies use `listPolicy` / `getPolicy`.\n\n⚠️ Role actions (`listRoles` / `getRole`) are rejected outright by the platform on **PostgreSQL environments** (`The current API does not support PostgreSQL type environments.`). This is a platform capability boundary, not a configuration problem — **do not keep retrying on PG**; use RLS (`managePgDatabase(action=\"execute\")` with `CREATE POLICY`) or the console to manage permissions instead.",
204230
204238
  manageTitle: "Manage CloudBase permissions and user config",
204231
- manageDescription: "Manage CloudBase permissions and user config: modify resource permissions (database/functions/buckets etc.), role management, member/policy add & remove, app user CRUD, and setting gateway OPA Rego policies (aligned with CLI `tcb policy set`).\n\nExamples:\n- Set a bucket to private: `action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"PRIVATE\"`\n- Create a role: `action=\"createRole\", roleName=\"admin\", roleIdentity=\"admin\"`\n- Open a cloud function to anonymous/unauthenticated access (PG goes through OPA, aligned with CLI `tcb policy set`): `action=\"updateResourcePermission\", resourceType=\"function\", resourceId=\"myFn\", permission=\"CUSTOM\", securityRule='{\"invoke\":true}'`\n- Set user Rego directly: `action=\"setPolicy\", regoContent=\"package authz.user\\n\\ndefault allow := false\\n\", confirm=true` (⚠️ immediately disables legacy gateway authorization)\n\nNote: `createUser` / `updateUser` are environment-side app user management capabilities, suitable for test accounts, administrators, or pre-provisioned users; they should not replace the Web SDK signup form in the browser. For frontend username/password signup use `auth.signUp({ username, password })` and for login `auth.signInWithPassword({ username, password })`. Creating username/password users directly in the browser with `auth.signUp` depends on SDK/provider support and must be verified before use; if unsupported, go through a backend or management API boundary and never expose keys in the browser. The detailed semantics of `securityRule` depend on `resourceType`: `doc._openid`, `auth.openid`, query-condition subset validation, and the `create` / `update` / `delete` JSON templates only apply to document database security rules with `resourceType=\"noSqlDatabase\"`; when configuring `function` or `storage`, refer to their official security rule docs instead of reusing NoSQL templates.\n\n📌 Cross-backend boundary note: before calling, check `EnvInfo.RuntimeBackends` via `envQuery(action=\"info\", envId=...)`:\n- `resourceType=\"noSqlDatabase\"` only affects CloudBase NoSQL document database collections; row-level permissions of CloudBase PostgreSQL (PG) tables are **not** controlled by it — for PG tables use RLS instead: run `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` and `CREATE POLICY ...` via `managePgDatabase(action=\"execute\", confirm=true)`. In the same PG environment, if NoSQL collections are still in use, continuing to use `noSqlDatabase` rules for those **collections** is correct — it does not mean \"this tool is disabled on PG environments\".\n- `resourceType=\"storage\"` controls NoSQL/COS bucket ACLs; PG `pgstore` buckets are not covered by this `resourceType`.\n- This tool does not cover MySQL; for MySQL database permissions use MySQL's own GRANT/REVOKE statements (via `manageMysqlDatabase`).\n\n⚠️ PostgreSQL environments: the platform `ModifyResourcePermission` API rejects PG environments outright. When `resourceType=\"function\"`, this tool automatically falls back to the Manager SDK `modifyEnvAuthzConfig` (aligned with CLI `tcb policy set`, writing `authz.user.rego`). `securityRule` accepts a full Rego document (`package authz.user`) or `'{\"invoke\":true}'` (auto-generates a policy allowing anonymous/unauthenticated calls to functions). Setting Rego disables legacy gateway authorization, same as the CLI. Prefer `action=\"setPolicy\"` for explicit OPA policies.",
204239
+ manageDescription: "Manage CloudBase permissions and user config: modify resource permissions (database/functions/buckets etc.), role management, member/policy add & remove, app user CRUD, and setting gateway OPA Rego policies (aligned with CLI `tcb policy set`).\n\nExamples:\n- Set a bucket to private: `action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"PRIVATE\"`\n- Create a role: `action=\"createRole\", roleName=\"admin\", roleIdentity=\"admin\"`\n- Open a cloud function to anonymous/unauthenticated access (PG goes through OPA, aligned with CLI `tcb policy set`): `action=\"updateResourcePermission\", resourceType=\"function\", resourceId=\"myFn\", permission=\"CUSTOM\", securityRule='{\"invoke\":true}'`\n- Set user Rego directly: `action=\"setPolicy\", regoContent=\"package authz.user\\n\\ndefault allow := false\\n\", confirm=true` (⚠️ immediately disables legacy gateway authorization)\n\nNote: `createUser` / `updateUser` are environment-side app user management capabilities, suitable for test accounts, administrators, or pre-provisioned users; they should not replace the Web SDK signup form in the browser. For frontend username/password signup use `auth.signUp({ username, password })` and for login `auth.signInWithPassword({ username, password })`. Creating username/password users directly in the browser with `auth.signUp` depends on SDK/provider support and must be verified before use; if unsupported, go through a backend or management API boundary and never expose keys in the browser. The detailed semantics of `securityRule` depend on `resourceType`: `doc._openid`, `auth.openid`, query-condition subset validation, and the `create` / `update` / `delete` JSON templates only apply to document database security rules with `resourceType=\"noSqlDatabase\"`; when configuring `function` or `storage`, refer to their official security rule docs instead of reusing NoSQL templates.\n\n📌 Cross-backend boundary note: before calling, check `EnvInfo.RuntimeBackends` via `queryEnv(action=\"info\", envId=...)`:\n- `resourceType=\"noSqlDatabase\"` only affects CloudBase NoSQL document database collections; row-level permissions of CloudBase PostgreSQL (PG) tables are **not** controlled by it — for PG tables use RLS instead: run `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` and `CREATE POLICY ...` via `managePgDatabase(action=\"execute\", confirm=true)`. In the same PG environment, if NoSQL collections are still in use, continuing to use `noSqlDatabase` rules for those **collections** is correct — it does not mean \"this tool is disabled on PG environments\".\n- `resourceType=\"storage\"` controls NoSQL/COS bucket ACLs; PG `pgstore` buckets are not covered by this `resourceType`.\n- This tool does not cover MySQL; for MySQL database permissions use MySQL's own GRANT/REVOKE statements (via `manageMysqlDatabase`).\n\n⚠️ PostgreSQL environments: the platform `ModifyResourcePermission` API rejects PG environments outright. When `resourceType=\"function\"`, this tool automatically falls back to the Manager SDK `modifyEnvAuthzConfig` (aligned with CLI `tcb policy set`, writing `authz.user.rego`). `securityRule` accepts a full Rego document (`package authz.user`) or `'{\"invoke\":true}'` (auto-generates a policy allowing anonymous/unauthenticated calls to functions). Setting Rego disables legacy gateway authorization, same as the CLI. Prefer `action=\"setPolicy\"` for explicit OPA policies.\n\n⚠️ Role actions (`createRole` / `deleteRoles` / `updateRole`) are rejected outright by the platform on **PostgreSQL environments** (`The current API does not support PostgreSQL type environments.`). This is a platform capability boundary, not a configuration problem — **do not keep retrying on PG**; use RLS (`managePgDatabase(action=\"execute\")` with `CREATE POLICY`) or the console to manage permissions instead.",
204232
204240
  writeVerificationHint: "For collections with backend permission control like {resourceId}, after the frontend calls .doc(id).update() / .doc(id).remove(), do not just check that no exception was thrown. Explicitly verify that updated / deleted in the result is greater than 0; if result.code or result.message exists, or updated / deleted is 0, treat it as a real failure and propagate the error.",
204233
204241
  permissionPropagationHint: "Right after updating security rules for {resourceId}, backend permissions usually take effect within a few seconds to about 30 seconds. If an immediate real write still returns DATABASE_PERMISSION_DENIED, wait a few seconds and retry the same .doc(id).update() / .doc(id).remove() with the same login state; do not wait blindly for minutes, do not immediately rewrite the rules repeatedly, and do not treat an old rejection during the short propagation window as evidence that the rule expression is still wrong.",
204234
204242
  validateRegoEmpty: "action=setPolicy requires non-empty regoContent (aligned with CLI `tcb policy set <regoContent>`).",
@@ -204326,11 +204334,11 @@ exports.rag = (0, types_js_1.defineModule)({
204326
204334
 
204327
204335
  返回内容包含该 skill 的 SKILL.md 全文,以及它在远端聚合仓(CNB raw)中的全部 .md 文件地址清单(SKILL.md 与 references/ 等,可直接 HTTP 抓取)。正文中代码栅栏之外的相对链接也会改写为绝对地址;若该 skill 在远端仓中不存在,则只返回内联内容并明确标注,不返回失效链接。
204328
204336
 
204329
- 固定技能文档 (skill) 查询当前支持 {skillCount} 个固定文档,分别是:
204330
- {skillList}
204337
+ 不确定该选哪个时:mode=skill 下不传 skillName、mode=openapi 下不传 apiName 直接调用,会返回当前可用清单及各自的适用场景 / 接口简介,再带上名称重新调用即可。可选名称也见本工具的 skillName / apiName 枚举(skill 共 {skillCount} 个,API 共 {openapiCount} 个)。
204331
204338
 
204332
- OpenAPI 文档 (openapi) 查询只需要传 mode="openapi" 和 apiName,不要传 action;action 仅用于 mode="docs"。当前支持 {openapiCount} 个 API 文档,分别是:
204333
- {openapiList}`,
204339
+ 注意:OpenAPI 文档 (openapi) 查询只需要传 mode="openapi" 和 apiName,不要传 action;action 仅用于 mode="docs"。`,
204340
+ skillCatalogHeader: "CloudBase 技能文档 (skill) 当前可用 {count} 个,用 mode=skill + skillName 读取其中一篇:",
204341
+ openapiCatalogHeader: "OpenAPI 文档 (openapi) 当前可用 {count} 个,用 mode=openapi + apiName 读取其中一篇:",
204334
204342
  skillListItem: "文档名:{name} 文档介绍:{description}",
204335
204343
  openapiListItem: "API名:{name} API介绍:{description}",
204336
204344
  paramRequired: "action={action} 时必须提供 {param}",
@@ -204374,11 +204382,11 @@ exports.rag = (0, types_js_1.defineModule)({
204374
204382
 
204375
204383
  The response contains the full SKILL.md of the skill plus the list of all its .md file URLs in the remote aggregate repo (CNB raw) — SKILL.md and references/ etc., fetchable over HTTP. Relative links outside code fences are rewritten to absolute URLs; when the skill is absent from the remote repo only the inline content is returned and that is stated explicitly, so no dead links are handed out.
204376
204384
 
204377
- Fixed skill doc (skill) queries currently support {skillCount} fixed docs:
204378
- {skillList}
204385
+ When unsure which one to pick: call with mode=skill and no skillName, or mode=openapi and no apiName, to get the current catalog with each entry's scenario / summary, then call again with the name. Available names are also listed in this tool's skillName / apiName enums (currently {skillCount} skills, {openapiCount} APIs).
204379
204386
 
204380
- OpenAPI doc (openapi) queries only need mode="openapi" and apiName; do not pass action — action is only for mode="docs". Currently {openapiCount} API docs are supported:
204381
- {openapiList}`,
204387
+ Note: OpenAPI doc (openapi) queries only need mode="openapi" and apiName; do not pass action — action is only for mode="docs".`,
204388
+ skillCatalogHeader: "CloudBase skill docs (skill), {count} available. Use mode=skill + skillName to read one:",
204389
+ openapiCatalogHeader: "OpenAPI docs (openapi), {count} available. Use mode=openapi + apiName to read one:",
204382
204390
  skillListItem: "Skill: {name} — Description: {description}",
204383
204391
  openapiListItem: "API: {name} — Description: {description}",
204384
204392
  paramRequired: "{param} is required when action={action}",
@@ -205218,7 +205226,8 @@ function registerAgentTools(server) {
205218
205226
  },
205219
205227
  annotations: {
205220
205228
  readOnlyHint: true,
205221
- openWorldHint: true,
205229
+ destructiveHint: false,
205230
+ openWorldHint: false,
205222
205231
  category: "agents",
205223
205232
  },
205224
205233
  }, async ({ action, agentId, pageNumber, pageSize, params, }) => {
@@ -205292,7 +205301,7 @@ function registerAgentTools(server) {
205292
205301
  readOnlyHint: false,
205293
205302
  destructiveHint: true,
205294
205303
  idempotentHint: false,
205295
- openWorldHint: true,
205304
+ openWorldHint: false,
205296
205305
  category: "agents",
205297
205306
  },
205298
205307
  }, async ({ action, agentId, name, description, runtime, timeout, memorySize, installDependency, zipFile, cosBucketRegion, tempCosObjectName, sessionConfig, cwd, params, }) => {
@@ -205747,7 +205756,8 @@ function registerAppAuthTools(server) {
205747
205756
  },
205748
205757
  annotations: {
205749
205758
  readOnlyHint: true,
205750
- openWorldHint: true,
205759
+ destructiveHint: false,
205760
+ openWorldHint: false,
205751
205761
  category: "auth",
205752
205762
  },
205753
205763
  }, async ({ action, providerId, clientId, keyType, pageNumber, pageSize, }) => withEnvelope(async () => {
@@ -205884,7 +205894,7 @@ function registerAppAuthTools(server) {
205884
205894
  readOnlyHint: false,
205885
205895
  destructiveHint: true,
205886
205896
  idempotentHint: false,
205887
- openWorldHint: true,
205897
+ openWorldHint: false,
205888
205898
  category: "auth",
205889
205899
  },
205890
205900
  }, async ({ action, patch, providerId, providerType, displayName, clientId, config, keyType, keyName, expireIn, keyId, }) => withEnvelope(async () => {
@@ -206180,9 +206190,15 @@ function registerAppTools(server) {
206180
206190
  .optional()
206181
206191
  .describe("版本名称。getAppVersion 时可与 buildId 二选一;已知版本号时优先传该值。"),
206182
206192
  buildId: zod_1.z
206183
- .string()
206193
+ // ⚠️ 同一字段驱动两条链路、类型要求不同(F13):
206194
+ // getAppVersion → SDK describeAppVersion,收 **string**
206195
+ // getBuildLog → 云 API DescribeCloudBaseRunBuildLog,BuildId 是 **Integer(int64)**
206196
+ // 收 string | number 并统一归一成 string(保证 getAppVersion 链路不回退),
206197
+ // getBuildLog 分支再转 number 传给云 API。
206198
+ .union([zod_1.z.string(), zod_1.z.number()])
206199
+ .transform((value) => String(value).trim())
206184
206200
  .optional()
206185
- .describe("构建 ID。getAppVersion 时可与 versionName 二选一;部署返回 BuildId 后可直接用它轮询状态。getBuildLog 时必填。"),
206201
+ .describe("构建 ID(数字或数字字符串均可)。getAppVersion 时可与 versionName 二选一;部署返回 BuildId 后可直接用它轮询状态。getBuildLog 时必填。"),
206186
206202
  start: zod_1.z
206187
206203
  .number()
206188
206204
  .optional()
@@ -206190,7 +206206,8 @@ function registerAppTools(server) {
206190
206206
  },
206191
206207
  annotations: {
206192
206208
  readOnlyHint: true,
206193
- openWorldHint: true,
206209
+ destructiveHint: false,
206210
+ openWorldHint: false,
206194
206211
  category: "apps",
206195
206212
  },
206196
206213
  }, async ({ action, serviceName, searchKey, pageNo, pageSize, versionName, buildId, start, }) => {
@@ -206284,15 +206301,22 @@ function registerAppTools(server) {
206284
206301
  }, (0, index_js_1.t)("apps.listVersionsSuccess")));
206285
206302
  }
206286
206303
  if (action === "getBuildLog") {
206287
- if (!buildId) {
206304
+ if (buildId === undefined || buildId === null || String(buildId).trim() === "") {
206288
206305
  throw new Error((0, index_js_1.t)("apps.buildIdRequired"));
206289
206306
  }
206307
+ // DescribeCloudBaseRunBuildLog 的 BuildId 是 Integer(int64)(官方文档 876/135707),
206308
+ // 传 string 会被后端拒:The value type of parameter BuildId is not valid, input type
206309
+ // should be int64。schema 侧已归一成 string(getAppVersion 需要 string),此处转 number。
206310
+ const numericBuildId = Number(String(buildId).trim());
206311
+ if (!Number.isInteger(numericBuildId) || numericBuildId <= 0) {
206312
+ throw new Error((0, index_js_1.t)("apps.buildIdMustBeNumeric", { buildId: String(buildId) }));
206313
+ }
206290
206314
  const result = await cloudbase.commonService("tcb", "2018-06-08").call({
206291
206315
  Action: "DescribeCloudBaseRunBuildLog",
206292
206316
  Param: {
206293
206317
  EnvId: cloudBaseOptions?.envId || process.env.CLOUDBASE_ENV_ID,
206294
206318
  ServiceName: serviceName,
206295
- BuildId: buildId,
206319
+ BuildId: numericBuildId,
206296
206320
  Start: start ?? 0,
206297
206321
  },
206298
206322
  });
@@ -206301,7 +206325,7 @@ function registerAppTools(server) {
206301
206325
  return (0, json_content_js_1.jsonContent)(buildEnvelope({
206302
206326
  action,
206303
206327
  serviceName,
206304
- buildId,
206328
+ buildId: String(numericBuildId),
206305
206329
  logs,
206306
206330
  total: result.Response?.Total || logs.length,
206307
206331
  nextStart: result.Response?.NextStart,
@@ -206314,7 +206338,8 @@ function registerAppTools(server) {
206314
206338
  deployType: "static-hosting",
206315
206339
  serviceName,
206316
206340
  versionName,
206317
- buildId,
206341
+ // 该链路要 string(SDK 类型 IDescribeCloudAppVersionParams.BuildId: string)
206342
+ buildId: buildId === undefined ? undefined : String(buildId),
206318
206343
  });
206319
206344
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
206320
206345
  // Platform may return Failed/failed; normalize before matching.
@@ -206366,12 +206391,21 @@ function registerAppTools(server) {
206366
206391
  .optional()
206367
206392
  .describe("要上传并部署的本地项目根目录绝对路径。本地模式下 deployApp 时必填;通常传源码所在目录(含 package.json 和源码),不是 dist 目录。构建产物目录请用 buildPath 指定。cloud mode 下无需传此参数,改用 cosTimestamp。"),
206368
206393
  cosTimestamp: zod_1.z
206369
- .coerce
206370
- .number()
206371
- .int()
206372
- .positive()
206394
+ // ⚠️ 不能用 z.coerce.number():SDK 的 StaticConfig.CosTimestamp(manager-node
206395
+ // types/cloudApp/types.d.ts `CosTimestamp?: string | null`)与后端 CreateCloudApp
206396
+ // 都要求 **string**。coerce 会把外部传入的值(含 getUploadUrl 返回的 unixTimestamp)
206397
+ // 强制转成 number,后端直接拒绝:
206398
+ // The value type of parameter `StaticConfig.CosTimestamp` is not valid,
206399
+ // input type should be `string`
206400
+ // 本地路径不受影响,是因为 cosTs 来自 SDK uploadCode() 的 string 返回值、不经 zod。
206401
+ // 因此这里收 string | number,统一归一成 string 后再透传。
206402
+ .union([zod_1.z.string(), zod_1.z.number()])
206403
+ .transform((value) => String(value).trim())
206404
+ .refine((value) => /^[1-9]\d*$/.test(value), {
206405
+ message: "cosTimestamp 必须是由数字组成的正整数时间戳,字符串或数字均可;直接使用 getUploadUrl 返回的 unixTimestamp 即可。",
206406
+ })
206373
206407
  .optional()
206374
- .describe("COS 时间戳(正整数 number,来自 getUploadUrl 返回的 unixTimestamp)。传入此值则直接使用已上传的代码创建应用,跳过本地文件上传。需先调用 getUploadUrl 获取预签名 URL,上传 ZIP 包后再传此时间戳。cloud mode 下为必填;本地模式也可传此值代替 filePath。两个路径严格二选一:filePath(本地打包上传)或 cosTimestamp(预签名 URL 上传),同时提供或都不提供都会报错。"),
206408
+ .describe("COS 时间戳(getUploadUrl 返回的 unixTimestamp,字符串或数字均可)。传入则直接用已上传的代码创建应用,跳过本地打包上传;需先 getUploadUrl 拿预签名 URL 并 PUT ZIP。cloud mode 必填。与 filePath 严格二选一,同时提供或都不提供都会报错。"),
206375
206409
  appPath: zod_1.z
206376
206410
  .string()
206377
206411
  .optional()
@@ -206415,7 +206449,7 @@ function registerAppTools(server) {
206415
206449
  readOnlyHint: false,
206416
206450
  destructiveHint: true,
206417
206451
  idempotentHint: false,
206418
- openWorldHint: true,
206452
+ openWorldHint: false,
206419
206453
  category: "apps",
206420
206454
  },
206421
206455
  }, async ({ action, serviceName, filePath, cosTimestamp, appPath, buildPath, framework, nodeJsVersion, installCmd, buildCmd, deployCmd, ignore, versionName, }) => {
@@ -206505,7 +206539,10 @@ function registerAppTools(server) {
206505
206539
  }
206506
206540
  }
206507
206541
  // Local stdio only: pack directory and upload. Cloud mode must never reach uploadCode.
206508
- let cosTs = cosTimestamp;
206542
+ // ⚠️ 类型契约(F12):SDK `StaticConfig.CosTimestamp` 与后端 CreateCloudApp 都要求
206543
+ // **string**。本地路径拿到的 `uploadResult.cosTimestamp` 本就是 SDK 给的 string;
206544
+ // 外部传入的可能是 number(MCP 客户端按 JSON Schema 传数字),这里统一归一。
206545
+ let cosTs = cosTimestamp === undefined ? undefined : String(cosTimestamp);
206509
206546
  if (!(0, cloud_mode_js_1.isCloudMode)() && filePath) {
206510
206547
  // Default excludes large build dirs (empirically target/ can be tens of GB).
206511
206548
  // Merge caller ignore with defaults so explicit ignore does not drop safety excludes.
@@ -206520,7 +206557,9 @@ function registerAppTools(server) {
206520
206557
  ignore: mergedIgnore,
206521
206558
  });
206522
206559
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, uploadResult);
206523
- cosTs = uploadResult.cosTimestamp;
206560
+ cosTs = uploadResult.cosTimestamp === undefined
206561
+ ? undefined
206562
+ : String(uploadResult.cosTimestamp);
206524
206563
  }
206525
206564
  // 构建命令智能默认值
206526
206565
  const resolvedInstallCmd = installCmd ?? "npm install";
@@ -207049,7 +207088,15 @@ function registerCapiTools(server) {
207049
207088
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions, requireEnvId: false });
207050
207089
  server.registerTool?.("callCloudApi", {
207051
207090
  title: "capi.title",
207052
- description: "capi.description",
207091
+ // ⚠️ 不能写成 `description: "capi.description"`(纯词典 key):注册包装层解析
207092
+ // key 时**不带插值参数**,而 {controlPlaneUrl}/{dependencyUrl} 只在错误路径
207093
+ // (下方 t("capi.errorBuild", …))传入 → 描述里的占位符永远替换不了,
207094
+ // tools/list 在 zh 与 en 下都会吐字面 `{controlPlaneUrl}`。
207095
+ // 因此这里按实例语言先解析成最终文案(与 env.ts 的 envQuery 别名同款处理)。
207096
+ description: (0, index_js_1.t)("capi.description", {
207097
+ controlPlaneUrl: CLOUDBASE_CONTROL_PLANE_DOC_URL,
207098
+ dependencyUrl: CLOUDBASE_DEPENDENCY_API_DOC_URL,
207099
+ }, server.lang),
207053
207100
  inputSchema: {
207054
207101
  service: zod_1.z
207055
207102
  .enum(exports.ALLOWED_SERVICES)
@@ -207078,7 +207125,7 @@ function registerCapiTools(server) {
207078
207125
  readOnlyHint: false,
207079
207126
  destructiveHint: true,
207080
207127
  idempotentHint: false,
207081
- openWorldHint: true,
207128
+ openWorldHint: false,
207082
207129
  category: CATEGORY,
207083
207130
  },
207084
207131
  }, async ({ service, action, params, version, region, }) => {
@@ -207377,6 +207424,7 @@ exports.buildCloudRunDeployNextStep = buildCloudRunDeployNextStep;
207377
207424
  exports.buildCloudRunDeployProgressHint = buildCloudRunDeployProgressHint;
207378
207425
  exports.queryCloudRunEnvStatus = queryCloudRunEnvStatus;
207379
207426
  exports.describeCloudRunEnvStatus = describeCloudRunEnvStatus;
207427
+ exports.isCloudRunEnvNotOpenedError = isCloudRunEnvNotOpenedError;
207380
207428
  exports.extractEnvBaseInfoVpc = extractEnvBaseInfoVpc;
207381
207429
  exports.resolveCloudRunDeployVpcInfo = resolveCloudRunDeployVpcInfo;
207382
207430
  exports.buildCreateCloudRunEnvParam = buildCreateCloudRunEnvParam;
@@ -207480,7 +207528,7 @@ const ManageCloudRunInputSchema = {
207480
207528
  PolicyThreshold: zod_1.z.number().min(1).max(100).describe('扩缩容阈值,单位为百分比。如60表示当资源使用率达到60%时触发扩缩容')
207481
207529
  })).optional().describe('扩缩容配置数组,用于配置服务的自动扩缩容策略。可配置多个扩缩容策略'),
207482
207530
  CustomLogs: zod_1.z.string().optional().describe('自定义日志配置,用于配置服务的日志收集和存储策略'),
207483
- Port: zod_1.z.number().min(1).max(65535).optional().describe('服务监听端口配置。函数型服务固定为3000,容器型服务可自定义。服务代码必须监听此端口才能正常接收请求'),
207531
+ Port: zod_1.z.number().min(1).max(65535).optional().describe('服务监听端口配置。函数型服务固定为3000(函数框架自身监听该端口,业务代码不要自行 app.listen);容器型服务可自定义,业务代码必须监听此端口'),
207484
207532
  EnvParams: zod_1.z.string().optional().describe('环境变量配置,JSON字符串格式。用于传递配置信息给服务代码,如\'{"DATABASE_URL":"postgres://user:pass@10.x.x.x:5432/db","NODE_ENV":"production"}\'。SDK v5.6.1+ 会自动对传入的环境变量进行 AES-256-CBC 加密传输。⚠️ 若 EnvParams 含 DATABASE_URL / MYSQL_* / POSTGRES_* / REDIS_* 等传统 TCP 连库变量,必须同时配置 VpcConf,否则实例通常无法访问 VPC 内数据库'),
207485
207533
  Dockerfile: zod_1.z.string().optional().describe('Dockerfile文件名配置,仅容器型服务需要。指定用于构建容器镜像的Dockerfile文件路径,默认为项目根目录下的Dockerfile'),
207486
207534
  BuildDir: zod_1.z.string().optional().describe('构建目录配置,指定代码构建的目录路径。当代码结构与标准不同时使用,默认为项目根目录'),
@@ -207957,6 +208005,36 @@ async function describeCloudRunEnvStatus(manager, envId) {
207957
208005
  : "unknown";
207958
208006
  return { isExist: true, status, baseInfo };
207959
208007
  }
208008
+ /**
208009
+ * 判断「云托管环境未开通 / 不存在」类错误,用于 `describeCloudRunEnvStatus` 失败的归一化分支。
208010
+ *
208011
+ * ⚠️ 必须**同时**看 `error.code` 与 `error.message`:
208012
+ * 报错文案有三套来源且会不一致 —— 英文错误码(`ResourceNotFound.CloudRunEnv`)、
208013
+ * 中文兜底(`[DescribeEnvBaseInfo] 资源不存在`)、旧式 `InvalidParameter.*Env`。
208014
+ * 只匹配 message 时,中文「资源不存在」不含 `ResourceNotFound`/`未开通`/`未初始化`,
208015
+ * 守卫会失效并把裸错误抛给模型(国际站未开通云托管的环境实测命中,见 F2)。
208016
+ *
208017
+ * 调用方只有 `DescribeEnvBaseInfo`(唯一资源就是环境),因此把「资源不存在」纳入
208018
+ * 未开通判定不会误伤其他语义。
208019
+ */
208020
+ function isCloudRunEnvNotOpenedError(error) {
208021
+ if (!error) {
208022
+ return false;
208023
+ }
208024
+ const codeCandidates = [
208025
+ error.code,
208026
+ error.Code,
208027
+ error.original?.code,
208028
+ error.original?.Code,
208029
+ ];
208030
+ const codes = codeCandidates
208031
+ .filter((value) => typeof value === "string")
208032
+ .join(" ");
208033
+ const message = error instanceof Error ? error.message : String(error);
208034
+ const haystack = `${codes} ${message}`;
208035
+ return (/ResourceNotFound|not.?initialized|未开通|未初始化|资源不存在|环境不存在/i.test(haystack) ||
208036
+ /InvalidParameter.*(?:Env|CloudRun)/i.test(haystack));
208037
+ }
207960
208038
  /** CloudRun EnvType for CreateCloudRunEnv (baas DescribeEnvBaseInfo enum). */
207961
208039
  exports.CLOUDRUN_ENV_TYPE = "baas";
207962
208040
  /**
@@ -208057,12 +208135,11 @@ async function ensureCloudRunEnvInitialized(options) {
208057
208135
  return true;
208058
208136
  }
208059
208137
  catch (error) {
208060
- const msg = error instanceof Error ? error.message : String(error);
208061
- // 错误兜底:未初始化/未开通类错误码。
208138
+ // 错误兜底:未初始化/未开通类错误码。判据统一收敛到 isCloudRunEnvNotOpenedError,
208139
+ // 避免「只匹配 message」被本地化文案(中文「资源不存在」)绕过。
208062
208140
  // 注意:裸 InvalidParameter 可能是普通参数错误,不拦截;仅当其带 Env/CloudRun
208063
208141
  // 上下文(疑似 "EnvironmentId not found / CloudRun Env 未开通")时才按未初始化处理。
208064
- if (/ResourceNotFound|not.?initialized|未开通|未初始化/i.test(msg) ||
208065
- /InvalidParameter.*(?:Env|CloudRun)/i.test(msg)) {
208142
+ if (isCloudRunEnvNotOpenedError(error)) {
208066
208143
  throwCloudRunEnvNotInitialized(options.envId);
208067
208144
  }
208068
208145
  // 其他错误(网络/权限等)不拦截,让上层按原逻辑处理。
@@ -208232,7 +208309,8 @@ function registerCloudRunTools(server) {
208232
208309
  inputSchema: queryCloudRunInputSchema,
208233
208310
  annotations: {
208234
208311
  readOnlyHint: true,
208235
- openWorldHint: true,
208312
+ destructiveHint: false,
208313
+ openWorldHint: false,
208236
208314
  category: "cloudrun"
208237
208315
  }
208238
208316
  }, async (args) => {
@@ -208626,8 +208704,7 @@ function registerCloudRunTools(server) {
208626
208704
  }
208627
208705
  catch (error) {
208628
208706
  const baseMessage = error instanceof Error ? error.message : String(error);
208629
- if (/ResourceNotFound|not.?initialized|未开通|未初始化/i.test(baseMessage) ||
208630
- /InvalidParameter.*(?:Env|CloudRun)/i.test(baseMessage)) {
208707
+ if (isCloudRunEnvNotOpenedError(error)) {
208631
208708
  status = { isExist: false, status: "unopened", baseInfo: {} };
208632
208709
  }
208633
208710
  else {
@@ -208692,7 +208769,7 @@ function registerCloudRunTools(server) {
208692
208769
  readOnlyHint: false,
208693
208770
  destructiveHint: true,
208694
208771
  idempotentHint: false,
208695
- openWorldHint: true,
208772
+ openWorldHint: false,
208696
208773
  category: "cloudrun"
208697
208774
  }
208698
208775
  }, async (args) => {
@@ -208720,8 +208797,7 @@ function registerCloudRunTools(server) {
208720
208797
  }
208721
208798
  catch (error) {
208722
208799
  const baseMessage = error instanceof Error ? error.message : String(error);
208723
- if (/ResourceNotFound|not.?initialized|未开通|未初始化/i.test(baseMessage) ||
208724
- /InvalidParameter.*(?:Env|CloudRun)/i.test(baseMessage)) {
208800
+ if (isCloudRunEnvNotOpenedError(error)) {
208725
208801
  current = { isExist: false, status: "unopened", baseInfo: {} };
208726
208802
  }
208727
208803
  else {
@@ -210311,7 +210387,8 @@ function registerDataModelTools(server) {
210311
210387
  },
210312
210388
  annotations: {
210313
210389
  readOnlyHint: true,
210314
- openWorldHint: true,
210390
+ destructiveHint: false,
210391
+ openWorldHint: false,
210315
210392
  category: "database",
210316
210393
  },
210317
210394
  }, async ({ action, name, names, }) => {
@@ -210650,7 +210727,7 @@ classDiagram
210650
210727
  readOnlyHint: false,
210651
210728
  destructiveHint: false,
210652
210729
  idempotentHint: false,
210653
- openWorldHint: true,
210730
+ openWorldHint: false,
210654
210731
  category: "database",
210655
210732
  },
210656
210733
  }, async ({ mermaidDiagram, action = "create", publish = false, dbInstanceType = "MYSQL", }) => {
@@ -211186,7 +211263,8 @@ checkIndex: 检查指定索引是否存在`),
211186
211263
  },
211187
211264
  annotations: {
211188
211265
  readOnlyHint: true,
211189
- openWorldHint: true,
211266
+ destructiveHint: false,
211267
+ openWorldHint: false,
211190
211268
  category: CATEGORY,
211191
211269
  },
211192
211270
  }, async ({ action, limit, offset, collectionName, indexName }) => {
@@ -211389,7 +211467,7 @@ deleteCollection: 删除集合`),
211389
211467
  readOnlyHint: false,
211390
211468
  destructiveHint: true,
211391
211469
  idempotentHint: false,
211392
- openWorldHint: true,
211470
+ openWorldHint: false,
211393
211471
  category: CATEGORY,
211394
211472
  },
211395
211473
  }, async ({ action, collectionName, updateOptions }) => {
@@ -211571,7 +211649,8 @@ deleteCollection: 删除集合`),
211571
211649
  },
211572
211650
  annotations: {
211573
211651
  readOnlyHint: true,
211574
- openWorldHint: true,
211652
+ destructiveHint: false,
211653
+ openWorldHint: false,
211575
211654
  category: CATEGORY,
211576
211655
  },
211577
211656
  }, async ({ collectionName, instanceId, query, projection, sort, limit, offset }) => {
@@ -211672,7 +211751,7 @@ deleteCollection: 删除集合`),
211672
211751
  readOnlyHint: false,
211673
211752
  destructiveHint: true,
211674
211753
  idempotentHint: false,
211675
- openWorldHint: true,
211754
+ openWorldHint: false,
211676
211755
  category: CATEGORY,
211677
211756
  },
211678
211757
  }, async ({ action, collectionName, instanceId, documents, query, update, isMulti, upsert, }) => {
@@ -214291,7 +214370,8 @@ function registerPGDatabaseTools(server, providedDeps) {
214291
214370
  },
214292
214371
  annotations: {
214293
214372
  readOnlyHint: true,
214294
- openWorldHint: true,
214373
+ destructiveHint: false,
214374
+ openWorldHint: false,
214295
214375
  category: CATEGORY,
214296
214376
  },
214297
214377
  }, async (args) => {
@@ -214450,7 +214530,7 @@ function registerPGDatabaseTools(server, providedDeps) {
214450
214530
  readOnlyHint: false,
214451
214531
  destructiveHint: true,
214452
214532
  idempotentHint: false,
214453
- openWorldHint: true,
214533
+ openWorldHint: false,
214454
214534
  category: CATEGORY,
214455
214535
  },
214456
214536
  }, async (args) => {
@@ -215460,7 +215540,8 @@ function registerSQLDatabaseTools(server) {
215460
215540
  },
215461
215541
  annotations: {
215462
215542
  readOnlyHint: true,
215463
- openWorldHint: true,
215543
+ destructiveHint: false,
215544
+ openWorldHint: false,
215464
215545
  category: CATEGORY,
215465
215546
  },
215466
215547
  }, async (args) => {
@@ -215529,7 +215610,7 @@ function registerSQLDatabaseTools(server) {
215529
215610
  readOnlyHint: false,
215530
215611
  destructiveHint: true,
215531
215612
  idempotentHint: false,
215532
- openWorldHint: true,
215613
+ openWorldHint: false,
215533
215614
  category: CATEGORY,
215534
215615
  },
215535
215616
  }, async (args) => {
@@ -215834,7 +215915,8 @@ function registerDeployTools(server) {
215834
215915
  annotations: {
215835
215916
  // 本地构建会执行 buildCommand 并写产物文件,不是 read-only;但不变更云端资源,非 destructive。
215836
215917
  readOnlyHint: false,
215837
- openWorldHint: true,
215918
+ destructiveHint: false,
215919
+ openWorldHint: false,
215838
215920
  category: "deploy",
215839
215921
  },
215840
215922
  }, async ({ cwd, mode }) => {
@@ -215906,7 +215988,8 @@ function registerDeployTools(server) {
215906
215988
  },
215907
215989
  annotations: {
215908
215990
  readOnlyHint: true,
215909
- openWorldHint: true,
215991
+ destructiveHint: false,
215992
+ openWorldHint: false,
215910
215993
  category: "deploy",
215911
215994
  },
215912
215995
  }, async ({ cwd, mode, envId: envIdInput, only, skip, yes, }) => {
@@ -215988,7 +216071,7 @@ function registerDeployTools(server) {
215988
216071
  annotations: {
215989
216072
  readOnlyHint: false,
215990
216073
  destructiveHint: true,
215991
- openWorldHint: true,
216074
+ openWorldHint: false,
215992
216075
  category: "deploy",
215993
216076
  },
215994
216077
  }, async ({ confirm, confirmDestructive, cwd, mode, envId: envIdInput, only, skip, yes, concurrency, continueOnError, }) => {
@@ -217753,7 +217836,7 @@ async function enrichEnvInfoWithMissingFields(manager, result, envId) {
217753
217836
  }
217754
217837
  }
217755
217838
  /**
217756
- * Project gateway Route.Enable onto envQuery(info) without mutating StaticDomain.
217839
+ * Project gateway Route.Enable onto queryEnv(info) without mutating StaticDomain.
217757
217840
  *
217758
217841
  * StaticStorages[].StaticDomain stays the cloud-API nominal hostname.
217759
217842
  * Sibling field staticDomainRouteEnabled (and EnvInfo-level mirrors for the
@@ -218312,7 +218395,7 @@ function registerEnvTools(server) {
218312
218395
  readOnlyHint: false,
218313
218396
  destructiveHint: false,
218314
218397
  idempotentHint: false,
218315
- openWorldHint: true,
218398
+ openWorldHint: false,
218316
218399
  category: "env",
218317
218400
  },
218318
218401
  }, async (rawArgs) => {
@@ -219027,14 +219110,7 @@ function registerEnvTools(server) {
219027
219110
  }
219028
219111
  (0, logger_js_1.debug)("降级到 listEnvs() 也失败:", fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)));
219029
219112
  const enhancedMessage = buildEnvQueryErrorMessage(fallbackError, "list");
219030
- return {
219031
- content: [
219032
- {
219033
- type: "text",
219034
- text: enhancedMessage,
219035
- },
219036
- ],
219037
- };
219113
+ return (0, tool_result_js_1.buildTextErrorResult)(enhancedMessage);
219038
219114
  }
219039
219115
  }
219040
219116
  result = buildEnvQueryListResult({
@@ -219217,14 +219293,7 @@ function registerEnvTools(server) {
219217
219293
  return toolPayloadResult;
219218
219294
  }
219219
219295
  const enhancedMessage = buildEnvQueryErrorMessage(error, action);
219220
- return {
219221
- content: [
219222
- {
219223
- type: "text",
219224
- text: enhancedMessage,
219225
- },
219226
- ],
219227
- };
219296
+ return (0, tool_result_js_1.buildTextErrorResult)(enhancedMessage);
219228
219297
  }
219229
219298
  };
219230
219299
  // Register primary tool name (queryEnv)
@@ -219294,7 +219363,8 @@ function registerEnvTools(server) {
219294
219363
  },
219295
219364
  annotations: {
219296
219365
  readOnlyHint: true,
219297
- openWorldHint: true,
219366
+ destructiveHint: false,
219367
+ openWorldHint: false,
219298
219368
  category: "env",
219299
219369
  },
219300
219370
  };
@@ -219326,9 +219396,9 @@ function registerEnvTools(server) {
219326
219396
  },
219327
219397
  annotations: {
219328
219398
  readOnlyHint: false,
219329
- destructiveHint: false, // 注意:delete操作虽然是破坏性的,但这里采用较宽松的标注
219399
+ destructiveHint: true,
219330
219400
  idempotentHint: false,
219331
- openWorldHint: true,
219401
+ openWorldHint: false,
219332
219402
  category: "env",
219333
219403
  },
219334
219404
  }, async ({ action, domains, }) => {
@@ -219358,16 +219428,9 @@ function registerEnvTools(server) {
219358
219428
  if (toolPayloadResult) {
219359
219429
  return toolPayloadResult;
219360
219430
  }
219361
- return {
219362
- content: [
219363
- {
219364
- type: "text",
219365
- text: (0, index_js_1.t)("env.domain.operationFailed", {
219366
- message: error instanceof Error ? error.message : String(error),
219367
- }),
219368
- },
219369
- ],
219370
- };
219431
+ return (0, tool_result_js_1.buildTextErrorResult)((0, index_js_1.t)("env.domain.operationFailed", {
219432
+ message: error instanceof Error ? error.message : String(error),
219433
+ }));
219371
219434
  }
219372
219435
  });
219373
219436
  } // end: wxide guard for envDomainManagement
@@ -219419,7 +219482,7 @@ function registerEnvTools(server) {
219419
219482
  readOnlyHint: false,
219420
219483
  destructiveHint: true,
219421
219484
  idempotentHint: false,
219422
- openWorldHint: true,
219485
+ openWorldHint: false,
219423
219486
  category: "env",
219424
219487
  },
219425
219488
  }, async (rawArgs) => {
@@ -219893,14 +219956,7 @@ function registerEnvTools(server) {
219893
219956
  message: (0, index_js_1.t)("env.manage.listPackagesFailed", { message: errorMessage }),
219894
219957
  });
219895
219958
  }
219896
- return {
219897
- content: [
219898
- {
219899
- type: "text",
219900
- text: (0, index_js_1.t)("env.manage.operationFailed", { message: errorMessage }),
219901
- },
219902
- ],
219903
- };
219959
+ return (0, tool_result_js_1.buildTextErrorResult)((0, index_js_1.t)("env.manage.operationFailed", { message: errorMessage }));
219904
219960
  }
219905
219961
  });
219906
219962
  }
@@ -223062,7 +223118,8 @@ function registerFunctionTools(server) {
223062
223118
  },
223063
223119
  annotations: {
223064
223120
  readOnlyHint: true,
223065
- openWorldHint: true,
223121
+ destructiveHint: false,
223122
+ openWorldHint: false,
223066
223123
  category: "functions",
223067
223124
  },
223068
223125
  }, async (input) => withEnvelope(() => handleQueryFunctions(input)));
@@ -223147,7 +223204,7 @@ function registerFunctionTools(server) {
223147
223204
  readOnlyHint: false,
223148
223205
  destructiveHint: true,
223149
223206
  idempotentHint: false,
223150
- openWorldHint: true,
223207
+ openWorldHint: false,
223151
223208
  category: "functions",
223152
223209
  },
223153
223210
  }, async (input) => withEnvelope(() => handleManageFunctions(input)));
@@ -223251,6 +223308,123 @@ function registerGatewayTools(server) {
223251
223308
  data: {},
223252
223309
  message: error instanceof Error ? error.message : String(error),
223253
223310
  });
223311
+ /**
223312
+ * 上游目标存在性校验(F1)。
223313
+ *
223314
+ * 背景:`createHttpServiceRoute` 不校验 `UpstreamResourceName` 是否存在,预检
223315
+ * (VerifyHTTPServiceRoute)也只查域名归属/证书/配额,因此**不存在的上游也能真实落库并
223316
+ * 返回 success**,形成悬空路由(path 被占用、指向不存在的目标)。实测两站复现。
223317
+ *
223318
+ * 设计原则:**只在能证明「不存在」时拒绝**。
223319
+ * - 列表接口报错、分页未扫全、类型不支持 → `unknown`,放行并记日志(fail-open)。
223320
+ * 校验失败绝不该阻塞用户创建合法路由。
223321
+ * - 只有扫完全量且确实没有该名称时,才返回 `missing`。
223322
+ *
223323
+ * 覆盖范围:SCF / WEB_SCF(函数列表)、CBR(云托管服务列表)。
223324
+ * NOT covered:STATIC_STORE 的 UpstreamResourceName 是静态托管**实例名/固定别名
223325
+ * staticstore**(不是 CloudApp 应用名),可靠列举要额外走 DescribeStaticStore,且该资源
223326
+ * 随托管启用必然存在、悬空风险低;LH(轻量应用服务器)无列举接口。两者一律 fail-open。
223327
+ */
223328
+ const UPSTREAM_PROBE_PAGE_SIZE = 100;
223329
+ const UPSTREAM_PROBE_MAX_PAGES = 5;
223330
+ const UPSTREAM_PROBE_CANDIDATE_LIMIT = 10;
223331
+ const probeUpstreamResource = async (cloudbase, params) => {
223332
+ const wanted = params.upstreamResourceName.trim();
223333
+ const upstreamResourceType = params.upstreamResourceType;
223334
+ try {
223335
+ if (upstreamResourceType === "SCF" || upstreamResourceType === "WEB_SCF") {
223336
+ // fail-open:老 SDK / mock 没有该 API 时不校验,绝不让「拿不到列表」变成「判定不存在」。
223337
+ if (typeof cloudbase.functions?.getFunctionList !== "function") {
223338
+ return { status: "unknown", reason: "function-list-api-unavailable" };
223339
+ }
223340
+ const names = new Set();
223341
+ let scanned = 0;
223342
+ let total = Number.POSITIVE_INFINITY;
223343
+ for (let page = 0; page < UPSTREAM_PROBE_MAX_PAGES; page += 1) {
223344
+ const res = await cloudbase.functions.getFunctionList(UPSTREAM_PROBE_PAGE_SIZE, scanned);
223345
+ const list = Array.isArray(res?.Functions) ? res.Functions : [];
223346
+ for (const item of list) {
223347
+ const name = item?.FunctionName;
223348
+ if (typeof name === "string" && name) {
223349
+ names.add(name);
223350
+ }
223351
+ }
223352
+ scanned += list.length;
223353
+ total = typeof res?.TotalCount === "number" ? res.TotalCount : scanned;
223354
+ if (names.has(wanted) || list.length === 0 || scanned >= total) {
223355
+ break;
223356
+ }
223357
+ }
223358
+ if (names.has(wanted)) {
223359
+ return { status: "exists" };
223360
+ }
223361
+ if (scanned >= total) {
223362
+ return {
223363
+ status: "missing",
223364
+ candidates: [...names].slice(0, UPSTREAM_PROBE_CANDIDATE_LIMIT),
223365
+ };
223366
+ }
223367
+ return { status: "unknown", reason: "function-list-partially-scanned" };
223368
+ }
223369
+ if (upstreamResourceType === "CBR") {
223370
+ // fail-open:同上,`cloudrun?.list` 缺失时会短路成 undefined,若不显式拦截会被
223371
+ // 误判成「列表为空 = 目标不存在」,从而错误拒绝合法路由。
223372
+ if (typeof cloudbase.cloudrun?.list !== "function") {
223373
+ return { status: "unknown", reason: "cloudrun-list-api-unavailable" };
223374
+ }
223375
+ const names = new Set();
223376
+ for (let page = 1; page <= UPSTREAM_PROBE_MAX_PAGES; page += 1) {
223377
+ const res = await cloudbase.cloudrun.list({
223378
+ pageSize: UPSTREAM_PROBE_PAGE_SIZE,
223379
+ pageNum: page,
223380
+ });
223381
+ const list = Array.isArray(res?.ServerList) ? res.ServerList : [];
223382
+ for (const item of list) {
223383
+ const record = item;
223384
+ const name = record?.ServerName ?? record?.Name;
223385
+ if (typeof name === "string" && name) {
223386
+ names.add(name);
223387
+ }
223388
+ }
223389
+ const total = typeof res?.Total === "number" ? res.Total : names.size;
223390
+ if (names.has(wanted) || list.length === 0 || names.size >= total) {
223391
+ break;
223392
+ }
223393
+ }
223394
+ if (names.has(wanted)) {
223395
+ return { status: "exists" };
223396
+ }
223397
+ return {
223398
+ status: "missing",
223399
+ candidates: [...names].slice(0, UPSTREAM_PROBE_CANDIDATE_LIMIT),
223400
+ };
223401
+ }
223402
+ // STATIC_STORE / LH:无可靠的实例列举通路,不校验(见上方说明)。
223403
+ return { status: "unknown", reason: `unsupported-upstream-type:${upstreamResourceType}` };
223404
+ }
223405
+ catch (error) {
223406
+ const message = error instanceof Error ? error.message : String(error);
223407
+ return { status: "unknown", reason: message };
223408
+ }
223409
+ };
223410
+ /** 上游缺失时的候选列举工具,按类型给出下一步可用的只读入口。 */
223411
+ const upstreamProbeNextAction = (upstreamResourceType) => {
223412
+ if (upstreamResourceType === "SCF" || upstreamResourceType === "WEB_SCF") {
223413
+ return {
223414
+ tool: "queryFunctions",
223415
+ action: "listFunctions",
223416
+ reason: (0, index_js_1.t)("gateway.create.upstreamNotFoundReason"),
223417
+ };
223418
+ }
223419
+ if (upstreamResourceType === "CBR") {
223420
+ return {
223421
+ tool: "queryCloudRun",
223422
+ action: "list",
223423
+ reason: (0, index_js_1.t)("gateway.create.upstreamNotFoundReason"),
223424
+ };
223425
+ }
223426
+ return null;
223427
+ };
223254
223428
  const verifyHttpServiceRouteOrFail = async (params) => {
223255
223429
  const cloudbase = await getManager();
223256
223430
  let result;
@@ -223737,6 +223911,46 @@ function registerGatewayTools(server) {
223737
223911
  case "createRoute": {
223738
223912
  const cloudbase = await getManager();
223739
223913
  const payload = await normalizeRoutePayload(input);
223914
+ // 上游目标存在性校验(F1):平台侧不校验 UpstreamResourceName,不存在的目标也会
223915
+ // 真实落库形成悬空路由并返回 success。能证明不存在就拒绝,校验不了就放行(fail-open)。
223916
+ const upstreamProbe = await probeUpstreamResource(cloudbase, {
223917
+ upstreamResourceType: payload.resolved.upstreamResourceType,
223918
+ upstreamResourceName: payload.resolved.upstreamResourceName,
223919
+ });
223920
+ if (upstreamProbe.status === "missing") {
223921
+ const nextAction = upstreamProbeNextAction(payload.resolved.upstreamResourceType);
223922
+ return {
223923
+ success: false,
223924
+ data: {
223925
+ action: "createRoute",
223926
+ upstreamResourceType: payload.resolved.upstreamResourceType,
223927
+ targetName: payload.resolved.upstreamResourceName,
223928
+ path: payload.resolved.path,
223929
+ candidates: upstreamProbe.candidates,
223930
+ ...(upstreamProbe.candidates.length
223931
+ ? {
223932
+ candidateHint: (0, index_js_1.t)("gateway.create.upstreamCandidates", {
223933
+ type: payload.resolved.upstreamResourceType,
223934
+ candidates: upstreamProbe.candidates.join("、"),
223935
+ }),
223936
+ }
223937
+ : {}),
223938
+ },
223939
+ message: (0, index_js_1.t)("gateway.create.upstreamNotFound", {
223940
+ type: payload.resolved.upstreamResourceType,
223941
+ name: payload.resolved.upstreamResourceName,
223942
+ }),
223943
+ ...(nextAction ? { nextActions: [nextAction] } : {}),
223944
+ };
223945
+ }
223946
+ if (upstreamProbe.status === "unknown") {
223947
+ // 校验不了不阻塞创建,但要留痕,便于排查「为什么这次没拦住」。
223948
+ server.logger?.({
223949
+ type: "errorToolCall",
223950
+ toolName: "manageGateway",
223951
+ message: `createRoute upstream probe skipped (${payload.resolved.upstreamResourceType}/${payload.resolved.upstreamResourceName}): ${upstreamProbe.reason}`,
223952
+ });
223953
+ }
223740
223954
  // Probe → create: ownership / cert / quota / conflict checks before create.
223741
223955
  const verifyFailure = await verifyHttpServiceRouteOrFail({
223742
223956
  envId: payload.EnvId,
@@ -224196,7 +224410,8 @@ function registerGatewayTools(server) {
224196
224410
  },
224197
224411
  annotations: {
224198
224412
  readOnlyHint: true,
224199
- openWorldHint: true,
224413
+ destructiveHint: false,
224414
+ openWorldHint: false,
224200
224415
  category: "gateway",
224201
224416
  },
224202
224417
  }, async (input) => withEnvelope(() => handleQueryGateway(input)));
@@ -224314,7 +224529,7 @@ function registerGatewayTools(server) {
224314
224529
  readOnlyHint: false,
224315
224530
  destructiveHint: true,
224316
224531
  idempotentHint: false,
224317
- openWorldHint: true,
224532
+ openWorldHint: false,
224318
224533
  category: "gateway",
224319
224534
  },
224320
224535
  }, async (input) => withEnvelope(() => handleManageGateway(input)));
@@ -224758,6 +224973,15 @@ function buildWebsiteConfigNextStep() {
224758
224973
  },
224759
224974
  };
224760
224975
  }
224976
+ /**
224977
+ * 静态托管的对象 key 一律不带前导斜杠(`dir/f.txt` 而非 `/dir/f.txt`),但 agent
224978
+ * 从 URL 形态(`/index.html`)推路径时很自然带上斜杠。底层 SDK 的 `getCloudKey`
224979
+ * 只补尾部 `/`、不剥离前导 `/`,随后 `getBucket({ Prefix })` 做的是严格字节前缀
224980
+ * 匹配 ⇒ `/dir/` 匹配不到 `dir/f.txt`。因此所有对外传入的路径/前缀都先在此归一化。
224981
+ */
224982
+ function stripLeadingSlashes(value) {
224983
+ return value.replace(/^\/+/, '');
224984
+ }
224761
224985
  function buildFindFilesNextStep(prefix) {
224762
224986
  return {
224763
224987
  tool: 'queryHosting',
@@ -224986,7 +225210,8 @@ function registerHostingTools(server) {
224986
225210
  inputSchema: queryHostingInputSchema,
224987
225211
  annotations: {
224988
225212
  readOnlyHint: true,
224989
- openWorldHint: true,
225213
+ destructiveHint: false,
225214
+ openWorldHint: false,
224990
225215
  category: 'hosting',
224991
225216
  },
224992
225217
  }, async (args) => {
@@ -225029,8 +225254,11 @@ function registerHostingTools(server) {
225029
225254
  if (!input.prefix) {
225030
225255
  throw new Error((0, index_js_1.t)('hosting.findFilesPrefixRequired'));
225031
225256
  }
225257
+ // 读侧与 delete 侧同源:prefix 原样透传时,agent 按 URL 形态传的
225258
+ // `/assets/` 在 COS 侧严格前缀匹配命中 0 条,会被误读成「目录是空的」。
225259
+ const normalizedPrefix = stripLeadingSlashes(input.prefix);
225032
225260
  const result = await cloudbase.hosting.findFiles({
225033
- prefix: input.prefix,
225261
+ prefix: normalizedPrefix,
225034
225262
  marker: input.marker,
225035
225263
  maxKeys: input.maxKeys,
225036
225264
  });
@@ -225042,7 +225270,7 @@ function registerHostingTools(server) {
225042
225270
  success: true,
225043
225271
  data: {
225044
225272
  action: 'findFiles',
225045
- prefix: input.prefix,
225273
+ prefix: normalizedPrefix,
225046
225274
  marker: input.marker,
225047
225275
  maxKeys: input.maxKeys,
225048
225276
  files: normalizedFiles,
@@ -225051,7 +225279,7 @@ function registerHostingTools(server) {
225051
225279
  result,
225052
225280
  },
225053
225281
  message: (0, index_js_1.t)('hosting.findFilesSuccess', {
225054
- prefix: input.prefix,
225282
+ prefix: normalizedPrefix,
225055
225283
  count: normalizedFiles.length,
225056
225284
  more: nextMarker ? (0, index_js_1.t)('hosting.findFilesMore') : '',
225057
225285
  }),
@@ -225140,7 +225368,7 @@ function registerHostingTools(server) {
225140
225368
  readOnlyHint: false,
225141
225369
  destructiveHint: true,
225142
225370
  idempotentHint: false,
225143
- openWorldHint: true,
225371
+ openWorldHint: false,
225144
225372
  category: 'hosting',
225145
225373
  },
225146
225374
  }, async (args) => {
@@ -225302,10 +225530,18 @@ function registerHostingTools(server) {
225302
225530
  if (!input.confirm) {
225303
225531
  throw new Error((0, index_js_1.t)('hosting.deleteConfirmRequired'));
225304
225532
  }
225533
+ // 静态托管对象 key 不带前导斜杠。若不规范化,isDir=true 会走
225534
+ // storage.deleteDirectoryCustom,其前缀由 getCloudKey(cloudPath)
225535
+ // 生成且不做前导斜杠剥离,做严格字节前缀匹配时 `/dir/` 匹配不到
225536
+ // `dir/f.txt`,导致目录删除静默 no-op(Deleted:0,文件全在)。
225537
+ // 同时下面的回查 findFiles(prefix) 也是严格前缀匹配,带斜杠时
225538
+ // 同样命中 0 条,会把「没删到」误判成「删干净了 verified:true」。
225539
+ // 这里统一剥离前导斜杠,两种形态(/dir 与 dir)都按正确 key 处理。
225540
+ const normalizedCloudPath = stripLeadingSlashes(input.cloudPath);
225305
225541
  let result;
225306
225542
  try {
225307
225543
  result = await cloudbase.hosting.deleteFiles({
225308
- cloudPath: input.cloudPath,
225544
+ cloudPath: normalizedCloudPath,
225309
225545
  isDir: input.isDir ?? false,
225310
225546
  });
225311
225547
  }
@@ -225327,10 +225563,28 @@ function registerHostingTools(server) {
225327
225563
  }
225328
225564
  try {
225329
225565
  const checkResult = await cloudbase.hosting.findFiles({
225330
- prefix: input.cloudPath,
225331
- maxKeys: 1,
225566
+ prefix: normalizedCloudPath,
225567
+ maxKeys: 100,
225332
225568
  });
225333
- if (Array.isArray(checkResult) && checkResult.length > 0) {
225569
+ // ⚠️ 回查有两个坑,都会让 verified 失真:
225570
+ // 1) findFiles 返回 **COS 风格对象**(列表在 Contents 内),直接用
225571
+ // `Array.isArray(checkResult)` 判断会静默失效、恒判 verified=true。
225572
+ // 统一走 extractFileList 归一化。
225573
+ // 2) findFiles 是 **prefix** 语义:删单文件 /a/b.txt 时,若同前缀的
225574
+ // /a/b.txt.bak 还在也会命中 → 把「已删成功」误判成未验证。
225575
+ // 单文件场景必须精确比对 Key(前缀场景见 normalizeFileFields 的 key 字段)。
225576
+ const remaining = normalizeFileFields(checkResult);
225577
+ const targetPath = normalizedCloudPath;
225578
+ const stillExists = input.isDir
225579
+ ? remaining.length > 0
225580
+ : remaining.some((file) => {
225581
+ const key = typeof file.key === 'string' ? file.key : '';
225582
+ // 拿不到 Key 时保守按「仍存在」处理,避免误报已验证
225583
+ if (!key)
225584
+ return true;
225585
+ return stripLeadingSlashes(key) === targetPath;
225586
+ });
225587
+ if (stillExists) {
225334
225588
  deleteVerified = false;
225335
225589
  verificationError = verificationError ?? (0, index_js_1.t)('hosting.deleteVerifyFailed');
225336
225590
  }
@@ -225354,7 +225608,11 @@ function registerHostingTools(server) {
225354
225608
  type: input.isDir ? (0, index_js_1.t)('hosting.typeDirectory') : (0, index_js_1.t)('hosting.typeFile'),
225355
225609
  cloudPath: input.cloudPath,
225356
225610
  })
225357
- : (0, index_js_1.t)('hosting.deleteUnverified', { cloudPath: input.cloudPath }),
225611
+ // 这里刻意插入规范化后的路径:该文案的 {cloudPath} 会被拼进
225612
+ // `queryHosting(action="findFiles", prefix="{cloudPath}")` 自查命令里,
225613
+ // 带前导斜杠的前缀在 COS 侧严格匹配不到任何对象,agent 照抄会得到
225614
+ // 「0 命中 ⇒ 看起来删干净了」的错误结论。
225615
+ : (0, index_js_1.t)('hosting.deleteUnverified', { cloudPath: normalizedCloudPath }),
225358
225616
  });
225359
225617
  }
225360
225618
  case 'setWebsiteDocument': {
@@ -225592,7 +225850,8 @@ function registerLogTools(server) {
225592
225850
  },
225593
225851
  annotations: {
225594
225852
  readOnlyHint: true,
225595
- openWorldHint: true,
225853
+ destructiveHint: false,
225854
+ openWorldHint: false,
225596
225855
  category: "logs",
225597
225856
  },
225598
225857
  }, async ({ action, queryString, service, startTime, endTime, limit, context, sort, }) => {
@@ -226141,7 +226400,8 @@ function registerMsgPushTools(server) {
226141
226400
  },
226142
226401
  annotations: {
226143
226402
  readOnlyHint: true,
226144
- openWorldHint: true,
226403
+ destructiveHint: false,
226404
+ openWorldHint: false,
226145
226405
  category: CATEGORY,
226146
226406
  },
226147
226407
  }, async ({ appid, env, action }) => {
@@ -226286,7 +226546,7 @@ function registerMsgPushTools(server) {
226286
226546
  readOnlyHint: false,
226287
226547
  destructiveHint: true,
226288
226548
  idempotentHint: true,
226289
- openWorldHint: true,
226549
+ openWorldHint: false,
226290
226550
  category: CATEGORY,
226291
226551
  },
226292
226552
  }, async ({ appid, env_id, function_name, action, msg_type, event_types, enable, qbase_container_path, qbase_env, text_mode, confirm, }) => {
@@ -227155,7 +227415,8 @@ function registerPermissionTools(server) {
227155
227415
  },
227156
227416
  annotations: {
227157
227417
  readOnlyHint: true,
227158
- openWorldHint: true,
227418
+ destructiveHint: false,
227419
+ openWorldHint: false,
227159
227420
  category: "permissions",
227160
227421
  },
227161
227422
  }, async ({ action, resourceType, resourceId, resourceIds, roleId, roleIdentity, roleName, uid, username, pageNo, pageSize, extension, policyResourceType, }) => withEnvelope(async () => {
@@ -227384,7 +227645,7 @@ function registerPermissionTools(server) {
227384
227645
  readOnlyHint: false,
227385
227646
  destructiveHint: true,
227386
227647
  idempotentHint: false,
227387
- openWorldHint: true,
227648
+ openWorldHint: false,
227388
227649
  category: "permissions",
227389
227650
  },
227390
227651
  }, async ({ action, resourceType, resourceId, permission, securityRule, roleId, roleIds, roleName, roleIdentity, description, memberUids, policies, policyIds, uid, uids, username, password, userStatus, regoContent, confirm, }) => withEnvelope(async () => {
@@ -228131,16 +228392,7 @@ async function registerRagTools(server) {
228131
228392
  title: "rag.title",
228132
228393
  description: (0, index_js_1.t)("rag.description", {
228133
228394
  skillCount: skills.length,
228134
- skillList: skills
228135
- .map((skill) => (0, index_js_1.t)("rag.skillListItem", {
228136
- name: path.basename(path.dirname(skill.absolutePath)),
228137
- description: skill.description,
228138
- }))
228139
- .join("\n"),
228140
228395
  openapiCount: openapis.length,
228141
- openapiList: openapis
228142
- .map((api) => (0, index_js_1.t)("rag.openapiListItem", { name: api.name, description: api.description }))
228143
- .join("\n"),
228144
228396
  }),
228145
228397
  inputSchema: {
228146
228398
  mode: SearchKnowledgeModeEnum,
@@ -228166,6 +228418,7 @@ async function registerRagTools(server) {
228166
228418
  },
228167
228419
  annotations: {
228168
228420
  readOnlyHint: true,
228421
+ destructiveHint: false,
228169
228422
  openWorldHint: true,
228170
228423
  category: "rag",
228171
228424
  },
@@ -228217,6 +228470,16 @@ async function registerRagTools(server) {
228217
228470
  }
228218
228471
  }
228219
228472
  if (mode === "skill") {
228473
+ // 不传 skillName:返回带适用场景的完整目录,让调用方按需发现后再取正文
228474
+ if (!skillName?.trim()) {
228475
+ return (0, json_content_js_1.jsonContent)([
228476
+ (0, index_js_1.t)("rag.skillCatalogHeader", { count: skillNames.length }),
228477
+ ...skills.map((item) => (0, index_js_1.t)("rag.skillListItem", {
228478
+ name: path.basename(path.dirname(item.absolutePath)),
228479
+ description: item.description,
228480
+ })),
228481
+ ].join("\n"));
228482
+ }
228220
228483
  const skill = skills.find((item) => item.absolutePath.includes(skillName));
228221
228484
  if (!skill) {
228222
228485
  const remoteHint = skillName?.trim()
@@ -228267,6 +228530,15 @@ async function registerRagTools(server) {
228267
228530
  return { content: [{ type: "text", text: sections.join("\n\n") }] };
228268
228531
  }
228269
228532
  if (mode === "openapi") {
228533
+ if (!apiName?.trim()) {
228534
+ return (0, json_content_js_1.jsonContent)([
228535
+ (0, index_js_1.t)("rag.openapiCatalogHeader", { count: openapiNames.length }),
228536
+ ...openapis.map((api) => (0, index_js_1.t)("rag.openapiListItem", {
228537
+ name: api.name,
228538
+ description: api.description,
228539
+ })),
228540
+ ].join("\n"));
228541
+ }
228270
228542
  const api = openapis.find((api) => api.name === apiName);
228271
228543
  if (!api) {
228272
228544
  return {
@@ -228323,7 +228595,7 @@ function extractDescriptionFromFrontMatter(content) {
228323
228595
  fm.push(lines[i]);
228324
228596
  const match = fm
228325
228597
  .join("\n")
228326
- .match(/^(?:decsription|description)\s*:\s*(.*)$/m);
228598
+ .match(/^description\s*:\s*(.*)$/m);
228327
228599
  return match ? match[1].trim() : null;
228328
228600
  }
228329
228601
  async function collectSkillDescriptions(rootDir) {
@@ -229291,7 +229563,8 @@ function registerStorageTools(server) {
229291
229563
  inputSchema: queryStorageInputSchema,
229292
229564
  annotations: {
229293
229565
  readOnlyHint: true,
229294
- openWorldHint: true,
229566
+ destructiveHint: false,
229567
+ openWorldHint: false,
229295
229568
  category: "storage"
229296
229569
  }
229297
229570
  }, async (args) => {
@@ -229471,7 +229744,7 @@ function registerStorageTools(server) {
229471
229744
  readOnlyHint: false,
229472
229745
  destructiveHint: true,
229473
229746
  idempotentHint: false,
229474
- openWorldHint: true,
229747
+ openWorldHint: false,
229475
229748
  category: "storage"
229476
229749
  }
229477
229750
  }, async (args) => {
@@ -229941,7 +230214,8 @@ function registerPGStorageTools(server) {
229941
230214
  },
229942
230215
  annotations: {
229943
230216
  readOnlyHint: true,
229944
- openWorldHint: true,
230217
+ destructiveHint: false,
230218
+ openWorldHint: false,
229945
230219
  category: CATEGORY,
229946
230220
  },
229947
230221
  }, async (args) => {
@@ -231191,7 +231465,7 @@ exports.SITE_REGION_MAP = {
231191
231465
  },
231192
231466
  };
231193
231467
  exports.SITE_IDS = Object.keys(exports.SITE_REGION_MAP);
231194
- /** Regions accepted by envQuery(list) / cross-region DescribeEnvs probes. */
231468
+ /** Regions accepted by queryEnv(list) / cross-region DescribeEnvs probes. */
231195
231469
  exports.TCB_QUERY_REGIONS = [
231196
231470
  "ap-shanghai",
231197
231471
  "ap-guangzhou",
@@ -231562,7 +231836,7 @@ class TelemetryReporter {
231562
231836
  const arch = os_1.default.arch(); // 系统架构
231563
231837
  // Build-time injected version; guard for vitest / non-webpack runtimes.
231564
231838
  const mcpVersion = process.env.npm_package_version ||
231565
- ( true ? "2.34.2" : 0);
231839
+ ( true ? "2.34.4" : 0);
231566
231840
  return {
231567
231841
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
231568
231842
  deviceId: this.deviceId,
@@ -231966,6 +232240,7 @@ exports.ToolPayloadError = void 0;
231966
232240
  exports.buildJsonToolResult = buildJsonToolResult;
231967
232241
  exports.isBusinessFailureToolResult = isBusinessFailureToolResult;
231968
232242
  exports.withBusinessFailureIsError = withBusinessFailureIsError;
232243
+ exports.buildTextErrorResult = buildTextErrorResult;
231969
232244
  exports.isToolPayloadError = isToolPayloadError;
231970
232245
  exports.toolPayloadErrorToResult = toolPayloadErrorToResult;
231971
232246
  exports.buildAuthNextStep = buildAuthNextStep;
@@ -232032,6 +232307,28 @@ function withBusinessFailureIsError(result) {
232032
232307
  }
232033
232308
  return { ...record, isError: true };
232034
232309
  }
232310
+ /**
232311
+ * 纯文本失败回执:**必须显式带 `isError: true`**。
232312
+ *
232313
+ * 为什么需要它:`withBusinessFailureIsError` 只能识别结构化的 `{ success: false }`
232314
+ * 载荷(`isBusinessFailureToolResult` 会 JSON.parse 文本内容再嗅 `success`)。工具在自己
232315
+ * 的 catch 里返回 `{ content: [{ type: "text", text }] }` 这种**纯文本**回执时,文本不是
232316
+ * JSON,嗅探不到任何失败标记 → 客户端只看到 `isError: false`,把失败当成功处理。
232317
+ *
232318
+ * 实测(F7):`queryEnv(action="usage")` 不带 envId 时抛出的引导文案就是这样返回的,
232319
+ * 只看 `isError` 的客户端会漏判。MCP 协议里 `isError` 是唯一可靠的失败标记,别省。
232320
+ */
232321
+ function buildTextErrorResult(text) {
232322
+ return {
232323
+ content: [
232324
+ {
232325
+ type: "text",
232326
+ text,
232327
+ },
232328
+ ],
232329
+ isError: true,
232330
+ };
232331
+ }
232035
232332
  class ToolPayloadError extends Error {
232036
232333
  payload;
232037
232334
  constructor(payload) {
@@ -232144,6 +232441,9 @@ ${envId}
232144
232441
  // 构建标题
232145
232442
  const title = `MCP工具错误: ${toolName}`;
232146
232443
  // 构建问题描述
232444
+ // ⚠️ MCP 版本必须优先取构建期注入的 __MCP_VERSION__:hosted 场景下进程是 BFF 的进程,
232445
+ // process.env.npm_package_version 是**宿主应用**的版本(实测报出 0.0.1,与 MCP 自身
232446
+ // 版本 2.34.2 自相矛盾);仅在未注入(源码直跑)时才回退到它。
232147
232447
  const body = `## 错误描述
232148
232448
  工具 \`${toolName}\` 执行时发生错误
232149
232449
 
@@ -232155,7 +232455,7 @@ ${envIdSection}
232155
232455
  ## 环境信息
232156
232456
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
232157
232457
  - Node.js版本: ${process.version}
232158
- - MCP 版本:${process.env.npm_package_version || ( true ? "2.34.2" : 0)}
232458
+ - MCP 版本:${( true ? "2.34.4" : 0) || 0 || 0}
232159
232459
  - 系统架构: ${os_1.default.arch()}
232160
232460
  - 时间: ${new Date().toISOString()}
232161
232461
  - 请求ID: ${requestId}
@@ -232244,7 +232544,7 @@ function createWrappedHandler(name, handler, server) {
232244
232544
  requestId,
232245
232545
  ide: server.ide || process.env.INTEGRATION_IDE || ''
232246
232546
  });
232247
- const mcpVersion = true ? "2.34.2" : 0;
232547
+ const mcpVersion = true ? "2.34.4" : 0;
232248
232548
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
232249
232549
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
232250
232550
  // 创建新的错误对象,保持原有的错误类型但更新消息