@cloudbase/cloudbase-mcp 2.33.0 → 2.33.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ exports.getAuthProgressState = getAuthProgressState;
38
38
  exports.resolveAuthOptions = resolveAuthOptions;
39
39
  exports.getAuthConfigValidationError = getAuthConfigValidationError;
40
40
  exports.buildAuthConfigSummary = buildAuthConfigSummary;
41
+ exports.buildDeviceLoginOptions = buildDeviceLoginOptions;
41
42
  exports.setPendingAuthProgressState = setPendingAuthProgressState;
42
43
  exports.resolveAuthProgressState = resolveAuthProgressState;
43
44
  exports.rejectAuthProgressState = rejectAuthProgressState;
@@ -256,9 +257,10 @@ function getAuthConfigValidationError(options) {
256
257
  if (options.oauthCustom && !options.oauthEndpoint) {
257
258
  return "oauthCustom=true 时必须同时提供 oauthEndpoint。";
258
259
  }
259
- if (options.oauthEndpoint && !options.oauthCustom) {
260
- return "配置自定义 oauthEndpoint 时必须启用 oauthCustom=true。";
261
- }
260
+ // oauthEndpoint + 显式 oauthCustom=false 放行:resolveAuthOptions 对"只配 endpoint
261
+ // 未配 custom"已默认 custom=true,走到这里的 endpoint+false 只可能是用户显式指定——
262
+ // 即标准 {code,result} 包装格式的自定义端点(如国际站 tcb-api.tencentcloud.com),
263
+ // 拦截会导致标准格式端点在工具层永远不可用。
262
264
  return null;
263
265
  }
264
266
  function buildAuthConfigSummary(options) {
@@ -270,6 +272,40 @@ function buildAuthConfigSummary(options) {
270
272
  uses_toolbox_defaults: options.usesToolboxDefaults,
271
273
  };
272
274
  }
275
+ /**
276
+ * 构造 device flow 的 loginByWebAuth 基础参数(onDeviceCode 由调用方按需注入)。
277
+ *
278
+ * 所有 device 登录路径(ensureLogin 与 auth 工具 start_auth 的 device 分支)必须
279
+ * 共享本函数:TCB_SITE=intl 时的 getOAuthEndpoint 覆写与 getAuthUrl 授权页改写
280
+ * 只在 ensureLogin 生效,会导致工具层直连路径仍打国内站端点/授权页,国际站账号
281
+ * 无法完成授权(device-code 注册表国内外隔离,国内授权页对国际站账号无效)。
282
+ */
283
+ function buildDeviceLoginOptions(resolvedAuthOptions, siteHints = {}) {
284
+ const resolvedSite = (0, site_map_js_1.resolveSite)(siteHints.region, siteHints.site ?? process.env.TCB_SITE);
285
+ const loginOptions = { flow: "device" };
286
+ if (resolvedAuthOptions.clientId) {
287
+ loginOptions.client_id = resolvedAuthOptions.clientId;
288
+ }
289
+ if (resolvedAuthOptions.oauthEndpoint) {
290
+ loginOptions.getOAuthEndpoint = () => resolvedAuthOptions.oauthEndpoint;
291
+ }
292
+ else if (resolvedSite === "intl") {
293
+ // 国际站 OAuth 后端独立部署(2026-09-01 实测 device/code+token 可用,注册表与国内站隔离);
294
+ // 不覆写时 toolbox 默认打国内站端点,国际站账号无法完成授权
295
+ loginOptions.getOAuthEndpoint = () => site_map_js_1.SITE_REGION_MAP.intl.oauthEndpoint;
296
+ }
297
+ if (resolvedSite === "intl") {
298
+ // device 模式的 verification_uri 标准模式下由客户端拼接,指向国内站授权页;
299
+ // 与 web 模式同款 host 改写,把授权页切到国际站
300
+ loginOptions.getAuthUrl = (url) => url
301
+ .replace(`${site_map_js_1.SITE_REGION_MAP.domestic.authHost}/dev`, `${site_map_js_1.SITE_REGION_MAP.intl.authHost}/dev`)
302
+ .replace(site_map_js_1.SITE_REGION_MAP.domestic.authHost, site_map_js_1.SITE_REGION_MAP.intl.authHost);
303
+ }
304
+ if (resolvedAuthOptions.oauthCustom) {
305
+ loginOptions.custom = true;
306
+ }
307
+ return loginOptions;
308
+ }
273
309
  function setPendingAuthProgressState(challenge, authMode = "device") {
274
310
  return updateAuthProgressState({
275
311
  status: "PENDING",
@@ -509,27 +545,11 @@ async function ensureLogin(options) {
509
545
  };
510
546
  }
511
547
  else {
512
- if (resolvedAuthOptions.clientId) {
513
- loginOptions.client_id = resolvedAuthOptions.clientId;
514
- }
515
- if (resolvedAuthOptions.oauthEndpoint) {
516
- loginOptions.getOAuthEndpoint = () => resolvedAuthOptions.oauthEndpoint;
517
- }
518
- else if (resolvedSite === "intl") {
519
- // 国际站 OAuth 后端独立部署(2026-09-01 实测 device/code+token 可用,注册表与国内站隔离);
520
- // 不覆写时 toolbox 默认打国内站端点,国际站账号无法完成授权
521
- loginOptions.getOAuthEndpoint = () => site_map_js_1.SITE_REGION_MAP.intl.oauthEndpoint;
522
- }
523
- if (resolvedSite === "intl") {
524
- // device 模式的 verification_uri 标准模式下由客户端拼接,指向国内站授权页;
525
- // 与 web 模式同款 host 改写,把授权页切到国际站
526
- loginOptions.getAuthUrl = (url) => url
527
- .replace(`${site_map_js_1.SITE_REGION_MAP.domestic.authHost}/dev`, `${site_map_js_1.SITE_REGION_MAP.intl.authHost}/dev`)
528
- .replace(site_map_js_1.SITE_REGION_MAP.domestic.authHost, site_map_js_1.SITE_REGION_MAP.intl.authHost);
529
- }
530
- if (resolvedAuthOptions.oauthCustom) {
531
- loginOptions.custom = true;
532
- }
548
+ // device 模式:与 auth 工具 start_auth device 分支共享 intl 端点覆写与授权页改写
549
+ Object.assign(loginOptions, buildDeviceLoginOptions(resolvedAuthOptions, {
550
+ region: options?.region,
551
+ site: options?.site,
552
+ }));
533
553
  if (options?.onDeviceCode) {
534
554
  loginOptions.onDeviceCode = (info) => {
535
555
  setPendingAuthProgressState(info, mode);
@@ -2071,7 +2091,7 @@ __webpack_unused_export__ = Object.values(exports.TCB_ACTION_INDEX_MAP);
2071
2091
 
2072
2092
 
2073
2093
  Object.defineProperty(exports, "__esModule", ({ value: true }));
2074
- exports.telemetryReporter = exports.reportToolkitLifecycle = exports.reportToolCall = exports.warn = exports.info = exports.error = exports.StdioServerTransport = void 0;
2094
+ exports.normalizeClientName = exports.telemetryReporter = exports.reportToolkitLifecycle = exports.reportToolCall = exports.warn = exports.info = exports.error = exports.StdioServerTransport = void 0;
2075
2095
  exports.createCloudBaseMcpServer = createCloudBaseMcpServer;
2076
2096
  exports.getDefaultServer = getDefaultServer;
2077
2097
  const mcp_js_1 = __webpack_require__(4619);
@@ -2089,6 +2109,7 @@ const storage_js_1 = __webpack_require__(3138);
2089
2109
  const types_js_1 = __webpack_require__(2344);
2090
2110
  const capi_js_1 = __webpack_require__(8900);
2091
2111
  const cloudrun_js_1 = __webpack_require__(3277);
2112
+ const deploy_js_1 = __webpack_require__(3668);
2092
2113
  const dataModel_js_1 = __webpack_require__(2722);
2093
2114
  const gateway_js_1 = __webpack_require__(9921);
2094
2115
  const agents_js_1 = __webpack_require__(8409);
@@ -2102,6 +2123,7 @@ const logger_js_1 = __webpack_require__(781);
2102
2123
  const site_map_js_1 = __webpack_require__(4533);
2103
2124
  const tool_result_js_1 = __webpack_require__(9645);
2104
2125
  const tool_wrapper_js_1 = __webpack_require__(3389);
2126
+ const telemetry_js_1 = __webpack_require__(5198);
2105
2127
  // 默认插件列表
2106
2128
  const DEFAULT_PLUGINS = [
2107
2129
  "env",
@@ -2115,6 +2137,7 @@ const DEFAULT_PLUGINS = [
2115
2137
  "setup",
2116
2138
  "rag",
2117
2139
  "cloudrun",
2140
+ "deploy",
2118
2141
  "gateway",
2119
2142
  "app-auth",
2120
2143
  "apps",
@@ -2168,6 +2191,7 @@ const AVAILABLE_PLUGINS = {
2168
2191
  agents: { name: "agents", register: agents_js_1.registerAgentTools },
2169
2192
  apps: { name: "apps", register: apps_js_1.registerAppTools },
2170
2193
  cloudrun: { name: "cloudrun", register: cloudrun_js_1.registerCloudRunTools },
2194
+ deploy: { name: "deploy", register: deploy_js_1.registerDeployTools },
2171
2195
  capi: { name: "capi", register: capi_js_1.registerCapiTools },
2172
2196
  "msg-push": { name: "msg-push", register: msg_push_js_1.registerMsgPushTools },
2173
2197
  };
@@ -2245,7 +2269,7 @@ function parseEnabledPlugins(pluginsEnabled, pluginsDisabled) {
2245
2269
  * await server.connect(transport);
2246
2270
  */
2247
2271
  async function createCloudBaseMcpServer(options) {
2248
- const { name = "cloudbase-mcp", version = "1.0.0", enableTelemetry = true, cloudBaseOptions, authOptions, cloudMode = false, ide, logger, pluginsEnabled, pluginsDisabled, pluginOptions, } = options ?? {};
2272
+ const { name = "cloudbase-mcp", version = "1.0.0", enableTelemetry = true, cloudBaseOptions, authOptions, cloudMode = false, ide, client, logger, pluginsEnabled, pluginsDisabled, pluginOptions, } = options ?? {};
2249
2273
  // Enable cloud mode if specified
2250
2274
  if (cloudMode) {
2251
2275
  (0, cloud_mode_js_1.enableCloudMode)();
@@ -2308,6 +2332,11 @@ async function createCloudBaseMcpServer(options) {
2308
2332
  if (ide) {
2309
2333
  server.ide = ide;
2310
2334
  }
2335
+ // Store client in server instance for telemetry (normalized, invalid values dropped)
2336
+ const normalizedClient = (0, telemetry_js_1.normalizeClientName)(client);
2337
+ if (normalizedClient) {
2338
+ server.client = normalizedClient;
2339
+ }
2311
2340
  // Store logger in server instance for tools to access
2312
2341
  if (logger) {
2313
2342
  server.logger = logger;
@@ -2342,10 +2371,11 @@ var logger_js_2 = __webpack_require__(781);
2342
2371
  Object.defineProperty(exports, "error", ({ enumerable: true, get: function () { return logger_js_2.error; } }));
2343
2372
  Object.defineProperty(exports, "info", ({ enumerable: true, get: function () { return logger_js_2.info; } }));
2344
2373
  Object.defineProperty(exports, "warn", ({ enumerable: true, get: function () { return logger_js_2.warn; } }));
2345
- var telemetry_js_1 = __webpack_require__(5198);
2346
- Object.defineProperty(exports, "reportToolCall", ({ enumerable: true, get: function () { return telemetry_js_1.reportToolCall; } }));
2347
- Object.defineProperty(exports, "reportToolkitLifecycle", ({ enumerable: true, get: function () { return telemetry_js_1.reportToolkitLifecycle; } }));
2348
- Object.defineProperty(exports, "telemetryReporter", ({ enumerable: true, get: function () { return telemetry_js_1.telemetryReporter; } }));
2374
+ var telemetry_js_2 = __webpack_require__(5198);
2375
+ Object.defineProperty(exports, "reportToolCall", ({ enumerable: true, get: function () { return telemetry_js_2.reportToolCall; } }));
2376
+ Object.defineProperty(exports, "reportToolkitLifecycle", ({ enumerable: true, get: function () { return telemetry_js_2.reportToolkitLifecycle; } }));
2377
+ Object.defineProperty(exports, "telemetryReporter", ({ enumerable: true, get: function () { return telemetry_js_2.telemetryReporter; } }));
2378
+ Object.defineProperty(exports, "normalizeClientName", ({ enumerable: true, get: function () { return telemetry_js_2.normalizeClientName; } }));
2349
2379
 
2350
2380
 
2351
2381
  /***/ },
@@ -3905,7 +3935,7 @@ function registerAppTools(server) {
3905
3935
  var __webpack_unused_export__;
3906
3936
 
3907
3937
  __webpack_unused_export__ = ({ value: true });
3908
- exports.rg = void 0;
3938
+ exports.rg = exports.AV = void 0;
3909
3939
  __webpack_unused_export__ = assertTcbCloudRunActionAllowed;
3910
3940
  __webpack_unused_export__ = resolveCloudApiRegionAndParams;
3911
3941
  __webpack_unused_export__ = removeEmptyStringParams;
@@ -3919,7 +3949,7 @@ const tcb_action_index_js_1 = __webpack_require__(2550);
3919
3949
  const CATEGORY = "cloud-api";
3920
3950
  const CLOUDBASE_CONTROL_PLANE_DOC_URL = "https://cloud.tencent.com/document/product/876/34809";
3921
3951
  const CLOUDBASE_DEPENDENCY_API_DOC_URL = "https://cloud.tencent.com/document/product/876/34808";
3922
- const ALLOWED_SERVICES = [
3952
+ exports.AV = [
3923
3953
  "tcb",
3924
3954
  "tcbr",
3925
3955
  "scf",
@@ -3928,6 +3958,8 @@ const ALLOWED_SERVICES = [
3928
3958
  "lowcode",
3929
3959
  "cdn",
3930
3960
  "vpc",
3961
+ "monitor",
3962
+ "postgres",
3931
3963
  ];
3932
3964
  /**
3933
3965
  * Legacy tcb small-tenant CloudRun (CloudBase Run) API family — blocked.
@@ -4049,6 +4081,9 @@ function buildCapiDocGuidance(service) {
4049
4081
  if (service === "tcb" || service === "tcbr" || service === "lowcode" || service === "scf") {
4050
4082
  return `优先查阅 CloudBase API 概览 ${CLOUDBASE_CONTROL_PLANE_DOC_URL} 与云开发依赖资源接口指引 ${CLOUDBASE_DEPENDENCY_API_DOC_URL}。`;
4051
4083
  }
4084
+ if (service === "monitor") {
4085
+ return `请优先核对云监控(腾讯云可观测平台)官方 API 文档:API 概览 https://cloud.tencent.com/document/product/649/30343 ,单 Action 详细文档在 https://cloud.tencent.com/document/api/248/ 产品线下。`;
4086
+ }
4052
4087
  return `请优先核对对应官方云 API 文档;若你的场景其实是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode="openapi"),不要继续猜测管控面 Action。`;
4053
4088
  }
4054
4089
  /** Match CAM / authorization failures from Tencent Cloud control-plane APIs. */
@@ -4154,7 +4189,7 @@ function registerCapiTools(server) {
4154
4189
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions, requireEnvId: false });
4155
4190
  server.registerTool?.("callCloudApi", {
4156
4191
  title: "调用云API",
4157
- description: `通用的云 API 调用工具,主要用于 CloudBase / 腾讯云管控面与依赖资源相关 API 调用。调用前请先确认 service、Action 与 Param,避免猜测 Action 名称。如果你的目标是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请不要优先使用 callCloudApi,而应优先查看对应 OpenAPI / Swagger。现有 OpenAPI / Swagger 能力不是通用的管控面 Action 集合;管控面 API 请优先参考 CloudBase API 概览 ${CLOUDBASE_CONTROL_PLANE_DOC_URL} 与云开发依赖资源接口指引 ${CLOUDBASE_DEPENDENCY_API_DOC_URL}。对于 tcb service,常用 Action 分类如下:
4192
+ description: `通用的云 API 调用工具,主要用于 CloudBase / 腾讯云管控面与依赖资源相关 API 调用。**调用前必读接口索引** https://docs.cloudbase.net/ai/cloudbase-ai-toolkit/api-reference.md (每日自动同步的 Action 级索引,含 rate limit;先查此索引确认 service/Action/参数,避免猜测 Action 名称;索引未覆盖的产品再去该产品官方 API 文档核对)。如果你的目标是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请不要优先使用 callCloudApi,而应优先查看对应 OpenAPI / Swagger。现有 OpenAPI / Swagger 能力不是通用的管控面 Action 集合;管控面 API 请优先参考 CloudBase API 概览 ${CLOUDBASE_CONTROL_PLANE_DOC_URL} 与云开发依赖资源接口指引 ${CLOUDBASE_DEPENDENCY_API_DOC_URL}。对于 tcb service,常用 Action 分类如下:
4158
4193
 
4159
4194
  **环境管理**: \`CreateEnv\`、\`ModifyEnv\`、\`DescribeEnvs\`、\`DestroyEnv\`
4160
4195
  **用户管理**: \`CreateUser\`、\`ModifyUser\`、\`DescribeUserList\`、\`DeleteUsers\`
@@ -4169,8 +4204,8 @@ function registerCapiTools(server) {
4169
4204
  销毁环境时,常见做法是至少带上 \`EnvId\` 和 \`BypassCheck: true\`,如果环境已经处于隔离期再按文档补 \`IsForce: true\`。`,
4170
4205
  inputSchema: {
4171
4206
  service: zod_1.z
4172
- .enum(ALLOWED_SERVICES)
4173
- .describe("选择要访问的服务。可选:tcb、tcbr、scf、sts、cam、lowcode、cdn、vpc。对于 tcb / scf / lowcode 等 CloudBase 管控面 Action,请优先查官方文档,不要直接猜测 Action。云托管统一走 tcbr(version 需传 2022-02-17)。"),
4207
+ .enum(exports.AV)
4208
+ .describe("选择要访问的服务。可选:tcb、tcbr、scf、sts、cam、lowcode、cdn、vpc、monitor(云监控/告警,version 需传 2018-07-24)、postgres(云数据库 PostgreSQL,version 需传 2017-03-12)。对于 tcb / scf / lowcode 等 CloudBase 管控面 Action,请优先查官方文档,不要直接猜测 Action。云托管统一走 tcbr(version 需传 2022-02-17)。"),
4174
4209
  action: zod_1.z
4175
4210
  .string()
4176
4211
  .min(1)
@@ -4178,7 +4213,7 @@ function registerCapiTools(server) {
4178
4213
  version: zod_1.z
4179
4214
  .string()
4180
4215
  .optional()
4181
- .describe("API 版本(可选)。缺省时按 service 使用 SDK 内置默认版本;tcbr 必须传 \"2022-02-17\"(否则请求缺少 X-TC-Version 会失败)。示例:service=\"tcbr\", version=\"2022-02-17\", action=\"CreateCloudRunEnv\", params={EnvId:\"env-xxx\",PackageType:\"Standard\"}。"),
4216
+ .describe("API 版本(可选)。缺省时按 service 使用 SDK 内置默认版本;tcbr 必须传 \"2022-02-17\"(否则请求缺少 X-TC-Version 会失败),monitor 必须传 \"2018-07-24\"、postgres 必须传 \"2017-03-12\"(这两个 service 无内置默认版本,不传会失败)。示例:service=\"tcbr\", version=\"2022-02-17\", action=\"CreateCloudRunEnv\", params={EnvId:\"env-xxx\",PackageType:\"Standard\"}。"),
4182
4217
  params: zod_1.z
4183
4218
  .record(zod_1.z.any())
4184
4219
  .optional()
@@ -4196,8 +4231,8 @@ function registerCapiTools(server) {
4196
4231
  category: CATEGORY,
4197
4232
  },
4198
4233
  }, async ({ service, action, params, version, region, }) => {
4199
- if (!ALLOWED_SERVICES.includes(service)) {
4200
- throw new Error(`Service ${service} is not allowed. Allowed services: ${ALLOWED_SERVICES.join(", ")}`);
4234
+ if (!exports.AV.includes(service)) {
4235
+ throw new Error(`Service ${service} is not allowed. Allowed services: ${exports.AV.join(", ")}`);
4201
4236
  }
4202
4237
  assertTcbCloudRunActionAllowed(service, action);
4203
4238
  const { region: resolvedRegion, params: bodyParams } = resolveCloudApiRegionAndParams({ region, params });
@@ -8937,6 +8972,7 @@ const path = __importStar(__webpack_require__(2521));
8937
8972
  const zod_1 = __webpack_require__(2971);
8938
8973
  const cloudbase_manager_js_1 = __webpack_require__(3109);
8939
8974
  const tool_result_js_1 = __webpack_require__(9645);
8975
+ const sql_risk_js_1 = __webpack_require__(9885);
8940
8976
  const CATEGORY = "PostgreSQL database";
8941
8977
  const QUERY_PG_DATABASE = "queryPgDatabase";
8942
8978
  const MANAGE_PG_DATABASE = "managePgDatabase";
@@ -9096,33 +9132,9 @@ function normalizeLimit(limit, fallback = 20, max = 200) {
9096
9132
  }
9097
9133
  return Math.max(1, Math.min(max, Math.floor(limit)));
9098
9134
  }
9099
- function stripLeadingSqlComments(sql) {
9100
- let normalized = sql.trim();
9101
- while (normalized.length > 0) {
9102
- if (normalized.startsWith("--")) {
9103
- normalized = normalized.replace(/^--.*(?:\r?\n|$)/, "").trimStart();
9104
- continue;
9105
- }
9106
- if (normalized.startsWith("#")) {
9107
- normalized = normalized.replace(/^#.*(?:\r?\n|$)/, "").trimStart();
9108
- continue;
9109
- }
9110
- if (normalized.startsWith("/*")) {
9111
- normalized = normalized.replace(/^\/\*[\s\S]*?\*\//, "").trimStart();
9112
- continue;
9113
- }
9114
- break;
9115
- }
9116
- return normalized;
9117
- }
9118
- function getSqlVerb(sql) {
9119
- const normalized = stripLeadingSqlComments(sql);
9120
- const match = normalized.match(/^([a-zA-Z]+)/);
9121
- return match ? match[1].toUpperCase() : "";
9122
- }
9123
9135
  function isReadOnlySql(sql) {
9124
- const normalized = stripLeadingSqlComments(sql);
9125
- const verb = getSqlVerb(normalized);
9136
+ const normalized = (0, sql_risk_js_1.stripLeadingSqlComments)(sql);
9137
+ const verb = (0, sql_risk_js_1.getSqlVerb)(normalized);
9126
9138
  const readOnlyVerbs = new Set([
9127
9139
  "SELECT",
9128
9140
  "SHOW",
@@ -9136,27 +9148,16 @@ function isReadOnlySql(sql) {
9136
9148
  return !/\b(INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|TRUNCATE|REPLACE|RENAME|GRANT|REVOKE|COMMENT|VACUUM|ANALYZE|FOR\s+UPDATE|FOR\s+SHARE)\b/i.test(normalized);
9137
9149
  }
9138
9150
  function buildLimitedReadOnlySql(sql, limit) {
9139
- const normalized = stripLeadingSqlComments(sql).replace(/;\s*$/, "");
9140
- const verb = getSqlVerb(normalized);
9151
+ const normalized = (0, sql_risk_js_1.stripLeadingSqlComments)(sql).replace(/;\s*$/, "");
9152
+ const verb = (0, sql_risk_js_1.getSqlVerb)(normalized);
9141
9153
  if (!["SELECT", "WITH", "VALUES"].includes(verb)) {
9142
9154
  return sql;
9143
9155
  }
9144
9156
  return `SELECT * FROM (${normalized}) AS cloudbase_mcp_readonly_limit LIMIT ${limit}`;
9145
9157
  }
9146
- function isDestructiveSql(sql) {
9147
- const normalized = stripLeadingSqlComments(sql);
9148
- const verb = getSqlVerb(normalized);
9149
- if (["DROP", "TRUNCATE", "DELETE"].includes(verb)) {
9150
- return true;
9151
- }
9152
- if (verb === "ALTER") {
9153
- return /\b(DROP|RENAME)\b/i.test(normalized);
9154
- }
9155
- return false;
9156
- }
9157
9158
  function classifySqlRisk(sql) {
9158
- const normalized = stripLeadingSqlComments(sql);
9159
- const verb = getSqlVerb(normalized);
9159
+ const normalized = (0, sql_risk_js_1.stripLeadingSqlComments)(sql);
9160
+ const verb = (0, sql_risk_js_1.getSqlVerb)(normalized);
9160
9161
  if (!verb) {
9161
9162
  return {
9162
9163
  risk: "unknown_risk",
@@ -9171,7 +9172,7 @@ function classifySqlRisk(sql) {
9171
9172
  requiresConfirm: false,
9172
9173
  };
9173
9174
  }
9174
- if (isDestructiveSql(normalized)) {
9175
+ if ((0, sql_risk_js_1.isDestructiveSql)(normalized)) {
9175
9176
  return {
9176
9177
  risk: "destructive",
9177
9178
  readOnly: false,
@@ -9217,7 +9218,7 @@ function isSchemaDdlRisk(risk, sql) {
9217
9218
  if (risk !== "destructive") {
9218
9219
  return false;
9219
9220
  }
9220
- const verb = getSqlVerb(stripLeadingSqlComments(sql));
9221
+ const verb = (0, sql_risk_js_1.getSqlVerb)((0, sql_risk_js_1.stripLeadingSqlComments)(sql));
9221
9222
  return ["DROP", "TRUNCATE", "ALTER"].includes(verb);
9222
9223
  }
9223
9224
  /**
@@ -9770,7 +9771,7 @@ function buildSchemaTable(schema, name) {
9770
9771
  return `${schema}.${name}`;
9771
9772
  }
9772
9773
  function parseTargetTableFromSql(sql, defaultSchema) {
9773
- const normalized = stripLeadingSqlComments(sql);
9774
+ const normalized = (0, sql_risk_js_1.stripLeadingSqlComments)(sql);
9774
9775
  const identifier = String.raw `((?:[A-Za-z_][A-Za-z0-9_$]*\.)?[A-Za-z_][A-Za-z0-9_$]*)`;
9775
9776
  const patterns = [
9776
9777
  new RegExp(String.raw `\bcreate\s+(?:temporary\s+|temp\s+|unlogged\s+)?table\s+(?:if\s+not\s+exists\s+)?${identifier}`, "i"),
@@ -10177,7 +10178,7 @@ function renderParameterizedSql(sql, values) {
10177
10178
  });
10178
10179
  }
10179
10180
  function inferCommand(sql) {
10180
- return getSqlVerb(sql) || undefined;
10181
+ return (0, sql_risk_js_1.getSqlVerb)(sql) || undefined;
10181
10182
  }
10182
10183
  function createManagerPgClient(context, cloudBaseOptions) {
10183
10184
  return {
@@ -10453,7 +10454,7 @@ async function handleExecuteSql(args, context, deps) {
10453
10454
  success: true,
10454
10455
  data: {
10455
10456
  classification,
10456
- command: result.command ?? getSqlVerb(args.sql),
10457
+ command: result.command ?? (0, sql_risk_js_1.getSqlVerb)(args.sql),
10457
10458
  rowCount: result.rowCount ?? null,
10458
10459
  previewRows: result.rows
10459
10460
  .slice(0, 5)
@@ -12549,6 +12550,474 @@ function registerSQLDatabaseTools(server) {
12549
12550
  }
12550
12551
 
12551
12552
 
12553
+ /***/ },
12554
+
12555
+ /***/ 3668
12556
+ (__unused_webpack_module, exports, __webpack_require__) {
12557
+
12558
+
12559
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12560
+ return (mod && mod.__esModule) ? mod : { "default": mod };
12561
+ };
12562
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
12563
+ exports.DEPLOY_ERROR_CODES = void 0;
12564
+ exports.registerDeployTools = registerDeployTools;
12565
+ const toolbox_1 = __webpack_require__(2090);
12566
+ const node_fs_1 = __importDefault(__webpack_require__(9627));
12567
+ const node_path_1 = __importDefault(__webpack_require__(5663));
12568
+ const zod_1 = __webpack_require__(2971);
12569
+ const cloudbase_manager_js_1 = __webpack_require__(3109);
12570
+ const json_content_js_1 = __webpack_require__(8027);
12571
+ const sql_risk_js_1 = __webpack_require__(9885);
12572
+ // 声明式部署可编排的资源类型,与 DeployOrchestrator 的部署顺序一致:
12573
+ // database → functions → app → hosting → gateway
12574
+ const RESOURCE_TYPES = ["database", "functions", "app", "hosting", "gateway"];
12575
+ /**
12576
+ * 声明式部署工具的稳定错误码。供 agent 程序化分支处理,不随文案调整而变化。
12577
+ * - CONFIG_NOT_FOUND:cwd 下未找到 cloudbaserc 配置文件
12578
+ * - CONFIG_INVALID:配置未通过 cloudbaserc schema 校验
12579
+ * - ENV_UNRESOLVED:无法确定目标环境 ID(配置/入参/登录态均缺失)
12580
+ * - INVALID_CONCURRENCY:并发数入参非法
12581
+ * - CONFIRM_REQUIRED:写操作未显式传 confirm=true
12582
+ * - DESTRUCTIVE_CONFIRM_REQUIRED:待执行的数据库迁移含破坏性语句,需额外传 confirmDestructive=true
12583
+ * - DEPLOY_FAILED:编排器执行失败或其它未分类错误(可能透传引擎 errorCode)
12584
+ */
12585
+ exports.DEPLOY_ERROR_CODES = {
12586
+ CONFIG_NOT_FOUND: "CONFIG_NOT_FOUND",
12587
+ CONFIG_INVALID: "CONFIG_INVALID",
12588
+ ENV_UNRESOLVED: "ENV_UNRESOLVED",
12589
+ INVALID_CONCURRENCY: "INVALID_CONCURRENCY",
12590
+ CONFIRM_REQUIRED: "CONFIRM_REQUIRED",
12591
+ DESTRUCTIVE_CONFIRM_REQUIRED: "DESTRUCTIVE_CONFIRM_REQUIRED",
12592
+ DEPLOY_FAILED: "DEPLOY_FAILED",
12593
+ };
12594
+ /**
12595
+ * 携带稳定错误码的部署错误。抛出后由 buildErrorEnvelope 归一为信封的 errorCode 字段。
12596
+ */
12597
+ class DeployError extends Error {
12598
+ code;
12599
+ constructor(code, message) {
12600
+ super(message);
12601
+ this.name = "DeployError";
12602
+ this.code = code;
12603
+ }
12604
+ }
12605
+ function buildEnvelope(data, message) {
12606
+ return { success: true, data, message };
12607
+ }
12608
+ function buildErrorEnvelope(error) {
12609
+ // DeployError 带显式错误码;引擎/SDK 抛出的错误若带 code 也透传;其余归为 DEPLOY_FAILED。
12610
+ let errorCode = exports.DEPLOY_ERROR_CODES.DEPLOY_FAILED;
12611
+ if (error instanceof DeployError) {
12612
+ errorCode = error.code;
12613
+ }
12614
+ else if (error &&
12615
+ typeof error === "object" &&
12616
+ "code" in error &&
12617
+ typeof error.code === "string" &&
12618
+ error.code.length > 0) {
12619
+ errorCode = error.code;
12620
+ }
12621
+ return {
12622
+ success: false,
12623
+ data: {},
12624
+ message: error instanceof Error ? error.message : String(error),
12625
+ errorCode,
12626
+ };
12627
+ }
12628
+ /**
12629
+ * 解析 cloudbaserc 声明式部署配置。
12630
+ *
12631
+ * 分两步完成:
12632
+ * 1. 定位并读取配置文件:searchConfig 从 cwd 探测 cloudbaserc.*(json/yaml/yml/js),
12633
+ * loadConfig 读取原始内容。读取阶段不强制 envId,
12634
+ * envId 由调用方按优先级单独确定,以支持用登录态环境部署未写死 envId 的配置。
12635
+ * 2. ConfigParser.parseRawConfig:按 mode 合并 envOverrides、下发 functionDefaultConfig
12636
+ * 到各函数、加载 .env / .env.local / .env.<mode> 并渲染 {{env.*}} / {{tcb.*}} 模板变量。
12637
+ */
12638
+ async function resolveDeployConfig(options) {
12639
+ const found = await (0, toolbox_1.searchConfig)(options.cwd);
12640
+ if (!found?.filepath) {
12641
+ throw new DeployError(exports.DEPLOY_ERROR_CODES.CONFIG_NOT_FOUND, `未在 ${options.cwd} 找到 cloudbaserc 配置文件(支持 json/yaml/yml/js)`);
12642
+ }
12643
+ const rawConfig = await (0, toolbox_1.loadConfig)({ configPath: found.filepath });
12644
+ return toolbox_1.ConfigParser.parseRawConfig(rawConfig ?? {}, options.cwd, {
12645
+ mode: options.mode,
12646
+ });
12647
+ }
12648
+ /**
12649
+ * 确定部署使用的环境 ID。
12650
+ *
12651
+ * 优先级:显式传入的 envId > 解析后配置中的 envId > MCP 登录态 / 绑定环境。
12652
+ * 显式入参优先于配置文件,配置未写死 envId 时回退到当前会话绑定的环境。
12653
+ * 三者都无则抛出错误。
12654
+ */
12655
+ async function resolveDeployEnvId(options) {
12656
+ if (options.envId && options.envId.length > 0) {
12657
+ return options.envId;
12658
+ }
12659
+ const configEnvId = options.config.envId;
12660
+ if (typeof configEnvId === "string" && configEnvId.length > 0) {
12661
+ return configEnvId;
12662
+ }
12663
+ const resolved = await (0, cloudbase_manager_js_1.getEnvId)(options.cloudBaseOptions);
12664
+ if (!resolved) {
12665
+ throw new DeployError(exports.DEPLOY_ERROR_CODES.ENV_UNRESOLVED, "未能确定部署环境 ID:请在 cloudbaserc 配置 envId、通过 envId 参数指定,或先登录并绑定环境。");
12666
+ }
12667
+ return resolved;
12668
+ }
12669
+ /**
12670
+ * 按 cloudbaserc schema 校验解析后的配置。
12671
+ *
12672
+ * 校验不通过时抛出错误,错误消息包含各字段的具体校验失败原因,供调用方定位问题。
12673
+ */
12674
+ function assertConfigValid(config) {
12675
+ const result = (0, toolbox_1.validateCloudBaseConfigBySchema)(config);
12676
+ if (result.valid) {
12677
+ return;
12678
+ }
12679
+ const detail = result.errors
12680
+ .map((item) => `${item.dataPath || "<root>"} ${item.message}`)
12681
+ .join("; ");
12682
+ throw new DeployError(exports.DEPLOY_ERROR_CODES.CONFIG_INVALID, `cloudbaserc 配置校验未通过:${detail}`);
12683
+ }
12684
+ /**
12685
+ * 校验并发数取值,非法时抛出错误。
12686
+ *
12687
+ * 未指定时返回 undefined(由编排器使用默认串行);指定时必须为不小于 1 的整数。
12688
+ */
12689
+ function normalizeConcurrency(concurrency) {
12690
+ if (concurrency === undefined) {
12691
+ return undefined;
12692
+ }
12693
+ if (!Number.isInteger(concurrency) || concurrency < 1) {
12694
+ throw new DeployError(exports.DEPLOY_ERROR_CODES.INVALID_CONCURRENCY, `无效的并发数 ${concurrency},应为不小于 1 的整数`);
12695
+ }
12696
+ return concurrency;
12697
+ }
12698
+ /**
12699
+ * 让 deployPlan 的预演结论与 deployApply 的实际执行语义对齐。
12700
+ *
12701
+ * 背景:编排器把「云端已存在的函数」标为 status='update',但 deployApply 在 yes!==true
12702
+ * 且无交互确认回调时,会对 functions+update 保守跳过(reason='no-confirm')。
12703
+ * 若 plan 原样返回 update,agent 预演看到「会更新」,实际执行却「跳过」——结论相反。
12704
+ *
12705
+ * 此函数按传入的 yes 复算 functions+update 的「有效动作」:
12706
+ * - yes===true:维持 update(执行时确实会覆盖)
12707
+ * - 否则:改标为 skip,并保留原始判定到 declaredStatus,action 说明为何跳过。
12708
+ * 其它资源类型与状态原样透传(database/app/hosting/gateway 的分类不受 yes 影响)。
12709
+ */
12710
+ function reconcilePlanWithExecution(plan, yes) {
12711
+ if (!Array.isArray(plan)) {
12712
+ return [];
12713
+ }
12714
+ return plan.map((item) => {
12715
+ if (!item || typeof item !== "object") {
12716
+ return item;
12717
+ }
12718
+ if (item.type === "functions" && item.status === "update" && !yes) {
12719
+ return {
12720
+ ...item,
12721
+ status: "skip",
12722
+ declaredStatus: "update",
12723
+ action: `已存在函数 ${item.name}:未传 yes=true,执行时将保守跳过(不覆盖)。传 yes=true 才会覆盖更新。`,
12724
+ };
12725
+ }
12726
+ return item;
12727
+ });
12728
+ }
12729
+ /**
12730
+ * 解析 migrations 目录(与引擎 DatabaseDeployer.resolveMigrationsDir 同规则):
12731
+ * 未配置默认 ./cloudbase/migrations;仅允许相对路径(禁止绝对路径 / 开头)。
12732
+ * 非法时返回 undefined(交由引擎在真正执行时抛出一致的错误,这里不抢报错)。
12733
+ */
12734
+ function resolveMigrationsDir(cwd, migrations) {
12735
+ const raw = typeof migrations === "string" && migrations ? migrations : "./cloudbase/migrations";
12736
+ if (node_path_1.default.isAbsolute(raw) || raw.startsWith("/")) {
12737
+ return undefined;
12738
+ }
12739
+ return node_path_1.default.resolve(cwd, raw);
12740
+ }
12741
+ /**
12742
+ * 从 dry-run plan 中提取「待执行(pending)的数据库迁移标识」。
12743
+ *
12744
+ * 编排器把 database 计划聚合为单条 create 项,pending 迁移写在
12745
+ * changes[].to(field==='migration'),形如 "{version}_{name}"。
12746
+ * 只关注 pending:已 applied 的迁移不会被重复执行,不应触发破坏性确认门。
12747
+ */
12748
+ function extractPendingMigrations(plan) {
12749
+ const result = [];
12750
+ for (const item of plan) {
12751
+ if (!item || item.type !== "database" || item.status !== "create") {
12752
+ continue;
12753
+ }
12754
+ const changes = item.changes;
12755
+ if (!Array.isArray(changes)) {
12756
+ continue;
12757
+ }
12758
+ for (const change of changes) {
12759
+ if (change?.field === "migration" && typeof change.to === "string" && change.to) {
12760
+ result.push(change.to);
12761
+ }
12762
+ }
12763
+ }
12764
+ return result;
12765
+ }
12766
+ /**
12767
+ * 检测「本次将要执行的数据库迁移」中是否含破坏性语句(DROP/TRUNCATE/DELETE、
12768
+ * ALTER ... DROP/RENAME)。
12769
+ *
12770
+ * 仅扫描 pending 迁移对应的本地 .sql 文件;已 applied 的不扫描,避免误拦。
12771
+ * 读文件失败(缺文件/目录非法)视为无法确认破坏性,返回空——真正的存在性/
12772
+ * 格式校验交由引擎执行时处理,这里只在能确证破坏性时加门,避免与引擎报错重复。
12773
+ */
12774
+ function detectDestructiveMigrations(options) {
12775
+ const pending = extractPendingMigrations(options.plan);
12776
+ if (pending.length === 0) {
12777
+ return [];
12778
+ }
12779
+ const dbConfig = options.databaseConfig && typeof options.databaseConfig === "object"
12780
+ ? options.databaseConfig
12781
+ : undefined;
12782
+ const migrationsDir = resolveMigrationsDir(options.cwd, dbConfig?.migrations);
12783
+ if (!migrationsDir) {
12784
+ return [];
12785
+ }
12786
+ const hits = [];
12787
+ for (const migration of pending) {
12788
+ const file = node_path_1.default.join(migrationsDir, `${migration}.sql`);
12789
+ let sqlText;
12790
+ try {
12791
+ sqlText = node_fs_1.default.readFileSync(file, "utf-8");
12792
+ }
12793
+ catch {
12794
+ // 文件读不到:不在此处报错,交给引擎执行时给出一致错误
12795
+ continue;
12796
+ }
12797
+ const statements = (0, sql_risk_js_1.findDestructiveStatements)(sqlText);
12798
+ if (statements.length > 0) {
12799
+ hits.push({ migration, file, statements });
12800
+ }
12801
+ }
12802
+ return hits;
12803
+ }
12804
+ function registerDeployTools(server) {
12805
+ const cloudBaseOptions = server.cloudBaseOptions;
12806
+ const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
12807
+ // 工具一:deployPlan —— 只读,预演部署计划(dry-run)
12808
+ server.registerTool?.("deployPlan", {
12809
+ title: "预演 CloudBase 声明式部署计划",
12810
+ description: "解析 cloudbaserc 并计算声明式部署计划(dry-run,不产生任何变更)。" +
12811
+ "这是 deployApply 的预演对仗工具:plan 计算、deployApply 执行同一份 cloudbaserc。" +
12812
+ "返回每个资源的动作分类:create=新建,update=覆盖更新,skip=无变更/不会执行," +
12813
+ "conflict=检测到冲突需中断,deploy=直传覆盖。" +
12814
+ "计划已按 yes 复算为「实际会发生的动作」:不传 yes=true 时,云端已存在的函数会标为 skip" +
12815
+ "(并在 declaredStatus 保留 update),与 deployApply 的实际执行结果一致,避免预演与执行相反。" +
12816
+ "\n适用边界:本工具用于项目级声明式编排(一份 cloudbaserc 统一 plan/apply);" +
12817
+ "单资源临时直传请用 manageFunctions/manageHosting/manageApps。" +
12818
+ "\n- cwd:项目根目录,默认当前工作目录" +
12819
+ "\n- mode:环境名,命中 envOverrides.<mode> 时合并对应的多环境覆盖配置" +
12820
+ "\n- envId:目标环境 ID,优先级高于 cloudbaserc 中的 envId;不传则用配置值或当前绑定环境" +
12821
+ "\n- only:仅计算指定资源类型的计划" +
12822
+ "\n- skip:跳过指定资源类型" +
12823
+ "\n- yes:与 deployApply 的 yes 对齐,用于复算已存在函数的有效动作。" +
12824
+ "true=预演为覆盖更新(update);false(默认)=预演为保守跳过(skip)",
12825
+ inputSchema: {
12826
+ cwd: zod_1.z
12827
+ .string()
12828
+ .optional()
12829
+ .describe("项目根目录,从此目录向下搜索 cloudbaserc;默认当前工作目录"),
12830
+ mode: zod_1.z
12831
+ .string()
12832
+ .optional()
12833
+ .describe("环境名(如 production/staging),命中 envOverrides.<mode> 时合并覆盖"),
12834
+ envId: zod_1.z
12835
+ .string()
12836
+ .optional()
12837
+ .describe("目标环境 ID,优先级高于 cloudbaserc 中的 envId;不传则用配置值或当前绑定环境"),
12838
+ only: zod_1.z
12839
+ .array(zod_1.z.enum(RESOURCE_TYPES))
12840
+ .optional()
12841
+ .describe("仅计算指定资源类型的计划,可选值:database/functions/app/hosting/gateway"),
12842
+ skip: zod_1.z
12843
+ .array(zod_1.z.enum(RESOURCE_TYPES))
12844
+ .optional()
12845
+ .describe("跳过指定资源类型,可选值:database/functions/app/hosting/gateway"),
12846
+ yes: zod_1.z
12847
+ .boolean()
12848
+ .optional()
12849
+ .describe("与 deployApply 的 yes 对齐,用于复算已存在函数的有效动作。" +
12850
+ "true=预演为覆盖更新;false(默认)=预演为保守跳过"),
12851
+ },
12852
+ annotations: {
12853
+ readOnlyHint: true,
12854
+ openWorldHint: true,
12855
+ category: "deploy",
12856
+ },
12857
+ }, async ({ cwd, mode, envId: envIdInput, only, skip, yes, }) => {
12858
+ try {
12859
+ const projectRoot = cwd ?? process.cwd();
12860
+ const config = await resolveDeployConfig({ cwd: projectRoot, mode });
12861
+ assertConfigValid(config);
12862
+ const envId = await resolveDeployEnvId({
12863
+ envId: envIdInput,
12864
+ config,
12865
+ cloudBaseOptions,
12866
+ });
12867
+ const manager = await getManager();
12868
+ const rawPlan = await manager.getDeployOrchestrator().deployPlan({
12869
+ config,
12870
+ envId,
12871
+ cwd: projectRoot,
12872
+ only,
12873
+ skip,
12874
+ dryRun: true,
12875
+ });
12876
+ // 按 yes 复算为「实际会发生的动作」,使预演与 deployApply 的执行结果一致
12877
+ const plan = reconcilePlanWithExecution(rawPlan, yes === true);
12878
+ return (0, json_content_js_1.jsonContent)(buildEnvelope({ cwd: projectRoot, mode: mode ?? null, envId, yes: yes === true, plan }, "已生成声明式部署计划"));
12879
+ }
12880
+ catch (error) {
12881
+ return (0, json_content_js_1.jsonContent)(buildErrorEnvelope(error));
12882
+ }
12883
+ });
12884
+ // 工具二:deployApply —— 写操作,执行声明式部署(本地形态的 apply,与 deployPlan 对仗)
12885
+ server.registerTool?.("deployApply", {
12886
+ title: "执行 CloudBase 声明式部署(本地 apply)",
12887
+ description: "解析 cloudbaserc 并按 database→functions→app→hosting→gateway 顺序执行声明式部署。" +
12888
+ "这是 deployPlan 的执行对仗工具(plan 预演 / deployApply 执行同一份 cloudbaserc)," +
12889
+ "属于本地形态的 apply(读本地 cloudbaserc 并在本地构建上传)," +
12890
+ "是会变更云端资源的写操作,必须显式传 confirm=true 才会执行。" +
12891
+ "建议先用 deployPlan 预演,确认计划无误后再执行。" +
12892
+ "\n适用边界:本工具用于项目级声明式编排(一份 cloudbaserc 统一 plan/apply);" +
12893
+ "单资源临时直传请用 manageFunctions/manageHosting/manageApps。" +
12894
+ "\n- confirm:必须显式传 true 才执行部署,否则直接拒绝" +
12895
+ "\n- confirmDestructive:当本次待执行的数据库迁移含破坏性语句(DROP/TRUNCATE/DELETE、" +
12896
+ "ALTER…DROP/RENAME)时,除 confirm 外还必须显式传 confirmDestructive=true 才会执行;" +
12897
+ "否则拒绝并列出命中的迁移与语句。无破坏性迁移时该参数不生效" +
12898
+ "\n- cwd:项目根目录,默认当前工作目录" +
12899
+ "\n- mode:环境名,命中 envOverrides.<mode> 时合并对应的多环境覆盖配置" +
12900
+ "\n- envId:目标环境 ID,优先级高于 cloudbaserc 中的 envId;不传则用配置值或当前绑定环境" +
12901
+ "\n- only:仅部署指定资源类型" +
12902
+ "\n- skip:跳过指定资源类型" +
12903
+ "\n- yes:遇到已存在资源时的处理方式。true=直接覆盖更新;false(默认)=保守跳过," +
12904
+ "在无法交互确认的场景下已存在资源不会被覆盖(与 deployPlan 的 yes 语义一致)" +
12905
+ "\n- concurrency:同类型资源最大并行数,默认 1(串行)" +
12906
+ "\n- continueOnError:某个资源失败后继续部署其余资源(database 失败仍强制中断)",
12907
+ inputSchema: {
12908
+ confirm: zod_1.z
12909
+ .boolean()
12910
+ .optional()
12911
+ .describe("危险操作确认开关。部署会变更云端资源,必须显式传 confirm=true 才会执行"),
12912
+ confirmDestructive: zod_1.z
12913
+ .boolean()
12914
+ .optional()
12915
+ .describe("破坏性数据库变更确认开关。当待执行迁移含 DROP/TRUNCATE/DELETE 或 ALTER…DROP/RENAME 时," +
12916
+ "必须在 confirm=true 之外额外显式传 confirmDestructive=true;无破坏性迁移时不生效"),
12917
+ cwd: zod_1.z
12918
+ .string()
12919
+ .optional()
12920
+ .describe("项目根目录,从此目录向下搜索 cloudbaserc;默认当前工作目录"),
12921
+ mode: zod_1.z
12922
+ .string()
12923
+ .optional()
12924
+ .describe("环境名(如 production/staging),命中 envOverrides.<mode> 时合并覆盖"),
12925
+ envId: zod_1.z
12926
+ .string()
12927
+ .optional()
12928
+ .describe("目标环境 ID,优先级高于 cloudbaserc 中的 envId;不传则用配置值或当前绑定环境"),
12929
+ only: zod_1.z
12930
+ .array(zod_1.z.enum(RESOURCE_TYPES))
12931
+ .optional()
12932
+ .describe("仅部署指定资源类型,可选值:database/functions/app/hosting/gateway"),
12933
+ skip: zod_1.z
12934
+ .array(zod_1.z.enum(RESOURCE_TYPES))
12935
+ .optional()
12936
+ .describe("跳过指定资源类型,可选值:database/functions/app/hosting/gateway"),
12937
+ yes: zod_1.z
12938
+ .boolean()
12939
+ .optional()
12940
+ .describe("遇到已存在资源时是否直接覆盖更新。true=覆盖;false(默认)=保守跳过已存在资源"),
12941
+ concurrency: zod_1.z
12942
+ .number()
12943
+ .int()
12944
+ .min(1)
12945
+ .optional()
12946
+ .describe("同类型资源最大并行数,默认 1(串行);仅作用于同类型资源,跨类型依赖顺序不变"),
12947
+ continueOnError: zod_1.z
12948
+ .boolean()
12949
+ .optional()
12950
+ .describe("某个资源失败后是否继续部署其余资源;database 失败始终强制中断"),
12951
+ },
12952
+ annotations: {
12953
+ readOnlyHint: false,
12954
+ destructiveHint: true,
12955
+ openWorldHint: true,
12956
+ category: "deploy",
12957
+ },
12958
+ }, async ({ confirm, confirmDestructive, cwd, mode, envId: envIdInput, only, skip, yes, concurrency, continueOnError, }) => {
12959
+ try {
12960
+ if (confirm !== true) {
12961
+ throw new DeployError(exports.DEPLOY_ERROR_CODES.CONFIRM_REQUIRED, "部署会变更云端资源,必须显式传 confirm=true 才会执行。" +
12962
+ "建议先用 deployPlan 预演部署计划,确认无误后再执行。");
12963
+ }
12964
+ const projectRoot = cwd ?? process.cwd();
12965
+ const config = await resolveDeployConfig({ cwd: projectRoot, mode });
12966
+ assertConfigValid(config);
12967
+ const envId = await resolveDeployEnvId({
12968
+ envId: envIdInput,
12969
+ config,
12970
+ cloudBaseOptions,
12971
+ });
12972
+ const manager = await getManager();
12973
+ const orchestrator = manager.getDeployOrchestrator();
12974
+ // 破坏性数据库变更门:仅当 database 参与本次部署时,先 dry-run 取 pending 迁移,
12975
+ // 检测破坏性语句;命中且未额外确认则拒绝,独立于总 confirm 之外多一道门。
12976
+ const databaseParticipates = !!config.database &&
12977
+ (!only || only.includes("database")) &&
12978
+ !(skip ?? []).includes("database");
12979
+ if (databaseParticipates && confirmDestructive !== true) {
12980
+ const dryRunPlan = await orchestrator.deployPlan({
12981
+ config,
12982
+ envId,
12983
+ cwd: projectRoot,
12984
+ only,
12985
+ skip,
12986
+ dryRun: true,
12987
+ });
12988
+ const destructive = detectDestructiveMigrations({
12989
+ plan: Array.isArray(dryRunPlan) ? dryRunPlan : [],
12990
+ cwd: projectRoot,
12991
+ databaseConfig: config.database,
12992
+ });
12993
+ if (destructive.length > 0) {
12994
+ throw new DeployError(exports.DEPLOY_ERROR_CODES.DESTRUCTIVE_CONFIRM_REQUIRED, "本次待执行的数据库迁移包含破坏性语句(DROP/TRUNCATE/DELETE 或 ALTER…DROP/RENAME)," +
12995
+ "可能造成数据/结构不可逆丢失。请先备份或复核,确认后额外传 confirmDestructive=true 再执行。" +
12996
+ "命中迁移:" +
12997
+ destructive
12998
+ .map((d) => `${d.migration}(${d.statements.length} 条破坏性语句)`)
12999
+ .join(";"));
13000
+ }
13001
+ }
13002
+ const result = await orchestrator.deploy({
13003
+ config,
13004
+ envId,
13005
+ cwd: projectRoot,
13006
+ only,
13007
+ skip,
13008
+ yes: yes === true,
13009
+ concurrency: normalizeConcurrency(concurrency),
13010
+ continueOnError,
13011
+ });
13012
+ return (0, json_content_js_1.jsonContent)(buildEnvelope({ cwd: projectRoot, mode: mode ?? null, envId, result }, "声明式部署已执行"));
13013
+ }
13014
+ catch (error) {
13015
+ return (0, json_content_js_1.jsonContent)(buildErrorEnvelope(error));
13016
+ }
13017
+ });
13018
+ }
13019
+
13020
+
12552
13021
  /***/ },
12553
13022
 
12554
13023
  /***/ 7528
@@ -14652,7 +15121,7 @@ function registerEnvTools(server) {
14652
15121
  oauthCustom: zod_1.z
14653
15122
  .boolean()
14654
15123
  .optional()
14655
- .describe("高级可选:自定义 endpoint 返回格式开关。未配置 endpoint 时默认 false;配置 endpoint 后默认 true,且不能设为 false"),
15124
+ .describe("高级可选:自定义 endpoint 返回格式开关。未配置 endpoint 时默认 false;配置 endpoint 后默认 true。标准 {code,result} 包装格式的端点(如国际站 tcb-api.tencentcloud.com)应显式传 false"),
14656
15125
  }
14657
15126
  : {}),
14658
15127
  envId: zod_1.z
@@ -14897,19 +15366,15 @@ function registerEnvTools(server) {
14897
15366
  }
14898
15367
  };
14899
15368
  try {
14900
- // 启动 Device Flow,全流程由 toolbox 负责轮询和写入 credential,这里不等待完成
15369
+ // 启动 Device Flow,全流程由 toolbox 负责轮询和写入 credential,这里不等待完成。
15370
+ // 登录参数(含 TCB_SITE=intl 端点覆写与授权页改写)与 ensureLogin 共享同一 helper,
15371
+ // 避免此直连路径绕过国际站改写导致国际站账号拿到的仍是国内站链接
14901
15372
  auth
14902
15373
  .loginByWebAuth({
14903
- flow: "device",
14904
- ...(resolvedAuthOptions.clientId
14905
- ? { client_id: resolvedAuthOptions.clientId }
14906
- : {}),
14907
- ...(resolvedAuthOptions.oauthEndpoint
14908
- ? { getOAuthEndpoint: () => resolvedAuthOptions.oauthEndpoint }
14909
- : {}),
14910
- ...(resolvedAuthOptions.oauthCustom
14911
- ? { custom: true }
14912
- : {}),
15374
+ ...(0, auth_js_1.buildDeviceLoginOptions)(resolvedAuthOptions, {
15375
+ region,
15376
+ site: server.cloudBaseOptions?.site,
15377
+ }),
14913
15378
  onDeviceCode: deviceOnCode,
14914
15379
  })
14915
15380
  .then(async () => {
@@ -24802,7 +25267,7 @@ function registerSetupTools(server) {
24802
25267
  server.registerTool("downloadTemplate", {
24803
25268
  title: "下载项目模板",
24804
25269
  description: `自动下载并部署CloudBase项目模板。
24805
- **Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.33.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
25270
+ **Note**: Call this tool when the user requests to create a new project using a CloudBase template.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.33.2" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
24806
25271
  inputSchema: {
24807
25272
  template: zod_1.z
24808
25273
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -25778,6 +26243,7 @@ function registerPGStorageTools(server) {
25778
26243
 
25779
26244
 
25780
26245
  Object.defineProperty(exports, "__esModule", ({ value: true }));
26246
+ exports.CLOUD_INCOMPATIBLE_ACTION_LEVEL_EXCEPTIONS = exports.CLOUD_INCOMPATIBLE_TOOLS = void 0;
25781
26247
  exports.isCloudMode = isCloudMode;
25782
26248
  exports.enableCloudMode = enableCloudMode;
25783
26249
  exports.getCloudModeStatus = getCloudModeStatus;
@@ -25823,6 +26289,65 @@ function getCloudModeStatus() {
25823
26289
  }
25824
26290
  return { enabled: false, source: null };
25825
26291
  }
26292
+ /**
26293
+ * Tools that are incompatible with cloud mode because they read from or write to
26294
+ * the local filesystem (uploads, downloads, config files, or building an upload
26295
+ * artifact from `process.cwd()`). In cloud mode these top-level tools are not
26296
+ * registered at all; where a tool has both cloud-safe and local-only actions,
26297
+ * gating happens at the action level inside the tool instead (see
26298
+ * `ensureActionAllowedInCloudMode` in functions.ts / hosting.ts).
26299
+ *
26300
+ * IMPORTANT: keep this list in sync with the tools actually registered. Entries
26301
+ * that no longer match a registered tool name are dead (they silently gate
26302
+ * nothing). `CLOUD_INCOMPATIBLE_TOOLS.test.ts` asserts every entry here maps to
26303
+ * a known tool or a documented action-level exception to prevent silent rot.
26304
+ */
26305
+ exports.CLOUD_INCOMPATIBLE_TOOLS = [
26306
+ // Auth tools - local file uploads
26307
+ 'auth',
26308
+ // Storage tools - local file uploads / downloads.
26309
+ // NOTE: `uploadFile` is not a top-level tool name (storage upload is exposed
26310
+ // via `manageStorage`); kept here as a documented action-level exception.
26311
+ 'uploadFile',
26312
+ 'manageStorage',
26313
+ // Function tools - local code uploads.
26314
+ // NOTE: createFunction / updateFunctionCode are now actions of `manageFunctions`,
26315
+ // not top-level tools, so gating them here is a no-op kept for documentation.
26316
+ // The real cloud-mode guard for those actions lives in
26317
+ // functions.ts#ensureActionAllowedInCloudMode. Left here intentionally so the
26318
+ // sync test flags them as "action-level exceptions" rather than removing the
26319
+ // record of why they are not top-level tools.
26320
+ 'updateFunctionCode',
26321
+ 'createFunction',
26322
+ // Declarative deploy tools - read local cloudbaserc and build the upload
26323
+ // artifact from cwd (process.cwd() when no cwd is passed). Same class as the
26324
+ // function/upload tools above: they have no meaning without a local project.
26325
+ 'deployApply',
26326
+ 'deployPlan',
26327
+ // Download tools - local file downloads
26328
+ 'downloadTemplate',
26329
+ // Setup tools - local config file operations.
26330
+ // NOTE: `setupEnvironmentId` is not a top-level tool name; kept as a documented
26331
+ // action-level exception (env binding is handled via `auth`/setup flows).
26332
+ 'setupEnvironmentId',
26333
+ // CloudRun tools - local file operations
26334
+ 'manageCloudRun',
26335
+ // manageApps is intentionally NOT listed: deployApp with localPath/filePath is
26336
+ // blocked per-action in apps.ts (CLOUD_MODE_UNSUPPORTED_ACTION) so cloud mode
26337
+ // can still use getUploadUrl + cosTimestamp, deleteApp, and deleteAppVersion.
26338
+ ];
26339
+ /**
26340
+ * Tool names that appear in CLOUD_INCOMPATIBLE_TOOLS for documentation but are no
26341
+ * longer registered as top-level tools (their cloud gating is enforced at the
26342
+ * action level instead). The sync test treats these as expected, everything else
26343
+ * must resolve to a registered tool.
26344
+ */
26345
+ exports.CLOUD_INCOMPATIBLE_ACTION_LEVEL_EXCEPTIONS = [
26346
+ 'createFunction',
26347
+ 'updateFunctionCode',
26348
+ 'uploadFile',
26349
+ 'setupEnvironmentId',
26350
+ ];
25826
26351
  /**
25827
26352
  * Check if a tool should be registered in cloud mode
25828
26353
  * @param toolName - The name of the tool
@@ -25833,29 +26358,7 @@ function shouldRegisterTool(toolName) {
25833
26358
  if (!isCloudMode()) {
25834
26359
  return true;
25835
26360
  }
25836
- // Cloud-incompatible tools that involve local file operations
25837
- const cloudIncompatibleTools = [
25838
- // Auth tools - local file uploads
25839
- 'auth',
25840
- // Storage tools - local file uploads
25841
- 'uploadFile',
25842
- // Hosting tools - action-level cloud gating is handled inside manageHosting
25843
- // Function tools - local code uploads
25844
- 'updateFunctionCode',
25845
- 'createFunction', // also involves local files
25846
- // Download tools - local file downloads
25847
- 'downloadTemplate',
25848
- // Setup tools - local config file operations
25849
- 'setupEnvironmentId',
25850
- // CloudRun tools - local file operations
25851
- 'manageCloudRun',
25852
- // Download tools - local file downloads
25853
- 'manageStorage',
25854
- // manageApps is intentionally NOT listed: deployApp with localPath/filePath is
25855
- // blocked per-action in apps.ts (CLOUD_MODE_UNSUPPORTED_ACTION) so cloud mode
25856
- // can still use getUploadUrl + cosTimestamp, deleteApp, and deleteAppVersion.
25857
- ];
25858
- return !cloudIncompatibleTools.includes(toolName);
26361
+ return !exports.CLOUD_INCOMPATIBLE_TOOLS.includes(toolName);
25859
26362
  }
25860
26363
 
25861
26364
 
@@ -27013,6 +27516,163 @@ function resolveSiteAndRegion(opts = {}) {
27013
27516
  }
27014
27517
 
27015
27518
 
27519
+ /***/ },
27520
+
27521
+ /***/ 9885
27522
+ (__unused_webpack_module, exports) {
27523
+
27524
+ var __webpack_unused_export__;
27525
+
27526
+ /**
27527
+ * Shared SQL risk heuristics.
27528
+ *
27529
+ * Extracted from databasePG.ts so both the interactive PG execute path and the
27530
+ * declarative deploy path classify destructive SQL with the *same* rules,
27531
+ * avoiding drift between two independent detectors.
27532
+ *
27533
+ * These are conservative lexical heuristics, not a full SQL parser: they strip
27534
+ * leading comments, read the leading verb, and match destructive keywords. They
27535
+ * are meant to decide whether an *extra* human confirmation is warranted, not to
27536
+ * be a security boundary.
27537
+ */
27538
+ __webpack_unused_export__ = ({ value: true });
27539
+ exports.stripLeadingSqlComments = stripLeadingSqlComments;
27540
+ exports.getSqlVerb = getSqlVerb;
27541
+ exports.isDestructiveSql = isDestructiveSql;
27542
+ __webpack_unused_export__ = splitSqlStatements;
27543
+ exports.findDestructiveStatements = findDestructiveStatements;
27544
+ /** Strip leading line (`--`, `#`) and block (`/* *​/`) comments and whitespace. */
27545
+ function stripLeadingSqlComments(sql) {
27546
+ let normalized = sql.trim();
27547
+ while (normalized.length > 0) {
27548
+ if (normalized.startsWith("--")) {
27549
+ normalized = normalized.replace(/^--.*(?:\r?\n|$)/, "").trimStart();
27550
+ continue;
27551
+ }
27552
+ if (normalized.startsWith("#")) {
27553
+ normalized = normalized.replace(/^#.*(?:\r?\n|$)/, "").trimStart();
27554
+ continue;
27555
+ }
27556
+ if (normalized.startsWith("/*")) {
27557
+ normalized = normalized.replace(/^\/\*[\s\S]*?\*\//, "").trimStart();
27558
+ continue;
27559
+ }
27560
+ break;
27561
+ }
27562
+ return normalized;
27563
+ }
27564
+ /** Return the leading SQL verb (uppercased), ignoring leading comments. */
27565
+ function getSqlVerb(sql) {
27566
+ const normalized = stripLeadingSqlComments(sql);
27567
+ const match = normalized.match(/^([a-zA-Z]+)/);
27568
+ return match ? match[1].toUpperCase() : "";
27569
+ }
27570
+ /**
27571
+ * Whether a *single* SQL statement is destructive (drops/truncates data or
27572
+ * removes/renames schema objects).
27573
+ */
27574
+ function isDestructiveSql(sql) {
27575
+ const normalized = stripLeadingSqlComments(sql);
27576
+ const verb = getSqlVerb(normalized);
27577
+ if (["DROP", "TRUNCATE", "DELETE"].includes(verb)) {
27578
+ return true;
27579
+ }
27580
+ if (verb === "ALTER") {
27581
+ return /\b(DROP|RENAME)\b/i.test(normalized);
27582
+ }
27583
+ return false;
27584
+ }
27585
+ /**
27586
+ * Split a possibly multi-statement SQL script into individual statements.
27587
+ *
27588
+ * Naive split on `;` at statement boundaries, but aware of single/double quotes,
27589
+ * line/block comments, and PostgreSQL dollar-quoted strings ($$ ... $$ or
27590
+ * $tag$ ... $tag$) so semicolons inside string bodies / function definitions do
27591
+ * not split a statement. Good enough for the conservative destructive scan; not
27592
+ * a full parser.
27593
+ */
27594
+ function splitSqlStatements(sqlText) {
27595
+ const statements = [];
27596
+ let current = "";
27597
+ let i = 0;
27598
+ const n = sqlText.length;
27599
+ while (i < n) {
27600
+ const ch = sqlText[i];
27601
+ const next = sqlText[i + 1];
27602
+ // line comment -- ... (to end of line)
27603
+ if (ch === "-" && next === "-") {
27604
+ const end = sqlText.indexOf("\n", i);
27605
+ const stop = end === -1 ? n : end + 1;
27606
+ current += sqlText.slice(i, stop);
27607
+ i = stop;
27608
+ continue;
27609
+ }
27610
+ // block comment /* ... */
27611
+ if (ch === "/" && next === "*") {
27612
+ const end = sqlText.indexOf("*/", i + 2);
27613
+ const stop = end === -1 ? n : end + 2;
27614
+ current += sqlText.slice(i, stop);
27615
+ i = stop;
27616
+ continue;
27617
+ }
27618
+ // single- or double-quoted string
27619
+ if (ch === "'" || ch === '"') {
27620
+ const quote = ch;
27621
+ current += ch;
27622
+ i += 1;
27623
+ while (i < n) {
27624
+ current += sqlText[i];
27625
+ // handle escaped quote by doubling ('' or "")
27626
+ if (sqlText[i] === quote) {
27627
+ if (sqlText[i + 1] === quote) {
27628
+ current += sqlText[i + 1];
27629
+ i += 2;
27630
+ continue;
27631
+ }
27632
+ i += 1;
27633
+ break;
27634
+ }
27635
+ i += 1;
27636
+ }
27637
+ continue;
27638
+ }
27639
+ // dollar-quoted string: $$ ... $$ or $tag$ ... $tag$
27640
+ if (ch === "$") {
27641
+ const tagMatch = sqlText.slice(i).match(/^\$([a-zA-Z_][a-zA-Z0-9_]*)?\$/);
27642
+ if (tagMatch) {
27643
+ const tag = tagMatch[0];
27644
+ const end = sqlText.indexOf(tag, i + tag.length);
27645
+ const stop = end === -1 ? n : end + tag.length;
27646
+ current += sqlText.slice(i, stop);
27647
+ i = stop;
27648
+ continue;
27649
+ }
27650
+ }
27651
+ if (ch === ";") {
27652
+ if (current.trim().length > 0) {
27653
+ statements.push(current.trim());
27654
+ }
27655
+ current = "";
27656
+ i += 1;
27657
+ continue;
27658
+ }
27659
+ current += ch;
27660
+ i += 1;
27661
+ }
27662
+ if (current.trim().length > 0) {
27663
+ statements.push(current.trim());
27664
+ }
27665
+ return statements;
27666
+ }
27667
+ /**
27668
+ * Scan a (possibly multi-statement) SQL script and return the destructive
27669
+ * statements found. Empty array means no destructive statement detected.
27670
+ */
27671
+ function findDestructiveStatements(sqlText) {
27672
+ return splitSqlStatements(sqlText).filter((stmt) => isDestructiveSql(stmt));
27673
+ }
27674
+
27675
+
27016
27676
  /***/ },
27017
27677
 
27018
27678
  /***/ 5198
@@ -27026,12 +27686,14 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
27026
27686
  exports.reportToolkitLifecycle = exports.reportToolCall = exports.telemetryReporter = void 0;
27027
27687
  exports.extractMcpClientInfo = extractMcpClientInfo;
27028
27688
  exports.readMcpClientInfoFromServer = readMcpClientInfoFromServer;
27689
+ exports.normalizeClientName = normalizeClientName;
27029
27690
  const crypto_1 = __importDefault(__webpack_require__(7823));
27030
27691
  const http_1 = __importDefault(__webpack_require__(3782));
27031
27692
  const https_1 = __importDefault(__webpack_require__(3871));
27032
27693
  const os_1 = __importDefault(__webpack_require__(8116));
27033
27694
  const cloudbase_manager_js_1 = __webpack_require__(3109);
27034
27695
  const logger_js_1 = __webpack_require__(781);
27696
+ const site_map_js_1 = __webpack_require__(4533);
27035
27697
  /**
27036
27698
  * 数据上报类
27037
27699
  * 用于收集 MCP 工具使用情况和错误信息,帮助改进产品
@@ -27061,6 +27723,12 @@ class TelemetryReporter {
27061
27723
  this.addAdditionalParams({ ide: process.env.INTEGRATION_IDE });
27062
27724
  (0, logger_js_1.debug)('检测到 IDE 集成环境', { ide: process.env.INTEGRATION_IDE });
27063
27725
  }
27726
+ // 检查 CLOUDBASE_MCP_CLIENT 环境变量(hosted 场景由上游解析真实 MCP client 后注入)
27727
+ const envClient = normalizeClientName(process.env.CLOUDBASE_MCP_CLIENT);
27728
+ if (envClient) {
27729
+ this.addAdditionalParams({ client: envClient });
27730
+ (0, logger_js_1.debug)('检测到 MCP client 标识', { client: envClient });
27731
+ }
27064
27732
  (0, logger_js_1.debug)('report_init', {
27065
27733
  enabled: this.enabled,
27066
27734
  deviceId: this.deviceId.substring(0, 8) + '...',
@@ -27078,7 +27746,7 @@ class TelemetryReporter {
27078
27746
  const arch = os_1.default.arch(); // 系统架构
27079
27747
  // Build-time injected version; guard for vitest / non-webpack runtimes.
27080
27748
  const mcpVersion = process.env.npm_package_version ||
27081
- ( true ? "2.33.0" : 0);
27749
+ ( true ? "2.33.2" : 0);
27082
27750
  return {
27083
27751
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
27084
27752
  deviceId: this.deviceId,
@@ -27289,6 +27957,58 @@ function readMcpClientInfoFromServer(server) {
27289
27957
  return {};
27290
27958
  }
27291
27959
  }
27960
+ /**
27961
+ * Normalize upstream-provided client identifier into a telemetry-safe token:
27962
+ * trim + lowercase + keep [a-z0-9-_] only + cap at 64 chars.
27963
+ * Canonical mapping (cursor-vscode -> cursor etc.) is the upstream's responsibility;
27964
+ * we only sanitize. Returns undefined when nothing usable remains.
27965
+ */
27966
+ function normalizeClientName(value) {
27967
+ if (typeof value !== "string") {
27968
+ return undefined;
27969
+ }
27970
+ const cleaned = value
27971
+ .trim()
27972
+ .toLowerCase()
27973
+ .replace(/[^a-z0-9_-]/g, "")
27974
+ .slice(0, 64);
27975
+ return cleaned || undefined;
27976
+ }
27977
+ // 进程内缓存 site/region 解析结果(resolveSiteAndRegion 会读项目配置文件,避免每次工具调用都走 IO)
27978
+ let cachedSiteRegion = null;
27979
+ /**
27980
+ * Resolve region/site for telemetry.
27981
+ * Priority: cloudBaseOptions(会话实际使用值)> resolveSiteAndRegion(env/项目配置解析,进程内缓存)。
27982
+ * 失败时降级 unknown,绝不影响工具调用主链路。
27983
+ */
27984
+ function resolveTelemetrySiteRegion(cloudBaseOptions) {
27985
+ const explicitRegion = cloudBaseOptions?.region;
27986
+ const explicitSite = (0, site_map_js_1.normalizeSite)(cloudBaseOptions?.site);
27987
+ try {
27988
+ if (!cachedSiteRegion) {
27989
+ const resolved = (0, site_map_js_1.resolveSiteAndRegion)();
27990
+ cachedSiteRegion = { site: resolved.site, region: resolved.region };
27991
+ }
27992
+ return {
27993
+ region: explicitRegion || cachedSiteRegion.region,
27994
+ site: explicitSite || cachedSiteRegion.site,
27995
+ };
27996
+ }
27997
+ catch (err) {
27998
+ (0, logger_js_1.debug)('解析 site/region 失败,遥测使用 unknown', err instanceof Error ? err : new Error(String(err)));
27999
+ return {
28000
+ region: explicitRegion || 'unknown',
28001
+ site: explicitSite || 'unknown',
28002
+ };
28003
+ }
28004
+ }
28005
+ /**
28006
+ * Resolve MCP client identifier for a single report:
28007
+ * explicit param (server.client) > CLOUDBASE_MCP_CLIENT env (hosted upstream injection).
28008
+ */
28009
+ function resolveClientName(explicit) {
28010
+ return normalizeClientName(explicit || process.env.CLOUDBASE_MCP_CLIENT);
28011
+ }
27292
28012
  function appendMcpClientInfoFields(eventData, clientInfo) {
27293
28013
  if (!clientInfo) {
27294
28014
  return;
@@ -27308,6 +28028,7 @@ const reportToolCall = async (params) => {
27308
28028
  const { nodeVersion, osType, osRelease, arch, mcpVersion } = exports.telemetryReporter.getUserAgent();
27309
28029
  // 安全获取环境ID,优先使用传入的配置
27310
28030
  const { envId, envIdSource } = resolveEnvId(params.cloudBaseOptions);
28031
+ const siteRegion = resolveTelemetrySiteRegion(params.cloudBaseOptions);
27311
28032
  (0, logger_js_1.debug)('[telemetry] 工具调用 envId 获取结果', {
27312
28033
  toolName: params.toolName,
27313
28034
  envId,
@@ -27323,6 +28044,8 @@ const reportToolCall = async (params) => {
27323
28044
  duration: params.duration !== undefined ? String(params.duration) : undefined,
27324
28045
  error: params.error ? params.error.substring(0, 200) : undefined, // 限制错误信息长度
27325
28046
  envId: envId || 'unknown',
28047
+ region: siteRegion.region,
28048
+ site: siteRegion.site,
27326
28049
  nodeVersion,
27327
28050
  osType,
27328
28051
  osRelease,
@@ -27347,6 +28070,11 @@ const reportToolCall = async (params) => {
27347
28070
  if (params.ide) {
27348
28071
  eventData.ide = params.ide;
27349
28072
  }
28073
+ // 添加 MCP client 标识(如果提供)
28074
+ const client = resolveClientName(params.client);
28075
+ if (client) {
28076
+ eventData.client = client;
28077
+ }
27350
28078
  appendMcpClientInfoFields(eventData, params.mcpClientInfo);
27351
28079
  // Debug: 打印最终上报参数
27352
28080
  (0, logger_js_1.debug)('[telemetry] 工具调用上报参数', {
@@ -27366,6 +28094,7 @@ const reportToolkitLifecycle = async (params) => {
27366
28094
  const { nodeVersion, osType, osRelease, arch, mcpVersion } = exports.telemetryReporter.getUserAgent();
27367
28095
  // 安全获取环境ID,优先使用传入的配置
27368
28096
  const { envId, envIdSource } = resolveEnvId(params.cloudBaseOptions);
28097
+ const siteRegion = resolveTelemetrySiteRegion(params.cloudBaseOptions);
27369
28098
  (0, logger_js_1.debug)('[telemetry] 生命周期事件 envId 获取结果', {
27370
28099
  event: params.event,
27371
28100
  envId,
@@ -27380,6 +28109,8 @@ const reportToolkitLifecycle = async (params) => {
27380
28109
  exitCode: params.exitCode !== undefined ? String(params.exitCode) : undefined,
27381
28110
  error: params.error ? params.error.substring(0, 200) : undefined, // 限制错误信息长度
27382
28111
  envId: envId || 'unknown',
28112
+ region: siteRegion.region,
28113
+ site: siteRegion.site,
27383
28114
  nodeVersion,
27384
28115
  osType,
27385
28116
  osRelease,
@@ -27390,6 +28121,11 @@ const reportToolkitLifecycle = async (params) => {
27390
28121
  if (params.ide) {
27391
28122
  eventData.ide = params.ide;
27392
28123
  }
28124
+ // 添加 MCP client 标识(如果提供)
28125
+ const lifecycleClient = resolveClientName(params.client);
28126
+ if (lifecycleClient) {
28127
+ eventData.client = lifecycleClient;
28128
+ }
27393
28129
  appendMcpClientInfoFields(eventData, params.mcpClientInfo);
27394
28130
  // Debug: 打印最终上报参数
27395
28131
  (0, logger_js_1.debug)('[telemetry] 生命周期事件上报参数', {
@@ -27602,7 +28338,7 @@ ${envIdSection}
27602
28338
  ## 环境信息
27603
28339
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
27604
28340
  - Node.js版本: ${process.version}
27605
- - MCP 版本:${process.env.npm_package_version || ( true ? "2.33.0" : 0)}
28341
+ - MCP 版本:${process.env.npm_package_version || ( true ? "2.33.2" : 0)}
27606
28342
  - 系统架构: ${os_1.default.arch()}
27607
28343
  - 时间: ${new Date().toISOString()}
27608
28344
  - 请求ID: ${requestId}
@@ -27691,7 +28427,7 @@ function createWrappedHandler(name, handler, server) {
27691
28427
  requestId,
27692
28428
  ide: server.ide || process.env.INTEGRATION_IDE || ''
27693
28429
  });
27694
- const mcpVersion = true ? "2.33.0" : 0;
28430
+ const mcpVersion = true ? "2.33.2" : 0;
27695
28431
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
27696
28432
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
27697
28433
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -27729,6 +28465,7 @@ function createWrappedHandler(name, handler, server) {
27729
28465
  inputParams: sanitizeArgs(args), // 添加入参上报
27730
28466
  cloudBaseOptions: cloudBaseOptions, // 传递 CloudBase 配置(可能已更新)
27731
28467
  ide: server.ide || process.env.INTEGRATION_IDE, // 传递集成IDE信息
28468
+ client: server.client || process.env.CLOUDBASE_MCP_CLIENT, // 传递 MCP client 来源标识
27732
28469
  mcpClientInfo: (0, telemetry_js_1.readMcpClientInfoFromServer)(server),
27733
28470
  }).catch(err => {
27734
28471
  // 静默处理上报错误,不影响主要功能
@@ -27859,7 +28596,7 @@ var x = (y) => {
27859
28596
  var x = {}; __webpack_require__.d(x, y); return x
27860
28597
  }
27861
28598
  var y = (x) => (() => (x))
27862
- module.exports = x({ ["AuthSupervisor"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.AuthSupervisor), ["authStore"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.authStore), ["loadEnvVariables"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.loadEnvVariables), ["refreshTmpToken"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.refreshTmpToken), ["resolveCredential"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.resolveCredential) });
28599
+ module.exports = x({ ["AuthSupervisor"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.AuthSupervisor), ["ConfigParser"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.ConfigParser), ["authStore"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.authStore), ["loadConfig"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.loadConfig), ["loadEnvVariables"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.loadEnvVariables), ["refreshTmpToken"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.refreshTmpToken), ["resolveCredential"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.resolveCredential), ["searchConfig"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.searchConfig), ["validateCloudBaseConfigBySchema"]: () => (__WEBPACK_EXTERNAL_MODULE__cloudbase_toolbox_034e5bd0__.validateCloudBaseConfigBySchema) });
27863
28600
 
27864
28601
  /***/ },
27865
28602
 
@@ -27978,13 +28715,9 @@ module.exports = x({ ["randomUUID"]: () => (__WEBPACK_EXTERNAL_MODULE_node_crypt
27978
28715
  /***/ },
27979
28716
 
27980
28717
  /***/ 9627
27981
- (module, __unused_webpack_exports, __webpack_require__) {
28718
+ (module) {
27982
28719
 
27983
- var x = (y) => {
27984
- var x = {}; __webpack_require__.d(x, y); return x
27985
- }
27986
- var y = (x) => (() => (x))
27987
- module.exports = x({ ["existsSync"]: () => (__WEBPACK_EXTERNAL_MODULE_node_fs_75ed2103__.existsSync), ["readFileSync"]: () => (__WEBPACK_EXTERNAL_MODULE_node_fs_75ed2103__.readFileSync) });
28720
+ module.exports = __WEBPACK_EXTERNAL_MODULE_node_fs_75ed2103__;
27988
28721
 
27989
28722
  /***/ },
27990
28723