@cloudbase/cloudbase-mcp 2.34.1 → 2.34.3
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/cli.cjs +5 -5
- package/dist/index.cjs +316 -70
- package/dist/index.js +316 -70
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2400,6 +2400,7 @@ exports.apps = (0, types_js_1.defineModule)({
|
|
|
2400
2400
|
getSuccess: "CloudBase 应用详情查询成功",
|
|
2401
2401
|
listVersionsSuccess: "CloudBase 应用版本列表查询成功",
|
|
2402
2402
|
buildIdRequired: "action=getBuildLog 时必须提供 buildId",
|
|
2403
|
+
buildIdMustBeNumeric: "buildId 必须是数字(云 API DescribeCloudBaseRunBuildLog 的 BuildId 为整数)。收到的值:{buildId}。请使用部署回执或 getAppVersion 返回的 BuildId。",
|
|
2403
2404
|
buildLogFound: "查询到 {count} 条构建日志",
|
|
2404
2405
|
buildLogEmpty: "暂无构建日志",
|
|
2405
2406
|
nextStepQueryBuildLog: "查询构建日志",
|
|
@@ -2471,6 +2472,7 @@ exports.apps = (0, types_js_1.defineModule)({
|
|
|
2471
2472
|
getSuccess: "CloudBase app details retrieved successfully",
|
|
2472
2473
|
listVersionsSuccess: "CloudBase app version list retrieved successfully",
|
|
2473
2474
|
buildIdRequired: "buildId is required when action=getBuildLog",
|
|
2475
|
+
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.",
|
|
2474
2476
|
buildLogFound: "Retrieved {count} build log entries",
|
|
2475
2477
|
buildLogEmpty: "No build logs yet",
|
|
2476
2478
|
nextStepQueryBuildLog: "Query build logs",
|
|
@@ -3730,7 +3732,7 @@ exports.env = (0, types_js_1.defineModule)({
|
|
|
3730
3732
|
"auth.deviceCodeMissing": "未获取到设备码信息,请重试设备码登录",
|
|
3731
3733
|
"auth.deviceStarted": "已发起设备码登录,请在浏览器中打开 verification_uri 并输入 user_code 完成授权。授权完成后请再次调用 auth(action=\"status\")。",
|
|
3732
3734
|
"auth.loginStateMissing": "未获取到登录态,请先完成认证",
|
|
3733
|
-
"auth.apiKeyArgsRequired": "action=login_by_api_key 时必须同时提供 apiKey 和
|
|
3735
|
+
"auth.apiKeyArgsRequired": "action=login_by_api_key 时必须同时提供 apiKey 和 apiKeyEnvId。",
|
|
3734
3736
|
"auth.apiKeySuccess": "API Key 认证成功,已获取临时密钥。",
|
|
3735
3737
|
"auth.apiKeyExchangeFailed": "API Key 换取临时密钥失败。",
|
|
3736
3738
|
"auth.apiKeyDiagLogin": "\n\n诊断信息:\n- CLOUDBASE_ENV_ID: {envId}\n- CLOUDBASE_API_KEY: {apiKeyPrefix}...(已截断)\n- TCB_SITE: {site}\n\n可能原因:\n1. API Key 已过期或被删除\n2. CLOUDBASE_ENV_ID 与 API Key 所属环境不匹配\n3. API Key 与站点不匹配:国际站环境的 Key 需配置 TCB_SITE=intl(走 ap-singapore 网关);国内站环境(含 ap-guangzhou/ap-singapore 地域)不要配置 intl\n4. 网络连接问题\n\n建议:请检查 API Key 和环境 ID 是否正确。",
|
|
@@ -3943,7 +3945,7 @@ exports.env = (0, types_js_1.defineModule)({
|
|
|
3943
3945
|
"auth.deviceCodeMissing": "No device code info was returned; please retry device-code login",
|
|
3944
3946
|
"auth.deviceStarted": "Device-code login started. Open verification_uri in the browser and enter user_code to complete the authorization. Once authorized, call auth(action=\"status\") again.",
|
|
3945
3947
|
"auth.loginStateMissing": "No login state was obtained; please complete authentication first",
|
|
3946
|
-
"auth.apiKeyArgsRequired": "Both apiKey and
|
|
3948
|
+
"auth.apiKeyArgsRequired": "Both apiKey and apiKeyEnvId must be provided when action=login_by_api_key.",
|
|
3947
3949
|
"auth.apiKeySuccess": "API Key authentication succeeded; temporary credentials obtained.",
|
|
3948
3950
|
"auth.apiKeyExchangeFailed": "Failed to exchange the API Key for temporary credentials.",
|
|
3949
3951
|
"auth.apiKeyDiagLogin": "\n\nDiagnostics:\n- CLOUDBASE_ENV_ID: {envId}\n- CLOUDBASE_API_KEY: {apiKeyPrefix}... (truncated)\n- TCB_SITE: {site}\n\nPossible causes:\n1. The API Key has expired or was deleted\n2. CLOUDBASE_ENV_ID does not match the environment the API Key belongs to\n3. The API Key does not match the site: keys for international-site environments need TCB_SITE=intl (routed via the ap-singapore gateway); do not set intl for domestic-site environments (including the ap-guangzhou/ap-singapore regions)\n4. Network connectivity issue\n\nSuggestion: check that the API Key and environment ID are correct.",
|
|
@@ -4605,6 +4607,9 @@ exports.gateway = (0, types_js_1.defineModule)({
|
|
|
4605
4607
|
"create.backendError": "为目标资源配置访问路由失败(后端内部错误)。请确保:1) 目标云函数已成功创建并处于 Active 状态;2) 环境默认 HTTP 域名已完成初始化(IsDefault);3) 该访问路径未被占用。",
|
|
4606
4608
|
"create.backendErrorScfHint": "此外注意:HTTP 云函数必须用 upstreamResourceType=WEB_SCF,Event 云函数必须用 SCF;互标会导致此错误。",
|
|
4607
4609
|
"create.backendErrorSuffix": " 原始错误:{message}",
|
|
4610
|
+
"create.upstreamNotFound": "createRoute 未执行:上游目标不存在。upstreamResourceType={type}、targetName={name} 在环境中未找到,未创建任何路由。请先创建该资源(或核对名称/类型),再重新调用 createRoute。",
|
|
4611
|
+
"create.upstreamCandidates": "当前环境已有的 {type} 目标(最多 10 个,供核对名称):{candidates}",
|
|
4612
|
+
"create.upstreamNotFoundReason": "核对上游资源名称与 upstreamResourceType 后重试 createRoute",
|
|
4608
4613
|
"create.privilegeOffHint": "⚠️ HTTP 网关总开关未开启,路由创建成功后访问仍将返回 HTTPSERVICE_NONACTIVATED(403);请先调用 manageGateway(action=\"enableService\", enable=true) 开启,再立刻探测 accessUrl(通常数秒到约 30 秒内生效,勿盲等 60 秒以上)。",
|
|
4609
4614
|
"create.privilegeOffReason": "开启 HTTP 网关总开关(EnableService),否则访问路由会报 HTTPSERVICE_NONACTIVATED(403)",
|
|
4610
4615
|
"create.privilegeUnknownHint": "(无法确认 HTTP 网关开关状态;若访问报 HTTPSERVICE_NONACTIVATED,请用 queryGateway(action=\"getPrivilege\") 查询后用 manageGateway(action=\"enableService\") 开启)",
|
|
@@ -4690,6 +4695,9 @@ exports.gateway = (0, types_js_1.defineModule)({
|
|
|
4690
4695
|
"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.",
|
|
4691
4696
|
"create.backendErrorScfHint": "Also note: HTTP cloud functions must use upstreamResourceType=WEB_SCF, Event cloud functions must use SCF; swapping them causes this error.",
|
|
4692
4697
|
"create.backendErrorSuffix": " Original error: {message}",
|
|
4698
|
+
"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.",
|
|
4699
|
+
"create.upstreamCandidates": "Existing {type} targets in this environment (up to 10, for name verification): {candidates}",
|
|
4700
|
+
"create.upstreamNotFoundReason": "Verify the upstream resource name and upstreamResourceType, then retry createRoute",
|
|
4693
4701
|
"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).",
|
|
4694
4702
|
"create.privilegeOffReason": "Turn on the HTTP gateway master switch (EnableService), otherwise accessing the route returns HTTPSERVICE_NONACTIVATED (403)",
|
|
4695
4703
|
"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\"))",
|
|
@@ -5177,9 +5185,9 @@ exports.permissions = void 0;
|
|
|
5177
5185
|
const types_js_1 = __webpack_require__(9921);
|
|
5178
5186
|
exports.permissions = (0, types_js_1.defineModule)({
|
|
5179
5187
|
queryTitle: "查询 CloudBase 权限与用户配置",
|
|
5180
|
-
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
|
|
5188
|
+
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`。\n\n⚠️ 角色类 action(`listRoles` / `getRole`)在 **PostgreSQL 类型环境**下平台侧一律拒绝(`The current API does not support PostgreSQL type environments.`)——这是平台能力边界,不是配置问题,**不要在 PG 环境反复重试**;PG 环境请改用 RLS(`managePgDatabase(action=\"execute\")` 跑 `CREATE POLICY`)或控制台管理权限。",
|
|
5181
5189
|
manageTitle: "管理 CloudBase 权限与用户配置",
|
|
5182
|
-
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\"
|
|
5190
|
+
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\"`。\n\n⚠️ 角色类 action(`createRole` / `deleteRoles` / `updateRole`)在 **PostgreSQL 类型环境**下平台侧一律拒绝(`The current API does not support PostgreSQL type environments.`)——平台能力边界,不是配置问题,**不要在 PG 环境反复重试**;PG 环境请改用 RLS(`managePgDatabase(action=\"execute\")` 跑 `CREATE POLICY`)或控制台管理权限。",
|
|
5183
5191
|
writeVerificationHint: "对于 {resourceId} 这类有后端权限控制的集合,前端调用 .doc(id).update() / .doc(id).remove() 后,不能只看是否没有抛异常。请显式检查返回结果中的 updated / deleted 是否大于 0;如果 result.code、result.message 存在,或 updated / deleted 为 0,要把它当作真实失败并向上抛错。",
|
|
5184
5192
|
permissionPropagationHint: "刚更新完 {resourceId} 的安全规则时,后端权限通常在数秒到约 30 秒内生效。若紧接着的真实写操作仍返回 DATABASE_PERMISSION_DENIED,请先间隔数秒用同一登录态重试同一条 .doc(id).update() / .doc(id).remove();不要盲等数分钟,也不要立刻连续重写规则,更不要在短暂传播窗口里把旧拒绝直接当成规则表达式仍然错误。",
|
|
5185
5193
|
validateRegoEmpty: "action=setPolicy 需要非空 regoContent(对齐 CLI `tcb policy set <regoContent>`)。",
|
|
@@ -5246,9 +5254,9 @@ exports.permissions = (0, types_js_1.defineModule)({
|
|
|
5246
5254
|
setPolicySuccess: "用户 OPA Rego 策略设置成功(旧网关鉴权已失效)",
|
|
5247
5255
|
}, {
|
|
5248
5256
|
queryTitle: "Query CloudBase permissions and user config",
|
|
5249
|
-
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
|
|
5257
|
+
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`.\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.",
|
|
5250
5258
|
manageTitle: "Manage CloudBase permissions and user config",
|
|
5251
|
-
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.",
|
|
5259
|
+
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.\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.",
|
|
5252
5260
|
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.",
|
|
5253
5261
|
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.",
|
|
5254
5262
|
validateRegoEmpty: "action=setPolicy requires non-empty regoContent (aligned with CLI `tcb policy set <regoContent>`).",
|
|
@@ -7139,9 +7147,15 @@ function registerAppTools(server) {
|
|
|
7139
7147
|
.optional()
|
|
7140
7148
|
.describe("版本名称。getAppVersion 时可与 buildId 二选一;已知版本号时优先传该值。"),
|
|
7141
7149
|
buildId: zod_1.z
|
|
7142
|
-
|
|
7150
|
+
// ⚠️ 同一字段驱动两条链路、类型要求不同(F13):
|
|
7151
|
+
// getAppVersion → SDK describeAppVersion,收 **string**
|
|
7152
|
+
// getBuildLog → 云 API DescribeCloudBaseRunBuildLog,BuildId 是 **Integer(int64)**
|
|
7153
|
+
// 收 string | number 并统一归一成 string(保证 getAppVersion 链路不回退),
|
|
7154
|
+
// getBuildLog 分支再转 number 传给云 API。
|
|
7155
|
+
.union([zod_1.z.string(), zod_1.z.number()])
|
|
7156
|
+
.transform((value) => String(value).trim())
|
|
7143
7157
|
.optional()
|
|
7144
|
-
.describe("构建 ID
|
|
7158
|
+
.describe("构建 ID(数字或数字字符串均可)。getAppVersion 时可与 versionName 二选一;部署返回 BuildId 后可直接用它轮询状态。getBuildLog 时必填。"),
|
|
7145
7159
|
start: zod_1.z
|
|
7146
7160
|
.number()
|
|
7147
7161
|
.optional()
|
|
@@ -7243,15 +7257,22 @@ function registerAppTools(server) {
|
|
|
7243
7257
|
}, (0, index_js_1.t)("apps.listVersionsSuccess")));
|
|
7244
7258
|
}
|
|
7245
7259
|
if (action === "getBuildLog") {
|
|
7246
|
-
if (
|
|
7260
|
+
if (buildId === undefined || buildId === null || String(buildId).trim() === "") {
|
|
7247
7261
|
throw new Error((0, index_js_1.t)("apps.buildIdRequired"));
|
|
7248
7262
|
}
|
|
7263
|
+
// DescribeCloudBaseRunBuildLog 的 BuildId 是 Integer(int64)(官方文档 876/135707),
|
|
7264
|
+
// 传 string 会被后端拒:The value type of parameter BuildId is not valid, input type
|
|
7265
|
+
// should be int64。schema 侧已归一成 string(getAppVersion 需要 string),此处转 number。
|
|
7266
|
+
const numericBuildId = Number(String(buildId).trim());
|
|
7267
|
+
if (!Number.isInteger(numericBuildId) || numericBuildId <= 0) {
|
|
7268
|
+
throw new Error((0, index_js_1.t)("apps.buildIdMustBeNumeric", { buildId: String(buildId) }));
|
|
7269
|
+
}
|
|
7249
7270
|
const result = await cloudbase.commonService("tcb", "2018-06-08").call({
|
|
7250
7271
|
Action: "DescribeCloudBaseRunBuildLog",
|
|
7251
7272
|
Param: {
|
|
7252
7273
|
EnvId: cloudBaseOptions?.envId || process.env.CLOUDBASE_ENV_ID,
|
|
7253
7274
|
ServiceName: serviceName,
|
|
7254
|
-
BuildId:
|
|
7275
|
+
BuildId: numericBuildId,
|
|
7255
7276
|
Start: start ?? 0,
|
|
7256
7277
|
},
|
|
7257
7278
|
});
|
|
@@ -7260,7 +7281,7 @@ function registerAppTools(server) {
|
|
|
7260
7281
|
return (0, json_content_js_1.jsonContent)(buildEnvelope({
|
|
7261
7282
|
action,
|
|
7262
7283
|
serviceName,
|
|
7263
|
-
buildId,
|
|
7284
|
+
buildId: String(numericBuildId),
|
|
7264
7285
|
logs,
|
|
7265
7286
|
total: result.Response?.Total || logs.length,
|
|
7266
7287
|
nextStart: result.Response?.NextStart,
|
|
@@ -7273,7 +7294,8 @@ function registerAppTools(server) {
|
|
|
7273
7294
|
deployType: "static-hosting",
|
|
7274
7295
|
serviceName,
|
|
7275
7296
|
versionName,
|
|
7276
|
-
|
|
7297
|
+
// 该链路要 string(SDK 类型 IDescribeCloudAppVersionParams.BuildId: string)
|
|
7298
|
+
buildId: buildId === undefined ? undefined : String(buildId),
|
|
7277
7299
|
});
|
|
7278
7300
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
7279
7301
|
// Platform may return Failed/failed; normalize before matching.
|
|
@@ -7325,12 +7347,21 @@ function registerAppTools(server) {
|
|
|
7325
7347
|
.optional()
|
|
7326
7348
|
.describe("要上传并部署的本地项目根目录绝对路径。本地模式下 deployApp 时必填;通常传源码所在目录(含 package.json 和源码),不是 dist 目录。构建产物目录请用 buildPath 指定。cloud mode 下无需传此参数,改用 cosTimestamp。"),
|
|
7327
7349
|
cosTimestamp: zod_1.z
|
|
7328
|
-
.coerce
|
|
7329
|
-
.
|
|
7330
|
-
|
|
7331
|
-
|
|
7350
|
+
// ⚠️ 不能用 z.coerce.number():SDK 的 StaticConfig.CosTimestamp(manager-node
|
|
7351
|
+
// types/cloudApp/types.d.ts `CosTimestamp?: string | null`)与后端 CreateCloudApp
|
|
7352
|
+
// 都要求 **string**。coerce 会把外部传入的值(含 getUploadUrl 返回的 unixTimestamp)
|
|
7353
|
+
// 强制转成 number,后端直接拒绝:
|
|
7354
|
+
// The value type of parameter `StaticConfig.CosTimestamp` is not valid,
|
|
7355
|
+
// input type should be `string`
|
|
7356
|
+
// 本地路径不受影响,是因为 cosTs 来自 SDK uploadCode() 的 string 返回值、不经 zod。
|
|
7357
|
+
// 因此这里收 string | number,统一归一成 string 后再透传。
|
|
7358
|
+
.union([zod_1.z.string(), zod_1.z.number()])
|
|
7359
|
+
.transform((value) => String(value).trim())
|
|
7360
|
+
.refine((value) => /^[1-9]\d*$/.test(value), {
|
|
7361
|
+
message: "cosTimestamp 必须是由数字组成的正整数时间戳,字符串或数字均可;直接使用 getUploadUrl 返回的 unixTimestamp 即可。",
|
|
7362
|
+
})
|
|
7332
7363
|
.optional()
|
|
7333
|
-
.describe("COS
|
|
7364
|
+
.describe("COS 时间戳(getUploadUrl 返回的 unixTimestamp,字符串或数字均可)。传入则直接用已上传的代码创建应用,跳过本地打包上传;需先 getUploadUrl 拿预签名 URL 并 PUT ZIP。cloud mode 必填。与 filePath 严格二选一,同时提供或都不提供都会报错。"),
|
|
7334
7365
|
appPath: zod_1.z
|
|
7335
7366
|
.string()
|
|
7336
7367
|
.optional()
|
|
@@ -7464,7 +7495,10 @@ function registerAppTools(server) {
|
|
|
7464
7495
|
}
|
|
7465
7496
|
}
|
|
7466
7497
|
// Local stdio only: pack directory and upload. Cloud mode must never reach uploadCode.
|
|
7467
|
-
|
|
7498
|
+
// ⚠️ 类型契约(F12):SDK `StaticConfig.CosTimestamp` 与后端 CreateCloudApp 都要求
|
|
7499
|
+
// **string**。本地路径拿到的 `uploadResult.cosTimestamp` 本就是 SDK 给的 string;
|
|
7500
|
+
// 外部传入的可能是 number(MCP 客户端按 JSON Schema 传数字),这里统一归一。
|
|
7501
|
+
let cosTs = cosTimestamp === undefined ? undefined : String(cosTimestamp);
|
|
7468
7502
|
if (!(0, cloud_mode_js_1.isCloudMode)() && filePath) {
|
|
7469
7503
|
// Default excludes large build dirs (empirically target/ can be tens of GB).
|
|
7470
7504
|
// Merge caller ignore with defaults so explicit ignore does not drop safety excludes.
|
|
@@ -7479,7 +7513,9 @@ function registerAppTools(server) {
|
|
|
7479
7513
|
ignore: mergedIgnore,
|
|
7480
7514
|
});
|
|
7481
7515
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, uploadResult);
|
|
7482
|
-
cosTs = uploadResult.cosTimestamp
|
|
7516
|
+
cosTs = uploadResult.cosTimestamp === undefined
|
|
7517
|
+
? undefined
|
|
7518
|
+
: String(uploadResult.cosTimestamp);
|
|
7483
7519
|
}
|
|
7484
7520
|
// 构建命令智能默认值
|
|
7485
7521
|
const resolvedInstallCmd = installCmd ?? "npm install";
|
|
@@ -8008,7 +8044,15 @@ function registerCapiTools(server) {
|
|
|
8008
8044
|
const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions, requireEnvId: false });
|
|
8009
8045
|
server.registerTool?.("callCloudApi", {
|
|
8010
8046
|
title: "capi.title",
|
|
8011
|
-
description: "capi.description"
|
|
8047
|
+
// ⚠️ 不能写成 `description: "capi.description"`(纯词典 key):注册包装层解析
|
|
8048
|
+
// key 时**不带插值参数**,而 {controlPlaneUrl}/{dependencyUrl} 只在错误路径
|
|
8049
|
+
// (下方 t("capi.errorBuild", …))传入 → 描述里的占位符永远替换不了,
|
|
8050
|
+
// tools/list 在 zh 与 en 下都会吐字面 `{controlPlaneUrl}`。
|
|
8051
|
+
// 因此这里按实例语言先解析成最终文案(与 env.ts 的 envQuery 别名同款处理)。
|
|
8052
|
+
description: (0, index_js_1.t)("capi.description", {
|
|
8053
|
+
controlPlaneUrl: CLOUDBASE_CONTROL_PLANE_DOC_URL,
|
|
8054
|
+
dependencyUrl: CLOUDBASE_DEPENDENCY_API_DOC_URL,
|
|
8055
|
+
}, server.lang),
|
|
8012
8056
|
inputSchema: {
|
|
8013
8057
|
service: zod_1.z
|
|
8014
8058
|
.enum(exports.AV)
|
|
@@ -8335,6 +8379,7 @@ exports.buildCloudRunDeployNextStep = buildCloudRunDeployNextStep;
|
|
|
8335
8379
|
exports.buildCloudRunDeployProgressHint = buildCloudRunDeployProgressHint;
|
|
8336
8380
|
exports.queryCloudRunEnvStatus = queryCloudRunEnvStatus;
|
|
8337
8381
|
exports.describeCloudRunEnvStatus = describeCloudRunEnvStatus;
|
|
8382
|
+
exports.isCloudRunEnvNotOpenedError = isCloudRunEnvNotOpenedError;
|
|
8338
8383
|
exports.extractEnvBaseInfoVpc = extractEnvBaseInfoVpc;
|
|
8339
8384
|
exports.resolveCloudRunDeployVpcInfo = resolveCloudRunDeployVpcInfo;
|
|
8340
8385
|
exports.buildCreateCloudRunEnvParam = buildCreateCloudRunEnvParam;
|
|
@@ -8915,6 +8960,36 @@ async function describeCloudRunEnvStatus(manager, envId) {
|
|
|
8915
8960
|
: "unknown";
|
|
8916
8961
|
return { isExist: true, status, baseInfo };
|
|
8917
8962
|
}
|
|
8963
|
+
/**
|
|
8964
|
+
* 判断「云托管环境未开通 / 不存在」类错误,用于 `describeCloudRunEnvStatus` 失败的归一化分支。
|
|
8965
|
+
*
|
|
8966
|
+
* ⚠️ 必须**同时**看 `error.code` 与 `error.message`:
|
|
8967
|
+
* 报错文案有三套来源且会不一致 —— 英文错误码(`ResourceNotFound.CloudRunEnv`)、
|
|
8968
|
+
* 中文兜底(`[DescribeEnvBaseInfo] 资源不存在`)、旧式 `InvalidParameter.*Env`。
|
|
8969
|
+
* 只匹配 message 时,中文「资源不存在」不含 `ResourceNotFound`/`未开通`/`未初始化`,
|
|
8970
|
+
* 守卫会失效并把裸错误抛给模型(国际站未开通云托管的环境实测命中,见 F2)。
|
|
8971
|
+
*
|
|
8972
|
+
* 调用方只有 `DescribeEnvBaseInfo`(唯一资源就是环境),因此把「资源不存在」纳入
|
|
8973
|
+
* 未开通判定不会误伤其他语义。
|
|
8974
|
+
*/
|
|
8975
|
+
function isCloudRunEnvNotOpenedError(error) {
|
|
8976
|
+
if (!error) {
|
|
8977
|
+
return false;
|
|
8978
|
+
}
|
|
8979
|
+
const codeCandidates = [
|
|
8980
|
+
error.code,
|
|
8981
|
+
error.Code,
|
|
8982
|
+
error.original?.code,
|
|
8983
|
+
error.original?.Code,
|
|
8984
|
+
];
|
|
8985
|
+
const codes = codeCandidates
|
|
8986
|
+
.filter((value) => typeof value === "string")
|
|
8987
|
+
.join(" ");
|
|
8988
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8989
|
+
const haystack = `${codes} ${message}`;
|
|
8990
|
+
return (/ResourceNotFound|not.?initialized|未开通|未初始化|资源不存在|环境不存在/i.test(haystack) ||
|
|
8991
|
+
/InvalidParameter.*(?:Env|CloudRun)/i.test(haystack));
|
|
8992
|
+
}
|
|
8918
8993
|
/** CloudRun EnvType for CreateCloudRunEnv (baas DescribeEnvBaseInfo enum). */
|
|
8919
8994
|
exports.CLOUDRUN_ENV_TYPE = "baas";
|
|
8920
8995
|
/**
|
|
@@ -9015,12 +9090,11 @@ async function ensureCloudRunEnvInitialized(options) {
|
|
|
9015
9090
|
return true;
|
|
9016
9091
|
}
|
|
9017
9092
|
catch (error) {
|
|
9018
|
-
|
|
9019
|
-
//
|
|
9093
|
+
// 错误兜底:未初始化/未开通类错误码。判据统一收敛到 isCloudRunEnvNotOpenedError,
|
|
9094
|
+
// 避免「只匹配 message」被本地化文案(中文「资源不存在」)绕过。
|
|
9020
9095
|
// 注意:裸 InvalidParameter 可能是普通参数错误,不拦截;仅当其带 Env/CloudRun
|
|
9021
9096
|
// 上下文(疑似 "EnvironmentId not found / CloudRun Env 未开通")时才按未初始化处理。
|
|
9022
|
-
if (
|
|
9023
|
-
/InvalidParameter.*(?:Env|CloudRun)/i.test(msg)) {
|
|
9097
|
+
if (isCloudRunEnvNotOpenedError(error)) {
|
|
9024
9098
|
throwCloudRunEnvNotInitialized(options.envId);
|
|
9025
9099
|
}
|
|
9026
9100
|
// 其他错误(网络/权限等)不拦截,让上层按原逻辑处理。
|
|
@@ -9584,8 +9658,7 @@ function registerCloudRunTools(server) {
|
|
|
9584
9658
|
}
|
|
9585
9659
|
catch (error) {
|
|
9586
9660
|
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
9587
|
-
if (
|
|
9588
|
-
/InvalidParameter.*(?:Env|CloudRun)/i.test(baseMessage)) {
|
|
9661
|
+
if (isCloudRunEnvNotOpenedError(error)) {
|
|
9589
9662
|
status = { isExist: false, status: "unopened", baseInfo: {} };
|
|
9590
9663
|
}
|
|
9591
9664
|
else {
|
|
@@ -9678,8 +9751,7 @@ function registerCloudRunTools(server) {
|
|
|
9678
9751
|
}
|
|
9679
9752
|
catch (error) {
|
|
9680
9753
|
const baseMessage = error instanceof Error ? error.message : String(error);
|
|
9681
|
-
if (
|
|
9682
|
-
/InvalidParameter.*(?:Env|CloudRun)/i.test(baseMessage)) {
|
|
9754
|
+
if (isCloudRunEnvNotOpenedError(error)) {
|
|
9683
9755
|
current = { isExist: false, status: "unopened", baseInfo: {} };
|
|
9684
9756
|
}
|
|
9685
9757
|
else {
|
|
@@ -19613,7 +19685,7 @@ function registerEnvTools(server) {
|
|
|
19613
19685
|
code: "INVALID_ARGS",
|
|
19614
19686
|
message: (0, index_js_1.t)("env.auth.apiKeyArgsRequired", undefined, outLang),
|
|
19615
19687
|
next_step: (0, tool_result_js_1.buildAuthNextStep)("login_by_api_key", {
|
|
19616
|
-
suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>",
|
|
19688
|
+
suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>", apiKeyEnvId: "<your-env-id>" },
|
|
19617
19689
|
}),
|
|
19618
19690
|
});
|
|
19619
19691
|
}
|
|
@@ -19658,7 +19730,7 @@ function registerEnvTools(server) {
|
|
|
19658
19730
|
auth_mode: "api_key",
|
|
19659
19731
|
envId: toolApiKeyEnvId,
|
|
19660
19732
|
next_step: (0, tool_result_js_1.buildAuthNextStep)("login_by_api_key", {
|
|
19661
|
-
suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>",
|
|
19733
|
+
suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>", apiKeyEnvId: "<your-env-id>" },
|
|
19662
19734
|
}),
|
|
19663
19735
|
});
|
|
19664
19736
|
}
|
|
@@ -19673,7 +19745,7 @@ function registerEnvTools(server) {
|
|
|
19673
19745
|
message: (0, index_js_1.t)("env.auth.apiKeyException", { message }, outLang),
|
|
19674
19746
|
auth_mode: "api_key",
|
|
19675
19747
|
next_step: (0, tool_result_js_1.buildAuthNextStep)("login_by_api_key", {
|
|
19676
|
-
suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>",
|
|
19748
|
+
suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>", apiKeyEnvId: "<your-env-id>" },
|
|
19677
19749
|
}),
|
|
19678
19750
|
});
|
|
19679
19751
|
}
|
|
@@ -19982,14 +20054,7 @@ function registerEnvTools(server) {
|
|
|
19982
20054
|
}
|
|
19983
20055
|
(0, logger_js_1.debug)("降级到 listEnvs() 也失败:", fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError)));
|
|
19984
20056
|
const enhancedMessage = buildEnvQueryErrorMessage(fallbackError, "list");
|
|
19985
|
-
return
|
|
19986
|
-
content: [
|
|
19987
|
-
{
|
|
19988
|
-
type: "text",
|
|
19989
|
-
text: enhancedMessage,
|
|
19990
|
-
},
|
|
19991
|
-
],
|
|
19992
|
-
};
|
|
20057
|
+
return (0, tool_result_js_1.buildTextErrorResult)(enhancedMessage);
|
|
19993
20058
|
}
|
|
19994
20059
|
}
|
|
19995
20060
|
result = buildEnvQueryListResult({
|
|
@@ -20172,14 +20237,7 @@ function registerEnvTools(server) {
|
|
|
20172
20237
|
return toolPayloadResult;
|
|
20173
20238
|
}
|
|
20174
20239
|
const enhancedMessage = buildEnvQueryErrorMessage(error, action);
|
|
20175
|
-
return
|
|
20176
|
-
content: [
|
|
20177
|
-
{
|
|
20178
|
-
type: "text",
|
|
20179
|
-
text: enhancedMessage,
|
|
20180
|
-
},
|
|
20181
|
-
],
|
|
20182
|
-
};
|
|
20240
|
+
return (0, tool_result_js_1.buildTextErrorResult)(enhancedMessage);
|
|
20183
20241
|
}
|
|
20184
20242
|
};
|
|
20185
20243
|
// Register primary tool name (queryEnv)
|
|
@@ -20313,16 +20371,9 @@ function registerEnvTools(server) {
|
|
|
20313
20371
|
if (toolPayloadResult) {
|
|
20314
20372
|
return toolPayloadResult;
|
|
20315
20373
|
}
|
|
20316
|
-
return {
|
|
20317
|
-
|
|
20318
|
-
|
|
20319
|
-
type: "text",
|
|
20320
|
-
text: (0, index_js_1.t)("env.domain.operationFailed", {
|
|
20321
|
-
message: error instanceof Error ? error.message : String(error),
|
|
20322
|
-
}),
|
|
20323
|
-
},
|
|
20324
|
-
],
|
|
20325
|
-
};
|
|
20374
|
+
return (0, tool_result_js_1.buildTextErrorResult)((0, index_js_1.t)("env.domain.operationFailed", {
|
|
20375
|
+
message: error instanceof Error ? error.message : String(error),
|
|
20376
|
+
}));
|
|
20326
20377
|
}
|
|
20327
20378
|
});
|
|
20328
20379
|
} // end: wxide guard for envDomainManagement
|
|
@@ -20848,14 +20899,7 @@ function registerEnvTools(server) {
|
|
|
20848
20899
|
message: (0, index_js_1.t)("env.manage.listPackagesFailed", { message: errorMessage }),
|
|
20849
20900
|
});
|
|
20850
20901
|
}
|
|
20851
|
-
return {
|
|
20852
|
-
content: [
|
|
20853
|
-
{
|
|
20854
|
-
type: "text",
|
|
20855
|
-
text: (0, index_js_1.t)("env.manage.operationFailed", { message: errorMessage }),
|
|
20856
|
-
},
|
|
20857
|
-
],
|
|
20858
|
-
};
|
|
20902
|
+
return (0, tool_result_js_1.buildTextErrorResult)((0, index_js_1.t)("env.manage.operationFailed", { message: errorMessage }));
|
|
20859
20903
|
}
|
|
20860
20904
|
});
|
|
20861
20905
|
}
|
|
@@ -24202,6 +24246,123 @@ function registerGatewayTools(server) {
|
|
|
24202
24246
|
data: {},
|
|
24203
24247
|
message: error instanceof Error ? error.message : String(error),
|
|
24204
24248
|
});
|
|
24249
|
+
/**
|
|
24250
|
+
* 上游目标存在性校验(F1)。
|
|
24251
|
+
*
|
|
24252
|
+
* 背景:`createHttpServiceRoute` 不校验 `UpstreamResourceName` 是否存在,预检
|
|
24253
|
+
* (VerifyHTTPServiceRoute)也只查域名归属/证书/配额,因此**不存在的上游也能真实落库并
|
|
24254
|
+
* 返回 success**,形成悬空路由(path 被占用、指向不存在的目标)。实测两站复现。
|
|
24255
|
+
*
|
|
24256
|
+
* 设计原则:**只在能证明「不存在」时拒绝**。
|
|
24257
|
+
* - 列表接口报错、分页未扫全、类型不支持 → `unknown`,放行并记日志(fail-open)。
|
|
24258
|
+
* 校验失败绝不该阻塞用户创建合法路由。
|
|
24259
|
+
* - 只有扫完全量且确实没有该名称时,才返回 `missing`。
|
|
24260
|
+
*
|
|
24261
|
+
* 覆盖范围:SCF / WEB_SCF(函数列表)、CBR(云托管服务列表)。
|
|
24262
|
+
* NOT covered:STATIC_STORE 的 UpstreamResourceName 是静态托管**实例名/固定别名
|
|
24263
|
+
* staticstore**(不是 CloudApp 应用名),可靠列举要额外走 DescribeStaticStore,且该资源
|
|
24264
|
+
* 随托管启用必然存在、悬空风险低;LH(轻量应用服务器)无列举接口。两者一律 fail-open。
|
|
24265
|
+
*/
|
|
24266
|
+
const UPSTREAM_PROBE_PAGE_SIZE = 100;
|
|
24267
|
+
const UPSTREAM_PROBE_MAX_PAGES = 5;
|
|
24268
|
+
const UPSTREAM_PROBE_CANDIDATE_LIMIT = 10;
|
|
24269
|
+
const probeUpstreamResource = async (cloudbase, params) => {
|
|
24270
|
+
const wanted = params.upstreamResourceName.trim();
|
|
24271
|
+
const upstreamResourceType = params.upstreamResourceType;
|
|
24272
|
+
try {
|
|
24273
|
+
if (upstreamResourceType === "SCF" || upstreamResourceType === "WEB_SCF") {
|
|
24274
|
+
// fail-open:老 SDK / mock 没有该 API 时不校验,绝不让「拿不到列表」变成「判定不存在」。
|
|
24275
|
+
if (typeof cloudbase.functions?.getFunctionList !== "function") {
|
|
24276
|
+
return { status: "unknown", reason: "function-list-api-unavailable" };
|
|
24277
|
+
}
|
|
24278
|
+
const names = new Set();
|
|
24279
|
+
let scanned = 0;
|
|
24280
|
+
let total = Number.POSITIVE_INFINITY;
|
|
24281
|
+
for (let page = 0; page < UPSTREAM_PROBE_MAX_PAGES; page += 1) {
|
|
24282
|
+
const res = await cloudbase.functions.getFunctionList(UPSTREAM_PROBE_PAGE_SIZE, scanned);
|
|
24283
|
+
const list = Array.isArray(res?.Functions) ? res.Functions : [];
|
|
24284
|
+
for (const item of list) {
|
|
24285
|
+
const name = item?.FunctionName;
|
|
24286
|
+
if (typeof name === "string" && name) {
|
|
24287
|
+
names.add(name);
|
|
24288
|
+
}
|
|
24289
|
+
}
|
|
24290
|
+
scanned += list.length;
|
|
24291
|
+
total = typeof res?.TotalCount === "number" ? res.TotalCount : scanned;
|
|
24292
|
+
if (names.has(wanted) || list.length === 0 || scanned >= total) {
|
|
24293
|
+
break;
|
|
24294
|
+
}
|
|
24295
|
+
}
|
|
24296
|
+
if (names.has(wanted)) {
|
|
24297
|
+
return { status: "exists" };
|
|
24298
|
+
}
|
|
24299
|
+
if (scanned >= total) {
|
|
24300
|
+
return {
|
|
24301
|
+
status: "missing",
|
|
24302
|
+
candidates: [...names].slice(0, UPSTREAM_PROBE_CANDIDATE_LIMIT),
|
|
24303
|
+
};
|
|
24304
|
+
}
|
|
24305
|
+
return { status: "unknown", reason: "function-list-partially-scanned" };
|
|
24306
|
+
}
|
|
24307
|
+
if (upstreamResourceType === "CBR") {
|
|
24308
|
+
// fail-open:同上,`cloudrun?.list` 缺失时会短路成 undefined,若不显式拦截会被
|
|
24309
|
+
// 误判成「列表为空 = 目标不存在」,从而错误拒绝合法路由。
|
|
24310
|
+
if (typeof cloudbase.cloudrun?.list !== "function") {
|
|
24311
|
+
return { status: "unknown", reason: "cloudrun-list-api-unavailable" };
|
|
24312
|
+
}
|
|
24313
|
+
const names = new Set();
|
|
24314
|
+
for (let page = 1; page <= UPSTREAM_PROBE_MAX_PAGES; page += 1) {
|
|
24315
|
+
const res = await cloudbase.cloudrun.list({
|
|
24316
|
+
pageSize: UPSTREAM_PROBE_PAGE_SIZE,
|
|
24317
|
+
pageNum: page,
|
|
24318
|
+
});
|
|
24319
|
+
const list = Array.isArray(res?.ServerList) ? res.ServerList : [];
|
|
24320
|
+
for (const item of list) {
|
|
24321
|
+
const record = item;
|
|
24322
|
+
const name = record?.ServerName ?? record?.Name;
|
|
24323
|
+
if (typeof name === "string" && name) {
|
|
24324
|
+
names.add(name);
|
|
24325
|
+
}
|
|
24326
|
+
}
|
|
24327
|
+
const total = typeof res?.Total === "number" ? res.Total : names.size;
|
|
24328
|
+
if (names.has(wanted) || list.length === 0 || names.size >= total) {
|
|
24329
|
+
break;
|
|
24330
|
+
}
|
|
24331
|
+
}
|
|
24332
|
+
if (names.has(wanted)) {
|
|
24333
|
+
return { status: "exists" };
|
|
24334
|
+
}
|
|
24335
|
+
return {
|
|
24336
|
+
status: "missing",
|
|
24337
|
+
candidates: [...names].slice(0, UPSTREAM_PROBE_CANDIDATE_LIMIT),
|
|
24338
|
+
};
|
|
24339
|
+
}
|
|
24340
|
+
// STATIC_STORE / LH:无可靠的实例列举通路,不校验(见上方说明)。
|
|
24341
|
+
return { status: "unknown", reason: `unsupported-upstream-type:${upstreamResourceType}` };
|
|
24342
|
+
}
|
|
24343
|
+
catch (error) {
|
|
24344
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24345
|
+
return { status: "unknown", reason: message };
|
|
24346
|
+
}
|
|
24347
|
+
};
|
|
24348
|
+
/** 上游缺失时的候选列举工具,按类型给出下一步可用的只读入口。 */
|
|
24349
|
+
const upstreamProbeNextAction = (upstreamResourceType) => {
|
|
24350
|
+
if (upstreamResourceType === "SCF" || upstreamResourceType === "WEB_SCF") {
|
|
24351
|
+
return {
|
|
24352
|
+
tool: "queryFunctions",
|
|
24353
|
+
action: "listFunctions",
|
|
24354
|
+
reason: (0, index_js_1.t)("gateway.create.upstreamNotFoundReason"),
|
|
24355
|
+
};
|
|
24356
|
+
}
|
|
24357
|
+
if (upstreamResourceType === "CBR") {
|
|
24358
|
+
return {
|
|
24359
|
+
tool: "queryCloudRun",
|
|
24360
|
+
action: "list",
|
|
24361
|
+
reason: (0, index_js_1.t)("gateway.create.upstreamNotFoundReason"),
|
|
24362
|
+
};
|
|
24363
|
+
}
|
|
24364
|
+
return null;
|
|
24365
|
+
};
|
|
24205
24366
|
const verifyHttpServiceRouteOrFail = async (params) => {
|
|
24206
24367
|
const cloudbase = await getManager();
|
|
24207
24368
|
let result;
|
|
@@ -24688,6 +24849,46 @@ function registerGatewayTools(server) {
|
|
|
24688
24849
|
case "createRoute": {
|
|
24689
24850
|
const cloudbase = await getManager();
|
|
24690
24851
|
const payload = await normalizeRoutePayload(input);
|
|
24852
|
+
// 上游目标存在性校验(F1):平台侧不校验 UpstreamResourceName,不存在的目标也会
|
|
24853
|
+
// 真实落库形成悬空路由并返回 success。能证明不存在就拒绝,校验不了就放行(fail-open)。
|
|
24854
|
+
const upstreamProbe = await probeUpstreamResource(cloudbase, {
|
|
24855
|
+
upstreamResourceType: payload.resolved.upstreamResourceType,
|
|
24856
|
+
upstreamResourceName: payload.resolved.upstreamResourceName,
|
|
24857
|
+
});
|
|
24858
|
+
if (upstreamProbe.status === "missing") {
|
|
24859
|
+
const nextAction = upstreamProbeNextAction(payload.resolved.upstreamResourceType);
|
|
24860
|
+
return {
|
|
24861
|
+
success: false,
|
|
24862
|
+
data: {
|
|
24863
|
+
action: "createRoute",
|
|
24864
|
+
upstreamResourceType: payload.resolved.upstreamResourceType,
|
|
24865
|
+
targetName: payload.resolved.upstreamResourceName,
|
|
24866
|
+
path: payload.resolved.path,
|
|
24867
|
+
candidates: upstreamProbe.candidates,
|
|
24868
|
+
...(upstreamProbe.candidates.length
|
|
24869
|
+
? {
|
|
24870
|
+
candidateHint: (0, index_js_1.t)("gateway.create.upstreamCandidates", {
|
|
24871
|
+
type: payload.resolved.upstreamResourceType,
|
|
24872
|
+
candidates: upstreamProbe.candidates.join("、"),
|
|
24873
|
+
}),
|
|
24874
|
+
}
|
|
24875
|
+
: {}),
|
|
24876
|
+
},
|
|
24877
|
+
message: (0, index_js_1.t)("gateway.create.upstreamNotFound", {
|
|
24878
|
+
type: payload.resolved.upstreamResourceType,
|
|
24879
|
+
name: payload.resolved.upstreamResourceName,
|
|
24880
|
+
}),
|
|
24881
|
+
...(nextAction ? { nextActions: [nextAction] } : {}),
|
|
24882
|
+
};
|
|
24883
|
+
}
|
|
24884
|
+
if (upstreamProbe.status === "unknown") {
|
|
24885
|
+
// 校验不了不阻塞创建,但要留痕,便于排查「为什么这次没拦住」。
|
|
24886
|
+
server.logger?.({
|
|
24887
|
+
type: "errorToolCall",
|
|
24888
|
+
toolName: "manageGateway",
|
|
24889
|
+
message: `createRoute upstream probe skipped (${payload.resolved.upstreamResourceType}/${payload.resolved.upstreamResourceName}): ${upstreamProbe.reason}`,
|
|
24890
|
+
});
|
|
24891
|
+
}
|
|
24691
24892
|
// Probe → create: ownership / cert / quota / conflict checks before create.
|
|
24692
24893
|
const verifyFailure = await verifyHttpServiceRouteOrFail({
|
|
24693
24894
|
envId: payload.EnvId,
|
|
@@ -26277,9 +26478,28 @@ function registerHostingTools(server) {
|
|
|
26277
26478
|
try {
|
|
26278
26479
|
const checkResult = await cloudbase.hosting.findFiles({
|
|
26279
26480
|
prefix: input.cloudPath,
|
|
26280
|
-
maxKeys:
|
|
26481
|
+
maxKeys: 100,
|
|
26281
26482
|
});
|
|
26282
|
-
|
|
26483
|
+
// ⚠️ 回查有两个坑,都会让 verified 失真:
|
|
26484
|
+
// 1) findFiles 返回 **COS 风格对象**(列表在 Contents 内),直接用
|
|
26485
|
+
// `Array.isArray(checkResult)` 判断会静默失效、恒判 verified=true。
|
|
26486
|
+
// 统一走 extractFileList 归一化。
|
|
26487
|
+
// 2) findFiles 是 **prefix** 语义:删单文件 /a/b.txt 时,若同前缀的
|
|
26488
|
+
// /a/b.txt.bak 还在也会命中 → 把「已删成功」误判成未验证。
|
|
26489
|
+
// 单文件场景必须精确比对 Key(前缀场景见 normalizeFileFields 的 key 字段)。
|
|
26490
|
+
const remaining = normalizeFileFields(checkResult);
|
|
26491
|
+
const stripLeadingSlash = (value) => value.replace(/^\/+/, '');
|
|
26492
|
+
const targetPath = stripLeadingSlash(input.cloudPath ?? '');
|
|
26493
|
+
const stillExists = input.isDir
|
|
26494
|
+
? remaining.length > 0
|
|
26495
|
+
: remaining.some((file) => {
|
|
26496
|
+
const key = typeof file.key === 'string' ? file.key : '';
|
|
26497
|
+
// 拿不到 Key 时保守按「仍存在」处理,避免误报已验证
|
|
26498
|
+
if (!key)
|
|
26499
|
+
return true;
|
|
26500
|
+
return stripLeadingSlash(key) === targetPath;
|
|
26501
|
+
});
|
|
26502
|
+
if (stillExists) {
|
|
26283
26503
|
deleteVerified = false;
|
|
26284
26504
|
verificationError = verificationError ?? (0, index_js_1.t)('hosting.deleteVerifyFailed');
|
|
26285
26505
|
}
|
|
@@ -32504,7 +32724,7 @@ class TelemetryReporter {
|
|
|
32504
32724
|
const arch = os_1.default.arch(); // 系统架构
|
|
32505
32725
|
// Build-time injected version; guard for vitest / non-webpack runtimes.
|
|
32506
32726
|
const mcpVersion = process.env.npm_package_version ||
|
|
32507
|
-
( true ? "2.34.
|
|
32727
|
+
( true ? "2.34.3" : 0);
|
|
32508
32728
|
return {
|
|
32509
32729
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
32510
32730
|
deviceId: this.deviceId,
|
|
@@ -32908,6 +33128,7 @@ exports.ToolPayloadError = void 0;
|
|
|
32908
33128
|
exports.buildJsonToolResult = buildJsonToolResult;
|
|
32909
33129
|
__webpack_unused_export__ = isBusinessFailureToolResult;
|
|
32910
33130
|
exports.withBusinessFailureIsError = withBusinessFailureIsError;
|
|
33131
|
+
exports.buildTextErrorResult = buildTextErrorResult;
|
|
32911
33132
|
exports.isToolPayloadError = isToolPayloadError;
|
|
32912
33133
|
exports.toolPayloadErrorToResult = toolPayloadErrorToResult;
|
|
32913
33134
|
exports.buildAuthNextStep = buildAuthNextStep;
|
|
@@ -32974,6 +33195,28 @@ function withBusinessFailureIsError(result) {
|
|
|
32974
33195
|
}
|
|
32975
33196
|
return { ...record, isError: true };
|
|
32976
33197
|
}
|
|
33198
|
+
/**
|
|
33199
|
+
* 纯文本失败回执:**必须显式带 `isError: true`**。
|
|
33200
|
+
*
|
|
33201
|
+
* 为什么需要它:`withBusinessFailureIsError` 只能识别结构化的 `{ success: false }`
|
|
33202
|
+
* 载荷(`isBusinessFailureToolResult` 会 JSON.parse 文本内容再嗅 `success`)。工具在自己
|
|
33203
|
+
* 的 catch 里返回 `{ content: [{ type: "text", text }] }` 这种**纯文本**回执时,文本不是
|
|
33204
|
+
* JSON,嗅探不到任何失败标记 → 客户端只看到 `isError: false`,把失败当成功处理。
|
|
33205
|
+
*
|
|
33206
|
+
* 实测(F7):`queryEnv(action="usage")` 不带 envId 时抛出的引导文案就是这样返回的,
|
|
33207
|
+
* 只看 `isError` 的客户端会漏判。MCP 协议里 `isError` 是唯一可靠的失败标记,别省。
|
|
33208
|
+
*/
|
|
33209
|
+
function buildTextErrorResult(text) {
|
|
33210
|
+
return {
|
|
33211
|
+
content: [
|
|
33212
|
+
{
|
|
33213
|
+
type: "text",
|
|
33214
|
+
text,
|
|
33215
|
+
},
|
|
33216
|
+
],
|
|
33217
|
+
isError: true,
|
|
33218
|
+
};
|
|
33219
|
+
}
|
|
32977
33220
|
class ToolPayloadError extends Error {
|
|
32978
33221
|
payload;
|
|
32979
33222
|
constructor(payload) {
|
|
@@ -33085,6 +33328,9 @@ ${envId}
|
|
|
33085
33328
|
// 构建标题
|
|
33086
33329
|
const title = `MCP工具错误: ${toolName}`;
|
|
33087
33330
|
// 构建问题描述
|
|
33331
|
+
// ⚠️ MCP 版本必须优先取构建期注入的 __MCP_VERSION__:hosted 场景下进程是 BFF 的进程,
|
|
33332
|
+
// process.env.npm_package_version 是**宿主应用**的版本(实测报出 0.0.1,与 MCP 自身
|
|
33333
|
+
// 版本 2.34.2 自相矛盾);仅在未注入(源码直跑)时才回退到它。
|
|
33088
33334
|
const body = `## 错误描述
|
|
33089
33335
|
工具 \`${toolName}\` 执行时发生错误
|
|
33090
33336
|
|
|
@@ -33096,7 +33342,7 @@ ${envIdSection}
|
|
|
33096
33342
|
## 环境信息
|
|
33097
33343
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
33098
33344
|
- Node.js版本: ${process.version}
|
|
33099
|
-
- MCP 版本:${
|
|
33345
|
+
- MCP 版本:${( true ? "2.34.3" : 0) || 0 || 0}
|
|
33100
33346
|
- 系统架构: ${os_1.default.arch()}
|
|
33101
33347
|
- 时间: ${new Date().toISOString()}
|
|
33102
33348
|
- 请求ID: ${requestId}
|
|
@@ -33185,7 +33431,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
33185
33431
|
requestId,
|
|
33186
33432
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
33187
33433
|
});
|
|
33188
|
-
const mcpVersion = true ? "2.34.
|
|
33434
|
+
const mcpVersion = true ? "2.34.3" : 0;
|
|
33189
33435
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
33190
33436
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
33191
33437
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|