@cloudbase/cloudbase-mcp 2.36.0 → 2.36.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
@@ -15,6 +15,7 @@ import * as __WEBPACK_EXTERNAL_MODULE_http__ from "http";
15
15
  import * as __WEBPACK_EXTERNAL_MODULE_https__ from "https";
16
16
  import * as __WEBPACK_EXTERNAL_MODULE_lockfile__ from "lockfile";
17
17
  import * as __WEBPACK_EXTERNAL_MODULE_net__ from "net";
18
+ import * as __WEBPACK_EXTERNAL_MODULE_node_async_hooks_1fa35564__ from "node:async_hooks";
18
19
  import * as __WEBPACK_EXTERNAL_MODULE_node_crypto_803ecaf5__ from "node:crypto";
19
20
  import * as __WEBPACK_EXTERNAL_MODULE_node_fs_75ed2103__ from "node:fs";
20
21
  import * as __WEBPACK_EXTERNAL_MODULE_node_os_e12349cb__ from "node:os";
@@ -47,6 +48,7 @@ exports.resolveAuthProgressState = resolveAuthProgressState;
47
48
  exports.rejectAuthProgressState = rejectAuthProgressState;
48
49
  exports.resetAuthProgressState = resetAuthProgressState;
49
50
  exports.getCredentialSource = getCredentialSource;
51
+ exports.isEnvCredentialRemovableByLogout = isEnvCredentialRemovableByLogout;
50
52
  exports.peekLoginState = peekLoginState;
51
53
  exports.ensureLogin = ensureLogin;
52
54
  exports.getLoginState = getLoginState;
@@ -480,6 +482,21 @@ function readProjectCredentialFile(projectRoot) {
480
482
  return null;
481
483
  }
482
484
  }
485
+ /**
486
+ * 删掉项目级凭据文件。只删这一个文件,目录留着——删目录会顺带清掉同在里面的
487
+ * `project.json` 等项目配置。
488
+ *
489
+ * 不走 toolbox 的 `logout({cwd})`:那个接口会**无条件先删全局凭据**,而「退出本项目」
490
+ * 不该把用户的账号级登录一起注销掉。
491
+ */
492
+ function deleteProjectCredentialFile(projectRoot) {
493
+ try {
494
+ (0, node_fs_1.unlinkSync)((0, node_path_1.join)((0, toolbox_1.getProjectDir)(projectRoot), "auth.json"));
495
+ }
496
+ catch {
497
+ // 文件本来就不在:登出是幂等的,不报错
498
+ }
499
+ }
483
500
  /**
484
501
  * 项目级凭据优先于账号级全局登录态。
485
502
  *
@@ -537,6 +554,32 @@ function clearAdoptedProjectEnv() {
537
554
  function isAdoptedProjectCredential(apiKey, envId) {
538
555
  return adoptedProjectApiKey !== null && apiKey === adoptedProjectApiKey && envId === adoptedProjectEnvId;
539
556
  }
557
+ /**
558
+ * env 里的这一份凭据能不能被 `logout()` 清掉。
559
+ *
560
+ * 能清掉的只有两种情况:是我们从项目级凭据文件回填的,或者它与当前项目级凭据文件里
561
+ * 那一份相同(`auth(login_by_api_key)` 写进程 env 的同时也把同一个 key 落到项目级)。
562
+ * 这两种都能沿 `logout()` 清掉:删项目级文件、再清 env。
563
+ *
564
+ * 其余情况是用户自己在 shell 或宿主配置里设的变量,进程删不掉,只能提示他去源头改。
565
+ */
566
+ function isEnvCredentialRemovableByLogout() {
567
+ const apiKey = getCloudBaseApiKeyFromEnv();
568
+ const envId = process.env.CLOUDBASE_ENV_ID;
569
+ if (!apiKey || !envId) {
570
+ return false;
571
+ }
572
+ if (isAdoptedProjectCredential(apiKey, envId)) {
573
+ return true;
574
+ }
575
+ try {
576
+ const stored = readProjectCredentialFile((0, project_config_js_1.requireProjectRoot)());
577
+ return stored?.apiKey === apiKey && stored?.envId === envId;
578
+ }
579
+ catch {
580
+ return false;
581
+ }
582
+ }
540
583
  /**
541
584
  * 全局凭据文件 `~/.config/.cloudbase/auth.json` 与 CloudBase CLI 共用一份:用户会在终端里
542
585
  * `tcb login`,宿主连接器的登录按钮执行的也是 `tcb login --flow web --yes`,两者都在
@@ -578,6 +621,8 @@ async function peekLoginState(options) {
578
621
  dropStaleCredentialStoreCache();
579
622
  credentialSource = null;
580
623
  const envVarLoginState = normalizeLoginStateFromEnvVars(options);
624
+ // 回填的那份 key 换不出临时密钥时置位:项目级文件里是同一份,不必再试一次
625
+ let projectCredentialSkipped = false;
581
626
  if (envVarLoginState) {
582
627
  // API Key 模式:需要先用 API Key 换取临时密钥
583
628
  if ('_type' in envVarLoginState && envVarLoginState._type === 'api_key') {
@@ -599,16 +644,28 @@ async function peekLoginState(options) {
599
644
  }
600
645
  catch (e) {
601
646
  (0, logger_js_1.debug)("peekLoginState: API Key login failed", { error: e instanceof Error ? e.message : String(e) });
602
- return null;
647
+ // 我们回填的那份失效(密钥被撤、环境被删):先清掉残留,别让它一直占着 env
648
+ // 把状态查询、登出、重新登录全堵死;项目文件本身不动,用户重新登录会覆盖它。
649
+ // 清完继续往下走,让账号级登录态接上。
650
+ if (isAdoptedProjectCredential(envVarLoginState.apiKey, envVarLoginState.envId)) {
651
+ clearAdoptedProjectEnv();
652
+ projectCredentialSkipped = true;
653
+ }
654
+ else {
655
+ // 用户自己在 shell / 宿主配置里设的变量,登不登得上由他那边决定
656
+ return null;
657
+ }
603
658
  }
604
659
  }
605
- // 腾讯云密钥模式:直接返回
606
- (0, logger_js_1.debug)("loginByApiSecret");
607
- credentialSource = "env";
608
- return envVarLoginState;
660
+ if (!projectCredentialSkipped) {
661
+ // 腾讯云密钥模式:直接返回
662
+ (0, logger_js_1.debug)("loginByApiSecret");
663
+ credentialSource = "env";
664
+ return envVarLoginState;
665
+ }
609
666
  }
610
667
  // 项目级凭据优先于账号级登录态(与 readProjectEnvId 同一原则)
611
- const projectLoginState = await resolveProjectLoginState();
668
+ const projectLoginState = projectCredentialSkipped ? null : await resolveProjectLoginState();
612
669
  if (projectLoginState) {
613
670
  credentialSource = "project";
614
671
  return projectLoginState;
@@ -707,21 +764,44 @@ async function ensureLogin(options) {
707
764
  async function getLoginState(options) {
708
765
  return ensureLogin(options);
709
766
  }
767
+ /**
768
+ * 退出登录:只清「当前生效的那一份」。
769
+ *
770
+ * 项目级凭据优先于账号级登录态,所以项目里有 API Key 时,登出只删项目文件、清掉
771
+ * 回填的环境变量,账号登录原样保留(删完它自动接上)。再调一次才轮到全局。
772
+ *
773
+ * 不把 cwd 交给 toolbox 的 `logout()`:那个接口会**无条件先删全局凭据**,
774
+ * 「退出本项目」不该把用户的账号级登录一起注销掉。
775
+ */
710
776
  async function logout(_options) {
711
777
  // 先同步缓存再删:否则 toolbox 会把本进程启动时的陈旧快照写回盘,
712
778
  // 把外部(CLI / 宿主登录按钮)刚写入的凭据一起抹掉。
713
779
  dropStaleCredentialStoreCache();
714
- let cwd;
780
+ const removed = [];
781
+ let projectRoot;
715
782
  try {
716
- cwd = (0, project_config_js_1.requireProjectRoot)();
783
+ projectRoot = (0, project_config_js_1.requireProjectRoot)();
717
784
  }
718
785
  catch {
719
- cwd = undefined;
786
+ // 宿主配置目录等非法项目根:没有项目级凭据
787
+ projectRoot = undefined;
788
+ }
789
+ const envApiKey = getCloudBaseApiKeyFromEnv();
790
+ const hasProjectCredential = !!(projectRoot && readProjectCredentialFile(projectRoot));
791
+ // env 里那份是用户自己设的(进程删不掉)时,既不算项目级生效,也不该顺手注销账号登录
792
+ const projectInEffect = hasProjectCredential && (!envApiKey || isEnvCredentialRemovableByLogout());
793
+ if (projectInEffect && projectRoot) {
794
+ deleteProjectCredentialFile(projectRoot);
795
+ clearAdoptedProjectEnv();
796
+ removed.push("project");
797
+ }
798
+ else if (!envApiKey) {
799
+ await auth.logout();
800
+ removed.push("global");
720
801
  }
721
- await auth.logout(cwd ? { cwd } : undefined);
722
- clearAdoptedProjectEnv();
723
802
  credentialSource = null;
724
803
  resetAuthProgressState();
804
+ return { removed };
725
805
  }
726
806
 
727
807
 
@@ -735,7 +815,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
735
815
  return (mod && mod.__esModule) ? mod : { "default": mod };
736
816
  };
737
817
  Object.defineProperty(exports, "__esModule", ({ value: true }));
738
- exports.envManager = void 0;
818
+ exports.envManager = exports.runInEnvSlot = void 0;
739
819
  exports.probeApiKeyCamCapability = probeApiKeyCamCapability;
740
820
  exports.fetchEnvOwnerUin = fetchEnvOwnerUin;
741
821
  exports.probeCamCapabilityForLogin = probeCamCapabilityForLogin;
@@ -757,7 +837,10 @@ const logger_js_1 = __webpack_require__(781);
757
837
  const tool_result_js_1 = __webpack_require__(9645);
758
838
  const site_map_js_1 = __webpack_require__(4533);
759
839
  const project_config_js_1 = __webpack_require__(9175);
840
+ const cloud_mode_js_1 = __webpack_require__(8694);
841
+ const env_binding_js_1 = __webpack_require__(1498);
760
842
  const local_endpoint_js_1 = __webpack_require__(6156);
843
+ const describe_envs_query_js_1 = __webpack_require__(1284);
761
844
  function toEnvCandidates(envList) {
762
845
  if (!Array.isArray(envList)) {
763
846
  return [];
@@ -943,11 +1026,7 @@ async function listAvailableEnvCandidates(options) {
943
1026
  try {
944
1027
  const result = await cloudbase.commonService("tcb", "2018-06-08").call({
945
1028
  Action: "DescribeEnvs",
946
- Param: {
947
- EnvTypes: ["weda", "baas"],
948
- IsVisible: false,
949
- Channels: ["dcloud", "iotenable", "tem", "scene_module"],
950
- },
1029
+ Param: (0, describe_envs_query_js_1.buildDescribeEnvsFilter)(),
951
1030
  });
952
1031
  const envList = result?.EnvList || result?.Data?.EnvList || [];
953
1032
  return toEnvCandidates(envList);
@@ -1165,6 +1244,7 @@ class EnvironmentManager {
1165
1244
  // 手动设置环境ID(用于外部调用)
1166
1245
  async setEnvId(envId) {
1167
1246
  this._setCachedEnvId(envId);
1247
+ (0, env_binding_js_1.noteEnvBinding)({ envId, source: "envManager.cachedEnvId" });
1168
1248
  (0, logger_js_1.debug)('手动设置环境ID并更新缓存:', { envId });
1169
1249
  }
1170
1250
  // Get cached envId without triggering fetch (for optimization)
@@ -1175,44 +1255,136 @@ class EnvironmentManager {
1175
1255
  // 全局实例
1176
1256
  const envManager = new EnvironmentManager();
1177
1257
  exports.envManager = envManager;
1178
- // 导出环境ID获取函数
1258
+ function requestHasCloudCredentials(cloudBaseOptions) {
1259
+ return Boolean((cloudBaseOptions?.secretId && cloudBaseOptions?.secretKey) ||
1260
+ cloudBaseOptions?.requestFn);
1261
+ }
1262
+ function throwCloudModeEnvRequired(envCandidates) {
1263
+ const singleEnvId = envCandidates.length === 1 ? envCandidates[0].envId : undefined;
1264
+ (0, tool_result_js_1.throwToolPayloadError)({
1265
+ ok: false,
1266
+ code: "ENV_REQUIRED",
1267
+ message: "当前请求没有带 envId。Cloud mode 不读取本机登录态、项目配置或进程环境变量。",
1268
+ env_candidates: envCandidates,
1269
+ next_step: (0, tool_result_js_1.buildAuthNextStep)("set_env", {
1270
+ requiredParams: singleEnvId ? undefined : ["envId"],
1271
+ suggestedArgs: singleEnvId
1272
+ ? { action: "set_env", envId: singleEnvId }
1273
+ : { action: "set_env" },
1274
+ }),
1275
+ });
1276
+ }
1277
+ /**
1278
+ * Cloud mode only sees this request. No envManager, no CLOUDBASE_ENV_ID, no project file, no auth.json.
1279
+ * A single environment is written back onto this request's options and is not cached on the process.
1280
+ */
1281
+ async function resolveCloudModeEnvId(cloudBaseOptions, requireEnvId) {
1282
+ if (cloudBaseOptions?.envId) {
1283
+ (0, env_binding_js_1.noteEnvBinding)({ envId: cloudBaseOptions.envId, source: "cloudBaseOptions" });
1284
+ return cloudBaseOptions.envId;
1285
+ }
1286
+ if (!requireEnvId) {
1287
+ return undefined;
1288
+ }
1289
+ if (!requestHasCloudCredentials(cloudBaseOptions)) {
1290
+ throwCloudModeEnvRequired([]);
1291
+ }
1292
+ const envCandidates = await listAvailableEnvCandidates({ cloudBaseOptions });
1293
+ if (envCandidates.length === 1) {
1294
+ const singleEnvId = envCandidates[0].envId;
1295
+ if (cloudBaseOptions) {
1296
+ cloudBaseOptions.envId = singleEnvId;
1297
+ }
1298
+ (0, env_binding_js_1.noteEnvBinding)({ envId: singleEnvId, source: "auto_bound" });
1299
+ return singleEnvId;
1300
+ }
1301
+ throwCloudModeEnvRequired(envCandidates);
1302
+ }
1303
+ async function getCloudBaseManagerCloudMode(options) {
1304
+ const requireEnvId = options.requireEnvId !== false;
1305
+ const cloudBaseOptions = options.cloudBaseOptions;
1306
+ if (!requestHasCloudCredentials(cloudBaseOptions)) {
1307
+ throwAuthRequiredError();
1308
+ }
1309
+ const resolvedEnvId = await resolveCloudModeEnvId(cloudBaseOptions, requireEnvId);
1310
+ return createCloudBaseManagerWithOptions({
1311
+ ...(cloudBaseOptions ?? {}),
1312
+ envId: resolvedEnvId,
1313
+ });
1314
+ }
1315
+ /**
1316
+ * Fresh slot for tests. Production stdio/hosted pass their own slot into `runWithEnvSlot`.
1317
+ * Re-exported from `env-binding.ts` so there is only one definition.
1318
+ */
1319
+ var env_binding_js_2 = __webpack_require__(1498);
1320
+ Object.defineProperty(exports, "runInEnvSlot", ({ enumerable: true, get: function () { return env_binding_js_2.runInEnvSlot; } }));
1179
1321
  async function getEnvId(cloudBaseOptions) {
1322
+ if ((0, cloud_mode_js_1.isCloudMode)()) {
1323
+ const envId = await resolveCloudModeEnvId(cloudBaseOptions, true);
1324
+ if (!envId) {
1325
+ throwCloudModeEnvRequired([]);
1326
+ }
1327
+ return envId;
1328
+ }
1329
+ return resolveEnvIdForCall(cloudBaseOptions);
1330
+ }
1331
+ async function resolveEnvIdForCall(cloudBaseOptions) {
1180
1332
  if (cloudBaseOptions?.envId) {
1181
1333
  (0, logger_js_1.debug)('使用传入的 envId:', { envId: cloudBaseOptions.envId });
1334
+ (0, env_binding_js_1.noteEnvBinding)({ envId: cloudBaseOptions.envId, source: "cloudBaseOptions" });
1182
1335
  return cloudBaseOptions.envId;
1183
1336
  }
1184
1337
  if ((0, local_endpoint_js_1.resolveLocalEndpoint)()) {
1185
1338
  return 'local';
1186
1339
  }
1187
- const cachedEnvId = envManager.getCachedEnvId() || process.env.CLOUDBASE_ENV_ID;
1340
+ const cachedEnvId = envManager.getCachedEnvId();
1188
1341
  if (cachedEnvId) {
1189
1342
  (0, logger_js_1.debug)('使用缓存中的 envId:', { envId: cachedEnvId });
1343
+ (0, env_binding_js_1.noteEnvBinding)({ envId: cachedEnvId, source: "envManager.cachedEnvId" });
1190
1344
  return cachedEnvId;
1191
1345
  }
1192
- // 项目级配置固定的环境优先于账号级登录态,保证「打开仓库即连对环境」,
1193
- // 且同一仓库的每个 Git worktree / 每个新进程都不需要重复 set_env。
1346
+ if (process.env.CLOUDBASE_ENV_ID) {
1347
+ (0, logger_js_1.debug)('使用进程环境变量的 envId:', { envId: process.env.CLOUDBASE_ENV_ID });
1348
+ (0, env_binding_js_1.noteEnvBinding)({
1349
+ envId: process.env.CLOUDBASE_ENV_ID,
1350
+ source: "process.env.CLOUDBASE_ENV_ID",
1351
+ });
1352
+ return process.env.CLOUDBASE_ENV_ID;
1353
+ }
1354
+ // Project config stays ahead of the account login env, and still writes the cache.
1194
1355
  const projectEnvId = (0, project_config_js_1.readProjectEnvId)();
1195
1356
  if (projectEnvId) {
1196
1357
  (0, logger_js_1.debug)('使用项目配置(.cloudbase/project.json)的 envId:', { envId: projectEnvId });
1197
1358
  await envManager.setEnvId(projectEnvId);
1359
+ (0, env_binding_js_1.noteEnvBinding)({ envId: projectEnvId, source: "project" });
1198
1360
  return projectEnvId;
1199
1361
  }
1200
1362
  const loginState = await (0, auth_js_1.peekLoginState)();
1201
1363
  if (typeof loginState?.envId === 'string' && loginState.envId.length > 0) {
1202
1364
  (0, logger_js_1.debug)('使用登录态中的 envId:', { envId: loginState.envId });
1365
+ // Do not cache. A project file that appears later in this process must still win.
1366
+ (0, env_binding_js_1.noteEnvBinding)({ envId: loginState.envId, source: "login_state" });
1203
1367
  return loginState.envId;
1204
1368
  }
1205
- // 否则使用默认逻辑
1206
- return envManager.getEnvId();
1369
+ const envId = await envManager.getEnvId();
1370
+ (0, env_binding_js_1.noteEnvBinding)({ envId, source: "auto_bound" });
1371
+ return envId;
1207
1372
  }
1208
- // 导出函数保持兼容性
1209
1373
  function resetCloudBaseManagerCache() {
1210
1374
  envManager.reset();
1375
+ (0, env_binding_js_1.clearEnvBinding)();
1211
1376
  }
1212
1377
  // 导出获取缓存环境ID的函数,供遥测模块使用
1213
1378
  function getCachedEnvId() {
1214
1379
  return envManager.getCachedEnvId();
1215
1380
  }
1381
+ /**
1382
+ * 实例 ID 是环境关联的 PaaS 资源标识,正常情况下与 envId 同生命周期。
1383
+ * 但失效通知跨系统没有出口(invalidate 在生产路径上没有任何调用点),
1384
+ * 所以用一个长 TTL 兜底:宁可每半小时多查一次环境信息,
1385
+ * 也不要拿着一份没人能证明还正确的实例 ID 一直用下去。
1386
+ */
1387
+ const DATABASE_INSTANCE_ID_CACHE_TTL_MS = 30 * 60 * 1000;
1216
1388
  const databaseInstanceIdCache = new Map();
1217
1389
  function buildDatabaseInstanceIdCacheKey(options) {
1218
1390
  const envId = options?.envId ?? options?.cloudBaseOptions?.envId ?? process.env.CLOUDBASE_ENV_ID ?? 'unknown';
@@ -1243,11 +1415,16 @@ async function getDatabaseInstanceId(options) {
1243
1415
  });
1244
1416
  const cachedEntry = databaseInstanceIdCache.get(cacheKey);
1245
1417
  if (cachedEntry?.instanceId) {
1246
- return {
1247
- instanceId: cachedEntry.instanceId,
1248
- source: 'cache',
1249
- cacheKey,
1250
- };
1418
+ if (cachedEntry.expiresAt && cachedEntry.expiresAt <= Date.now()) {
1419
+ databaseInstanceIdCache.delete(cacheKey);
1420
+ }
1421
+ else {
1422
+ return {
1423
+ instanceId: cachedEntry.instanceId,
1424
+ source: 'cache',
1425
+ cacheKey,
1426
+ };
1427
+ }
1251
1428
  }
1252
1429
  if (cachedEntry?.inflightPromise) {
1253
1430
  const instanceId = await cachedEntry.inflightPromise;
@@ -1272,6 +1449,7 @@ async function getDatabaseInstanceId(options) {
1272
1449
  const instanceId = await inflightPromise;
1273
1450
  databaseInstanceIdCache.set(cacheKey, {
1274
1451
  instanceId,
1452
+ expiresAt: Date.now() + DATABASE_INSTANCE_ID_CACHE_TTL_MS,
1275
1453
  });
1276
1454
  return {
1277
1455
  instanceId,
@@ -1288,6 +1466,9 @@ async function getDatabaseInstanceId(options) {
1288
1466
  * 每次都实时获取最新的 token/secretId/secretKey
1289
1467
  */
1290
1468
  async function getCloudBaseManager(options = {}) {
1469
+ if ((0, cloud_mode_js_1.isCloudMode)()) {
1470
+ return getCloudBaseManagerCloudMode(options);
1471
+ }
1291
1472
  const { requireEnvId = true, authStrategy = 'fail_fast', } = options;
1292
1473
  let cloudBaseOptions = options.cloudBaseOptions;
1293
1474
  const localEndpoint = (0, local_endpoint_js_1.resolveLocalEndpoint)();
@@ -1303,12 +1484,14 @@ async function getCloudBaseManager(options = {}) {
1303
1484
  // 如果传入了完整凭据或 requestFn,优先使用显式 CloudBase 配置
1304
1485
  if (cloudBaseOptions && (hasDirectCredentials || hasRequestFn)) {
1305
1486
  let resolvedEnvId = cloudBaseOptions.envId;
1487
+ let explicitSource = cloudBaseOptions.envId ? "cloudBaseOptions" : "default";
1306
1488
  if (requireEnvId && !resolvedEnvId) {
1307
1489
  const envCandidates = await listAvailableEnvCandidates({ cloudBaseOptions });
1308
1490
  if (envCandidates.length === 1) {
1309
1491
  const singleEnvId = envCandidates[0].envId;
1310
1492
  cloudBaseOptions.envId = singleEnvId;
1311
1493
  resolvedEnvId = singleEnvId;
1494
+ explicitSource = "auto_bound";
1312
1495
  (0, logger_js_1.debug)('自动绑定唯一环境(显式配置):', { envId: singleEnvId });
1313
1496
  }
1314
1497
  else if (authStrategy === 'fail_fast') {
@@ -1328,6 +1511,9 @@ async function getCloudBaseManager(options = {}) {
1328
1511
  }
1329
1512
  }
1330
1513
  (0, logger_js_1.debug)('使用传入的 CloudBase 配置');
1514
+ if (resolvedEnvId && explicitSource !== "default") {
1515
+ (0, env_binding_js_1.noteEnvBinding)({ envId: resolvedEnvId, source: explicitSource });
1516
+ }
1331
1517
  return createCloudBaseManagerWithOptions({
1332
1518
  ...cloudBaseOptions,
1333
1519
  envId: resolvedEnvId,
@@ -1349,27 +1535,33 @@ async function getCloudBaseManager(options = {}) {
1349
1535
  }
1350
1536
  const { envId: loginEnvId, secretId, secretKey, token } = loginState;
1351
1537
  let finalEnvId = cloudBaseOptions?.envId;
1538
+ let envSource = finalEnvId ? "cloudBaseOptions" : undefined;
1352
1539
  if (requireEnvId) {
1353
1540
  if (!finalEnvId) {
1354
- // Optimize: Check if envManager has cached envId first (fast path)
1355
- // If cached, use it directly; otherwise check loginEnvId before calling getEnvId()
1356
- // This avoids unnecessary async calls when we have a valid envId available
1357
- const cachedEnvId = envManager.getCachedEnvId() || process.env.CLOUDBASE_ENV_ID;
1358
- const projectEnvId = cachedEnvId ? undefined : (0, project_config_js_1.readProjectEnvId)();
1541
+ const cachedEnvId = envManager.getCachedEnvId();
1542
+ const envFromProcess = process.env.CLOUDBASE_ENV_ID;
1543
+ const projectEnvId = (cachedEnvId || envFromProcess) ? undefined : (0, project_config_js_1.readProjectEnvId)();
1359
1544
  if (cachedEnvId) {
1360
1545
  (0, logger_js_1.debug)('使用 envManager 缓存的环境ID:', { cachedEnvId });
1361
1546
  finalEnvId = cachedEnvId;
1547
+ envSource = "envManager.cachedEnvId";
1548
+ }
1549
+ else if (envFromProcess) {
1550
+ (0, logger_js_1.debug)('使用进程环境变量的环境ID:', { envId: envFromProcess });
1551
+ finalEnvId = envFromProcess;
1552
+ envSource = "process.env.CLOUDBASE_ENV_ID";
1362
1553
  }
1363
1554
  else if (projectEnvId) {
1364
- // 项目级绑定优先于全局登录态 envId:后者可能来自另一个仓库
1555
+ // Project binding stays ahead of the account login env.
1365
1556
  (0, logger_js_1.debug)('使用项目配置(project.json/cloudbaserc.json)的环境ID:', { projectEnvId });
1366
1557
  await envManager.setEnvId(projectEnvId);
1367
1558
  finalEnvId = projectEnvId;
1559
+ envSource = "project";
1368
1560
  }
1369
1561
  else if (loginEnvId) {
1370
- // If no cache but loginState has envId, use it directly
1371
1562
  (0, logger_js_1.debug)('使用 loginState 中的环境ID:', { loginEnvId });
1372
1563
  finalEnvId = loginEnvId;
1564
+ envSource = "login_state";
1373
1565
  }
1374
1566
  else {
1375
1567
  if (authStrategy === 'fail_fast') {
@@ -1378,6 +1570,7 @@ async function getCloudBaseManager(options = {}) {
1378
1570
  const singleEnvId = envCandidates[0].envId;
1379
1571
  await envManager.setEnvId(singleEnvId);
1380
1572
  finalEnvId = singleEnvId;
1573
+ envSource = "auto_bound";
1381
1574
  (0, logger_js_1.debug)('自动绑定唯一环境:', { envId: singleEnvId });
1382
1575
  }
1383
1576
  else {
@@ -1385,14 +1578,19 @@ async function getCloudBaseManager(options = {}) {
1385
1578
  }
1386
1579
  }
1387
1580
  else {
1388
- // ensure 模式下也保持非交互:单环境自动绑定,多环境返回 ENV_REQUIRED
1389
1581
  finalEnvId = await envManager.getEnvId();
1582
+ envSource = "auto_bound";
1390
1583
  }
1391
1584
  }
1392
1585
  }
1393
1586
  }
1394
- // envId priority: explicit option > envManager cache > loginState.envId
1395
1587
  const resolvedEnvId = finalEnvId || loginEnvId;
1588
+ if (!envSource && resolvedEnvId && resolvedEnvId === loginEnvId) {
1589
+ envSource = "login_state";
1590
+ }
1591
+ if (resolvedEnvId && envSource) {
1592
+ (0, env_binding_js_1.noteEnvBinding)({ envId: resolvedEnvId, source: envSource });
1593
+ }
1396
1594
  // region 直接使用 fallbackRegion(resolveSiteAndRegion 统一解析)
1397
1595
  // 不再通过 DescribeEnvs 查询 region,以兼容 STS 临时密钥场景
1398
1596
  const region = fallbackRegion;
@@ -2121,6 +2319,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
2121
2319
  exports.handleHostedMcpRequest = handleHostedMcpRequest;
2122
2320
  const streamableHttp_js_1 = __webpack_require__(6519);
2123
2321
  const server_js_1 = __webpack_require__(6324);
2322
+ const env_binding_js_1 = __webpack_require__(1498);
2124
2323
  const feedback_session_js_1 = __webpack_require__(3379);
2125
2324
  const client_info_cache_js_1 = __webpack_require__(5454);
2126
2325
  function readInitializeClientInfo(body) {
@@ -2198,36 +2397,42 @@ async function handleHostedMcpRequest(input) {
2198
2397
  const closeBoth = () => {
2199
2398
  void Promise.allSettled([transport?.close(), server?.close()]);
2200
2399
  };
2400
+ const injectedEnvId = input.serverOptions.cloudBaseOptions?.envId;
2401
+ const requestSlot = {
2402
+ binding: injectedEnvId
2403
+ ? { envId: injectedEnvId, source: "cloudBaseOptions" }
2404
+ : null,
2405
+ };
2201
2406
  try {
2202
- server = await (0, server_js_1.createCloudBaseMcpServer)({
2203
- name: input.serverOptions.name,
2204
- version: input.serverOptions.version,
2205
- enableTelemetry: input.serverOptions.enableTelemetry,
2206
- cloudBaseOptions: input.serverOptions.cloudBaseOptions,
2207
- cloudMode: input.serverOptions.cloudMode ?? true,
2208
- ide: input.serverOptions.ide,
2209
- client: input.serverOptions.client,
2210
- clientInfo,
2211
- // 会话级遥测的身份来源:把**当前** transport 的 sessionId 交给 server(公开 getter)。
2212
- // 这里是无状态传输(本文件 `:165` `sessionIdGenerator: undefined`)⇒ 今天它恒为 undefined、
2213
- // 上报按"一个实例一个会话"计;将来若有状态化,sessionId 会自动出现在这里。
2214
- getSessionId: () => transport?.sessionId,
2215
- lang: input.serverOptions.lang,
2216
- pluginsEnabled: input.serverOptions.pluginsEnabled,
2217
- pluginsDisabled: input.serverOptions.pluginsDisabled,
2218
- });
2219
- input.logger?.({
2220
- type: "cloudbase-mcp-server-create",
2221
- timeCost: Date.now() - startedAt,
2222
- });
2223
- transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
2224
- sessionIdGenerator: undefined,
2225
- enableJsonResponse: true,
2407
+ await (0, env_binding_js_1.runWithEnvSlot)(requestSlot, async () => {
2408
+ server = await (0, server_js_1.createCloudBaseMcpServer)({
2409
+ name: input.serverOptions.name,
2410
+ version: input.serverOptions.version,
2411
+ enableTelemetry: input.serverOptions.enableTelemetry,
2412
+ cloudBaseOptions: input.serverOptions.cloudBaseOptions,
2413
+ cloudMode: input.serverOptions.cloudMode ?? true,
2414
+ ide: input.serverOptions.ide,
2415
+ client: input.serverOptions.client,
2416
+ clientInfo,
2417
+ // Session id comes from this transport. Stateless mode leaves it undefined.
2418
+ getSessionId: () => transport?.sessionId,
2419
+ lang: input.serverOptions.lang,
2420
+ pluginsEnabled: input.serverOptions.pluginsEnabled,
2421
+ pluginsDisabled: input.serverOptions.pluginsDisabled,
2422
+ });
2423
+ input.logger?.({
2424
+ type: "cloudbase-mcp-server-create",
2425
+ timeCost: Date.now() - startedAt,
2426
+ });
2427
+ transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
2428
+ sessionIdGenerator: undefined,
2429
+ enableJsonResponse: true,
2430
+ });
2431
+ input.res.on("close", closeBoth);
2432
+ input.req.on("aborted", closeBoth);
2433
+ await server.connect(transport);
2434
+ await transport.handleRequest(input.req, input.res, input.body);
2226
2435
  });
2227
- input.res.on("close", closeBoth);
2228
- input.req.on("aborted", closeBoth);
2229
- await server.connect(transport);
2230
- await transport.handleRequest(input.req, input.res, input.body);
2231
2436
  }
2232
2437
  catch (error) {
2233
2438
  await Promise.allSettled([transport?.close(), server?.close()]);
@@ -2880,7 +3085,7 @@ exports.cloudrun = (0, types_js_1.defineModule)({
2880
3085
  "error.envNotInitialized": "当前环境({envId})尚未初始化云托管(CloudRun Env)。不能直接创建服务(CreateCloudRunServer 在无大租户记录时会默认创建到小租户,产生错误的小租户服务与版本)。\n请先开通云托管环境,再重试部署:\n- MCP:manageCloudRun(action=\"initEnv\", envId=\"{envId}\")(异步开通,幂等;开通完成后可用 queryCloudRun(action=\"envStatus\", envId=\"{envId}\") 查询 Status=normal)\n- 或控制台:环境 → 云托管 → 开通(https://tcb.cloud.tencent.com/dev?envId={envId}#/platform-run)\n初始化完成后重新调用 manageCloudRun(action=\"deploy\")。",
2881
3086
  "error.actionFailed": "[{context}] {baseMessage}",
2882
3087
  "error.buildManage": "[manageCloudRun/{action}] {baseMessage}\n建议:{suggestions}",
2883
- "error.deployTaskRunning": "服务 `{serverName}` 当前已有部署任务在执行,请等待现有任务完成后再重试。",
3088
+ "error.deployTaskRunning": "服务 `{serverName}` 当前已有部署任务在执行。**不要再发起 deploy**:先用 queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\") 读取该任务的真实状态——已结束就重新部署,长时间停在非终态则带上该输出联系 CloudBase 平台排查。",
2884
3089
  "error.deployTaskRunningForce": "注意:`force=true` 仅跳过本工具的确认提示,不会取消或覆盖平台上的发布任务。请先用 queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\") 确认任务真实状态:若任务已结束却仍报此错,或任务长时间停在非终态,请携带该输出联系 CloudBase 平台排查该环境的发布任务。",
2885
3090
  "error.vpcRequired": "CreateCloudRunServer 需要有效 VPC:请传 serverConfig.VpcConf(VpcId+SubnetId),或在 initEnv 时传入 vpcId/subnetIds(环境开通后 deploy 会自动从 EnvBaseInfo 回填)。若平台拒绝系统创建网络,必须指定上海地域 VPC。",
2886
3091
  "error.genericRetry": "请检查服务状态、部署参数和目标目录后重试。",
@@ -3070,7 +3275,7 @@ exports.cloudrun = (0, types_js_1.defineModule)({
3070
3275
  "error.envNotInitialized": "The current environment ({envId}) has not initialized CloudRun (CloudRun Env). You cannot create services directly (without a large-tenant record, CreateCloudRunServer defaults to the small tenant, producing wrong small-tenant services and versions).\nProvision the CloudRun env first, then retry the deploy:\n- MCP: manageCloudRun(action=\"initEnv\", envId=\"{envId}\") (async, idempotent; once done, query Status=normal via queryCloudRun(action=\"envStatus\", envId=\"{envId}\"))\n- Or console: Environment → CloudRun → Provision (https://tcb.cloud.tencent.com/dev?envId={envId}#/platform-run)\nAfter initialization, call manageCloudRun(action=\"deploy\") again.",
3071
3276
  "error.actionFailed": "[{context}] {baseMessage}",
3072
3277
  "error.buildManage": "[manageCloudRun/{action}] {baseMessage}\nSuggestions: {suggestions}",
3073
- "error.deployTaskRunning": "Service `{serverName}` already has a deploy task running. Wait for the current task to finish before retrying.",
3278
+ "error.deployTaskRunning": "Service `{serverName}` already has a deploy task running. Do NOT call deploy again: first read the real task state with queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\") - redeploy once it has finished, or contact the CloudBase platform with that output if it stays in a non-terminal state for a long time.",
3074
3279
  "error.deployTaskRunningForce": "Note: `force=true` only skips this tool's confirmation prompt; it does not cancel or override the platform-side deploy task. First confirm the real task state with queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\"): if the task has already finished but this error persists, or it stays in a non-terminal state for a long time, contact the CloudBase platform with that output to investigate this environment's deploy task.",
3075
3280
  "error.vpcRequired": "CreateCloudRunServer requires a valid VPC: pass serverConfig.VpcConf (VpcId+SubnetId), or pass vpcId/subnetIds during initEnv (after provisioning, deploy auto-fills from EnvBaseInfo). If the platform rejects system-created networks, you must specify a Shanghai-region VPC.",
3076
3281
  "error.genericRetry": "Check the service status, deploy parameters, and target directory, then retry.",
@@ -4674,9 +4879,12 @@ exports.env = (0, types_js_1.defineModule)({
4674
4879
  "auth.envReadyWithHint": "环境设置成功,当前环境: {envId}{regionHint}",
4675
4880
  "auth.regionHint": ",地域: {region}",
4676
4881
  "auth.regionHintUnverified": "。未在当前探测地域中确认该 envId,已按唯一 ID 直绑;若后续接口仍指向错误地域,请设置 TCB_REGION 或 queryEnv(action=\"list\", region=...)",
4677
- "auth.logoutNotAllowedApiKey": "当前使用 API Key 认证模式,不支持退出登录。如需切换认证方式,请移除 CLOUDBASE_API_KEY(或兼容的 CLOUDBASE_APIKEY)环境变量后重启。",
4882
+ "auth.logoutNotAllowedApiKey": "当前是 API Key 认证模式,凭据来自环境变量 {varName}。这个变量不是本进程设的、也不由本进程清除,所以没有执行登出。要切换认证方式:在 shell 里 unset {varName},或从启动本 MCP 的客户端配置里移除它,然后重启。",
4678
4883
  "auth.logoutConfirmRequired": "action=logout 时必须传 confirm=\"yes\"",
4679
4884
  "auth.loggedOut": "✅ 已退出登录",
4885
+ "auth.loggedOutProject": "✅ 已移除本项目的 API Key 登录。这台机器上若还有账号登录,它仍然有效;要一起退出请再执行一次 logout。",
4886
+ "auth.apiKeySwitchHintRemovable": "如需改用账号登录,先执行 logout 退出本项目 API Key,再 start_auth 授权。",
4887
+ "auth.apiKeySwitchHintExternal": "这个 key 来自外部环境变量,MCP 无法退出它;如需改用账号登录,先在 shell 或客户端配置里移除 {varName} 并重启。",
4680
4888
  "auth.tempCredNotLoggedIn": "当前未登录,请先完成管理端认证后再获取临时密钥。",
4681
4889
  "auth.tempCredConfirmRequired": "action=get_temp_credentials 时必须显式传 confirm=\"yes\",以确认你要导出当前管理端临时密钥。",
4682
4890
  "auth.tempCredUnsupportedType": "当前登录态不是可导出的临时密钥。仅支持通过 Web / device 登录得到的临时密钥,永久密钥登录不允许导出。",
@@ -4937,9 +5145,12 @@ exports.env = (0, types_js_1.defineModule)({
4937
5145
  "auth.envReadyWithHint": "Environment bound successfully. Current env: {envId}{regionHint}",
4938
5146
  "auth.regionHint": ", region: {region}",
4939
5147
  "auth.regionHintUnverified": ". This envId was not confirmed in the probed regions, so it was bound directly by its unique ID; if later APIs still target the wrong region, set TCB_REGION or use queryEnv(action=\"list\", region=...)",
4940
- "auth.logoutNotAllowedApiKey": "API Key authentication mode is in use and logout is not supported. To switch the authentication method, remove the CLOUDBASE_API_KEY (or the compatible CLOUDBASE_APIKEY) environment variable and restart.",
5148
+ "auth.logoutNotAllowedApiKey": "API Key authentication mode is in use, and the credential comes from the {varName} environment variable. This process neither set nor can remove that variable, so logout was skipped. To switch authentication methods, unset {varName} in your shell or remove it from the MCP client configuration, then restart.",
4941
5149
  "auth.logoutConfirmRequired": "confirm=\"yes\" must be passed when action=logout",
4942
5150
  "auth.loggedOut": "✅ Logged out",
5151
+ "auth.loggedOutProject": "✅ Removed this project's API Key login. An account login on this machine is still active; call logout again to sign out of it.",
5152
+ "auth.apiKeySwitchHintRemovable": "To switch to account login, run logout to remove this project's API Key, then start_auth.",
5153
+ "auth.apiKeySwitchHintExternal": "This key comes from an external environment variable and cannot be removed by MCP. To switch to account login, unset {varName} in your shell or client configuration and restart.",
4943
5154
  "auth.tempCredNotLoggedIn": "Not logged in. Complete management-plane authentication before fetching temporary credentials.",
4944
5155
  "auth.tempCredConfirmRequired": "confirm=\"yes\" must be passed explicitly when action=get_temp_credentials, to confirm that you want to export the current management-plane temporary credentials.",
4945
5156
  "auth.tempCredUnsupportedType": "The current login state is not an exportable temporary credential. Only temporary credentials obtained via Web / device login are supported; permanent key logins cannot be exported.",
@@ -5318,9 +5529,12 @@ exports.functions = (0, types_js_1.defineModule)({
5318
5529
  "\n- `limit`: 分页数量,默认值由后端决定" +
5319
5530
  "\n- `offset`: 分页偏移,从 0 开始" +
5320
5531
  "\n- 示例:`queryFunctions(action=\"listFunctions\", offset=10, limit=10)`" +
5321
- "\n\n**查询 CloudBase 云函数日志**:使用 `action=\"listFunctionLogs\"`,需要提供 `functionName` 参数。" +
5532
+ "\n\n**查询 CloudBase 云函数日志**:`action=\"listFunctionLogs\"`,需要提供 `functionName`。日志从 CLS 检索(底层 GetFunctionLogs 接口已下线)。" +
5322
5533
  "\n- 示例:`queryFunctions(action=\"listFunctionLogs\", functionName=\"my-function\")`" +
5323
- "\n- 如需查看日志详情:`queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
5534
+ "\n- 不传 startTime/endTime 时默认查最近 1 天;跨度不能超过 1 天,单次最多返回 100 条" +
5535
+ "\n- 返回 `hasMore=true` 表示还有更早的日志:把返回的 `context` 原样传回取下一页(`offset` 只在前 100 条内生效)" +
5536
+ "\n- `logs[]` 已解析成 requestId / log / statusCode / retCode / durationMs / memoryMb / timestamp" +
5537
+ "\n- 想看某一次调用的完整过程(START、业务日志、REPORT):`queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
5324
5538
  "\n\n**定时任务 / cron / 定时跑**:使用 `listFunctionTriggers` 查询函数的 timer 触发器配置。" +
5325
5539
  "\n\n**版本与流量路由**:`listVersionByFunction` 列出已发布版本(对齐 tcb fn list-function-versions);`getFunctionAlias` 查看别名/灰度配置(对齐 tcb fn get-route,aliasName 默认 `$DEFAULT`)。" +
5326
5540
  "\n\n**层(Layer)说明**:" +
@@ -5390,8 +5604,8 @@ exports.functions = (0, types_js_1.defineModule)({
5390
5604
  endTimeInvalid: "endTime 格式错误: \"{value}\"。必须使用 YYYY-MM-DD HH:mm:ss 格式(如 2024-01-01 23:59:59)",
5391
5605
  logRangeInvalidDatetime: "startTime 和 endTime 必须是有效的日期时间字符串",
5392
5606
  logRangeTooLong: "startTime 和 endTime 间隔不能超过一天",
5393
- logsInvalidParamTips: "常见原因:\n1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n2. startTime 和 endTime 间隔超过一天\n3. functionName 不存在或格式不正确\n建议:不传 startTime/endTime 时默认查询最近一天的日志。",
5394
- logDetailInvalidParamTips: "常见原因:\n1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n2. startTime 和 endTime 间隔超过一天\n建议:不传 startTime/endTime 时默认查询最近一天的日志。",
5607
+ logsInvalidParamTips: "常见原因:\n1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n2. startTime 和 endTime 间隔超过一天\n3. functionName 与日志里的 function_name 不一致(大小写敏感),或 qualifier 不匹配\n建议:不传 startTime/endTime 时默认查询最近一天的日志。",
5608
+ logDetailInvalidParamTips: "常见原因:\n1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n2. startTime 和 endTime 间隔超过一天\n3. requestId 不在该时间窗口内(日志按请求开始时间归档)\n建议:先用 listFunctionLogs 定位到 requestId 再查详情。",
5395
5609
  paramRequired: "{action} 操作时,{param} 参数是必需的",
5396
5610
  "deployStatus.cloudMode": "{action} 在 cloud mode 下不可用:异步部署任务只由 buildStrategy=cloud/local 的真实部署创建,而这两种策略的真实执行在 cloud mode 下都不支持(需要读取本地构建上下文或本地 Docker),因此不会存在任何 taskId。buildStrategy=image 走同步部署,直接在 manageFunctions 的返回里拿结果,也不需要查询部署状态。如需异步镜像构建部署,请改用本地 MCP 模式。",
5397
5611
  deployTaskNotFound: "未找到部署任务 {taskId};部署任务不存在,可能已过期、不属于当前环境 {envId},或 MCP Server 已重启(任务仅保存在 MCP 进程内存中)。",
@@ -5406,7 +5620,11 @@ exports.functions = (0, types_js_1.defineModule)({
5406
5620
  "reason.viewLogs": "查看该函数的执行日志",
5407
5621
  "reason.updateConfig": "更新该函数配置",
5408
5622
  "reason.checkGateway": "查看该函数是否已暴露网关访问入口",
5409
- "reason.logDetail": "按 requestId 查看单条日志详情",
5623
+ "reason.logDetail": "按 requestId 查看该次调用的完整日志",
5624
+ "reason.nextLogPage": "带上 context 取下一页(更早的)日志",
5625
+ "reason.widenLogRange": "放宽时间范围后重新查询该函数的日志",
5626
+ "reason.createLogService": "开通环境日志服务后再查询函数日志",
5627
+ "reason.searchClsFallback": "改用 queryLogs 直接按同一条 CLS 语句检索",
5410
5628
  "reason.attachLayer": "为该函数追加绑定层",
5411
5629
  "reason.reorderLayers": "整体调整层顺序或绑定列表",
5412
5630
  "reason.layerVersions": "查看某个层的版本列表",
@@ -5451,6 +5669,10 @@ exports.functions = (0, types_js_1.defineModule)({
5451
5669
  gotFunctionDetail: "已获取函数 {fnName} 的详情",
5452
5670
  gotFunctionLogs: "已获取函数 {fnName} 的日志列表",
5453
5671
  gotLogDetail: "已获取 requestId={requestId} 的日志详情",
5672
+ "logs.emptyByFunction": "在 {start} ~ {end} 之间没有查到函数 {fnName} 的日志。常见原因:该时间段内函数没有被调用、日志已过保留期、或 functionName / qualifier 不匹配。可以放宽时间范围后重试。",
5673
+ "logs.emptyByRequest": "在 {start} ~ {end} 之间没有查到 requestId={requestId} 的日志。请确认时间范围覆盖了这次调用(日志按请求开始时间归档),或先用 listFunctionLogs 定位到的 requestId。",
5674
+ "logs.serviceNotEnabled": "当前环境还没有开通日志服务,云函数日志查不到。请先调用 manageLogs(action=\"createLogService\", confirm=true) 开通,再重新查询。",
5675
+ "logs.queryFailed": "查询云函数日志失败:{message}",
5454
5676
  gotFunctionLayers: "已获取函数 {fnName} 当前绑定的层",
5455
5677
  gotLayers: "已获取 {count} 条层记录",
5456
5678
  gotLayerVersions: "已获取层 {layerName} 的版本列表",
@@ -5531,16 +5753,17 @@ exports.functions = (0, types_js_1.defineModule)({
5531
5753
  "schema.create.layers": "Layer 配置",
5532
5754
  "schema.manageLayer.name": "层名称",
5533
5755
  "schema.manageLayer.version": "层版本号",
5534
- "schema.query.action": "只读操作类型:\n- `listFunctions`: 列出所有 CloudBase 云函数\n- `getFunctionDetail`: 获取 CloudBase 云函数详情(需要 functionName)\n- `listFunctionLogs`: 查询 CloudBase 云函数执行日志(需要 functionName)\n- `getFunctionLogDetail`: 获取日志详情(需要 requestId)\n- `listFunctionLayers`: 列出函数绑定的层\n- `listLayers`: 列出所有层(账号级视图,含其他环境创建的层)\n- `listLayerVersions`: 列出层的版本(注意:是 Versions 不是 Version;账号级视图)\n- `getLayerVersionDetail`: 获取层版本详情(账号级视图)\n- `listFunctionTriggers`: 列出函数触发器(用于查看定时任务 / cron / timer 配置)\n- `getFunctionDownloadUrl`: 获取函数代码下载地址\n- `getFunctionDeployStatus`: 按 taskId 查询异步部署状态、阶段进度和最终结果。返回 data.build(构建子状态)、data.deploy(部署子状态)、data.progress(阶段事件);status=running 时 data.result 与 data.error 一律为 null,不得报告部署完成。调用方必须持续轮询直到 status=succeeded/failed;status=expired 表示任务超过最长保留时间(2 小时)被终结,云端可能仍在部署,需用 getFunctionDetail 确认。任务只保存在 MCP 进程内存中,过期或 MCP Server 重启后返回 errorCode=DEPLOY_TASK_NOT_FOUND;任务按环境隔离,只能查到当前环境自己发起的部署。cloud mode 下本 action 不可用:异步任务只由 buildStrategy=cloud/local 的真实部署创建,而这两种策略在 cloud mode 下都不支持真实执行,image 策略则走同步部署不产生 taskId。\n- `listVersionByFunction`: 列出函数已发布版本(对齐 tcb fn list-function-versions / SDK listVersionByFunction;需要 functionName)\n- `getFunctionAlias`: 查询函数别名与流量路由(对齐 tcb fn get-route / SDK getFunctionAlias;需要 functionName;aliasName 默认 $DEFAULT)\n- `getFunctionUploadUrl`: 获取函数代码包的 COS 预签名上传地址(ZIP 两段式部署阶段 A):PUT 代码 zip 到 uploadUrl(uploadHeaders 非空时须随请求携带对应请求头),再调用 manageFunctions 的 createFunction/updateFunctionCode 并传 code 三元组(阶段 B)。functionName 可选,仅用于生成上传对象 key。返回的 uploadUrl 含凭据签名,不得写入日志或持久化;阶段 B 会校验三元组的桶与地域必须是本环境自有存储桶,因此该地址只能用于取到它的那个环境;地址默认 300 秒内有效",
5756
+ "schema.query.action": "只读操作类型:\n- `listFunctions`: 列出所有 CloudBase 云函数\n- `getFunctionDetail`: 获取 CloudBase 云函数详情(需要 functionName)\n- `listFunctionLogs`: 查询云函数执行日志(需要 functionName;走 CLS 检索,默认最近 1 天,单次最多 100 条,翻页传 context)\n- `getFunctionLogDetail`: 获取某次调用的完整日志序列(需要 requestId,取自 listFunctionLogs 的 logs[].requestId)\n- `listFunctionLayers`: 列出函数绑定的层\n- `listLayers`: 列出所有层(账号级视图,含其他环境创建的层)\n- `listLayerVersions`: 列出层的版本(注意:是 Versions 不是 Version;账号级视图)\n- `getLayerVersionDetail`: 获取层版本详情(账号级视图)\n- `listFunctionTriggers`: 列出函数触发器(用于查看定时任务 / cron / timer 配置)\n- `getFunctionDownloadUrl`: 获取函数代码下载地址\n- `getFunctionDeployStatus`: 按 taskId 查询异步部署状态、阶段进度和最终结果。返回 data.build(构建子状态)、data.deploy(部署子状态)、data.progress(阶段事件);status=running 时 data.result 与 data.error 一律为 null,不得报告部署完成。调用方必须持续轮询直到 status=succeeded/failed;status=expired 表示任务超过最长保留时间(2 小时)被终结,云端可能仍在部署,需用 getFunctionDetail 确认。任务只保存在 MCP 进程内存中,过期或 MCP Server 重启后返回 errorCode=DEPLOY_TASK_NOT_FOUND;任务按环境隔离,只能查到当前环境自己发起的部署。cloud mode 下本 action 不可用:异步任务只由 buildStrategy=cloud/local 的真实部署创建,而这两种策略在 cloud mode 下都不支持真实执行,image 策略则走同步部署不产生 taskId。\n- `listVersionByFunction`: 列出函数已发布版本(对齐 tcb fn list-function-versions / SDK listVersionByFunction;需要 functionName)\n- `getFunctionAlias`: 查询函数别名与流量路由(对齐 tcb fn get-route / SDK getFunctionAlias;需要 functionName;aliasName 默认 $DEFAULT)\n- `getFunctionUploadUrl`: 获取函数代码包的 COS 预签名上传地址(ZIP 两段式部署阶段 A):PUT 代码 zip 到 uploadUrl(uploadHeaders 非空时须随请求携带对应请求头),再调用 manageFunctions 的 createFunction/updateFunctionCode 并传 code 三元组(阶段 B)。functionName 可选,仅用于生成上传对象 key。返回的 uploadUrl 含凭据签名,不得写入日志或持久化;阶段 B 会校验三元组的桶与地域必须是本环境自有存储桶,因此该地址只能用于取到它的那个环境;地址默认 300 秒内有效",
5535
5757
  "schema.query.functionName": "CloudBase 云函数名称。`getFunctionDetail`、`listFunctionLogs`、`listFunctionLayers`、`listFunctionTriggers`、`getFunctionDownloadUrl`、`listVersionByFunction`、`getFunctionAlias` 时必填;`getFunctionUploadUrl` 可选(仅用于生成上传对象 key)",
5536
- "schema.query.limit": "分页数量(limit)。列表类 action 可选,默认值由后端决定",
5537
- "schema.query.offset": "分页偏移(offset)。列表类 action 可选,默认 0",
5758
+ "schema.query.limit": "分页数量(limit)。列表类 action 可选,默认值由后端决定;日志类 action 单次最多 100 条,默认 20",
5759
+ "schema.query.offset": "分页偏移(offset)。列表类 action 可选,默认 0。日志类 action 只在前 100 条内生效,更早的日志请用返回的 context 翻页",
5538
5760
  "schema.query.codeSecret": "代码保护密钥,用于解密函数代码",
5539
5761
  "schema.query.revealEnvValues": "getFunctionDetail / listFunctionTriggers / listFunctionLayers 时是否返回环境变量明文值。默认 false:Value 脱敏为 ***,仅保留 Key 与 ValueLength,足以确认配置了哪些变量及变更是否生效;true 时返回明文,敏感变量会进入模型上下文,谨慎使用。如需查看明文,建议优先使用控制台或 CLI",
5540
- "schema.query.startTime": "日志查询开始时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00)。与 endTime 间隔不能超过一天。不传时默认查询最近一天",
5541
- "schema.query.endTime": "日志查询结束时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 23:59:59)。与 startTime 间隔不能超过一天。不传时默认为当前时间",
5542
- "schema.query.requestId": "日志请求 ID。`getFunctionLogDetail` 操作必填,可从 `listFunctionLogs` 结果中获取",
5543
- "schema.query.qualifier": "函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选",
5762
+ "schema.query.startTime": "日志查询开始时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00,按北京时间理解)。与 endTime 间隔不能超过一天。不传时默认查询最近一天",
5763
+ "schema.query.endTime": "日志查询结束时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 23:59:59,按北京时间理解)。与 startTime 间隔不能超过一天。不传时默认为当前时间",
5764
+ "schema.query.requestId": "日志请求 ID。`getFunctionLogDetail` 操作必填;取自 `listFunctionLogs` 返回的 logs[].requestId,也可传具体业务日志里出现的 RequestId",
5765
+ "schema.query.qualifier": "函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选,作为 CLS 检索条件",
5766
+ "schema.query.context": "日志翻页游标。把上一次 `listFunctionLogs` 返回的 `context` 原样传回即可取下一页;不要自己构造,null 表示没有更多",
5544
5767
  "schema.query.runtime": "层查询的运行时筛选,如 Nodejs18.15",
5545
5768
  "schema.query.searchKey": "层名称搜索关键字",
5546
5769
  "schema.query.layerName": "层名称。`listLayerVersions`、`getLayerVersionDetail` 操作必填。层为账号级共享命名空间;推荐固定格式 `{layerName}_{当前envId}`(如 common_cloud1-d9ghadgak3edf6b36)",
@@ -5599,9 +5822,12 @@ exports.functions = (0, types_js_1.defineModule)({
5599
5822
  "\n- `limit`: page size, the default is decided by the backend" +
5600
5823
  "\n- `offset`: pagination offset, starting from 0" +
5601
5824
  "\n- Example: `queryFunctions(action=\"listFunctions\", offset=10, limit=10)`" +
5602
- "\n\n**Querying cloud function logs**: use `action=\"listFunctionLogs\"`, which requires the `functionName` parameter." +
5825
+ "\n\n**Querying cloud function logs**: use `action=\"listFunctionLogs\"`, which requires `functionName`. Logs are retrieved from CLS (the underlying GetFunctionLogs API has been retired)." +
5603
5826
  "\n- Example: `queryFunctions(action=\"listFunctionLogs\", functionName=\"my-function\")`" +
5604
- "\n- To view log details: `queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
5827
+ "\n- Without startTime/endTime it queries the last day; the span must not exceed 1 day and a single call returns at most 100 entries" +
5828
+ "\n- `hasMore=true` means older logs exist: pass the returned `context` back unchanged to fetch the next page (`offset` only works within the first 100 entries)" +
5829
+ "\n- Each `logs[]` item is parsed into requestId / log / statusCode / retCode / durationMs / memoryMb / timestamp" +
5830
+ "\n- To see the full sequence of one invocation (START, business logs, REPORT): `queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
5605
5831
  "\n\n**Scheduled tasks / cron / timers**: use `listFunctionTriggers` to query a function's timer trigger configuration." +
5606
5832
  "\n\n**Versions and traffic routing**: `listVersionByFunction` lists published versions (aligns with tcb fn list-function-versions); `getFunctionAlias` views alias / canary config (aligns with tcb fn get-route; aliasName defaults to `$DEFAULT`)." +
5607
5833
  "\n\n**Layers**:" +
@@ -5671,8 +5897,8 @@ exports.functions = (0, types_js_1.defineModule)({
5671
5897
  endTimeInvalid: "Invalid endTime: \"{value}\". It must use the YYYY-MM-DD HH:mm:ss format (e.g. 2024-01-01 23:59:59)",
5672
5898
  logRangeInvalidDatetime: "startTime and endTime must be valid datetime strings",
5673
5899
  logRangeTooLong: "The interval between startTime and endTime must not exceed one day",
5674
- logsInvalidParamTips: "Common causes:\n1. startTime/endTime format error; it must be YYYY-MM-DD HH:mm:ss (e.g. 2024-01-01 00:00:00); ISO 8601 or timestamps are not supported\n2. The interval between startTime and endTime exceeds one day\n3. functionName does not exist or is malformed\nTip: without startTime/endTime, logs from the most recent day are queried by default.",
5675
- logDetailInvalidParamTips: "Common causes:\n1. startTime/endTime format error; it must be YYYY-MM-DD HH:mm:ss (e.g. 2024-01-01 00:00:00); ISO 8601 or timestamps are not supported\n2. The interval between startTime and endTime exceeds one day\nTip: without startTime/endTime, logs from the most recent day are queried by default.",
5900
+ logsInvalidParamTips: "Common causes:\n1. startTime/endTime format error; it must be YYYY-MM-DD HH:mm:ss (e.g. 2024-01-01 00:00:00); ISO 8601 or timestamps are not supported\n2. The interval between startTime and endTime exceeds one day\n3. functionName does not match function_name in the logs (case-sensitive), or qualifier does not match\nTip: without startTime/endTime, logs from the most recent day are queried by default.",
5901
+ logDetailInvalidParamTips: "Common causes:\n1. startTime/endTime format error; it must be YYYY-MM-DD HH:mm:ss (e.g. 2024-01-01 00:00:00); ISO 8601 or timestamps are not supported\n2. The interval between startTime and endTime exceeds one day\n3. requestId falls outside that window (logs are filed by request start time)\nTip: locate the requestId with listFunctionLogs first.",
5676
5902
  paramRequired: "The {param} parameter is required for the {action} action",
5677
5903
  "deployStatus.cloudMode": "{action} is unavailable in cloud mode: async deployment tasks are only created by real deployments with buildStrategy=cloud/local, and real execution of both strategies is unsupported in cloud mode (they need the local build context or local Docker), so no taskId can exist. buildStrategy=image uses synchronous deployment and returns the result directly from manageFunctions, so no deployment status query is needed. For async image build deployment, switch to local MCP mode.",
5678
5904
  deployTaskNotFound: "Deployment task {taskId} not found; the task does not exist — it may have expired, belong to a different environment than {envId}, or the MCP Server was restarted (tasks are only kept in MCP process memory).",
@@ -5687,7 +5913,11 @@ exports.functions = (0, types_js_1.defineModule)({
5687
5913
  "reason.viewLogs": "View the execution logs of this function",
5688
5914
  "reason.updateConfig": "Update this function's configuration",
5689
5915
  "reason.checkGateway": "Check whether this function has an exposed gateway access URL",
5690
- "reason.logDetail": "View a single log entry by requestId",
5916
+ "reason.logDetail": "View the full logs of that invocation by requestId",
5917
+ "reason.nextLogPage": "Pass context back to fetch the next (older) page of logs",
5918
+ "reason.widenLogRange": "Query this function's logs again with a wider time range",
5919
+ "reason.createLogService": "Enable the environment log service before querying function logs",
5920
+ "reason.searchClsFallback": "Search CLS directly with queryLogs using the same query string",
5691
5921
  "reason.attachLayer": "Attach an additional layer to this function",
5692
5922
  "reason.reorderLayers": "Adjust layer order or the binding list as a whole",
5693
5923
  "reason.layerVersions": "View the version list of a layer",
@@ -5732,6 +5962,10 @@ exports.functions = (0, types_js_1.defineModule)({
5732
5962
  gotFunctionDetail: "Retrieved details of function {fnName}",
5733
5963
  gotFunctionLogs: "Retrieved the log list of function {fnName}",
5734
5964
  gotLogDetail: "Retrieved the log detail for requestId={requestId}",
5965
+ "logs.emptyByFunction": "No logs found for function {fnName} between {start} and {end}. Common causes: the function was not invoked in that window, the logs have passed their retention period, or functionName / qualifier does not match. Widen the time range and retry.",
5966
+ "logs.emptyByRequest": "No logs found for requestId={requestId} between {start} and {end}. Make sure the window covers that invocation (logs are filed by request start time), or use a requestId located through listFunctionLogs.",
5967
+ "logs.serviceNotEnabled": "The log service is not enabled for this environment, so function logs cannot be read. Call manageLogs(action=\"createLogService\", confirm=true) first, then query again.",
5968
+ "logs.queryFailed": "Failed to query function logs: {message}",
5735
5969
  gotFunctionLayers: "Retrieved the layers currently bound to function {fnName}",
5736
5970
  gotLayers: "Retrieved {count} layer records",
5737
5971
  gotLayerVersions: "Retrieved the version list of layer {layerName}",
@@ -5812,16 +6046,17 @@ exports.functions = (0, types_js_1.defineModule)({
5812
6046
  "schema.create.layers": "Layer configuration",
5813
6047
  "schema.manageLayer.name": "Layer name",
5814
6048
  "schema.manageLayer.version": "Layer version number",
5815
- "schema.query.action": "Read-only action type:\n- `listFunctions`: list all CloudBase cloud functions\n- `getFunctionDetail`: get function details (requires functionName)\n- `listFunctionLogs`: query function execution logs (requires functionName)\n- `getFunctionLogDetail`: get log details (requires requestId)\n- `listFunctionLayers`: list layers bound to a function\n- `listLayers`: list all layers (account-level view, including layers from other environments)\n- `listLayerVersions`: list layer versions (Versions, not Version; account-level view)\n- `getLayerVersionDetail`: get layer version details (account-level view)\n- `listFunctionTriggers`: list function triggers (scheduled task / cron / timer configuration)\n- `getFunctionDownloadUrl`: get the function code download URL\n- `getFunctionDeployStatus`: query async deployment status, stage progress, and final result by taskId. Returns data.build, data.deploy, and data.progress. While status=running, data.result and data.error are null and deployment must not be reported as complete. Keep polling until status=succeeded/failed. status=expired means the task exceeded the 2-hour retention limit; cloud deployment may still be running, so confirm with getFunctionDetail. Tasks exist only in MCP process memory; expired tasks or a restarted MCP Server return errorCode=DEPLOY_TASK_NOT_FOUND. Tasks are isolated by environment. This action is unavailable in cloud mode because real cloud/local builds cannot run there, while image deployment is synchronous and creates no taskId.\n- `listVersionByFunction`: list published function versions (aligns with tcb fn list-function-versions / SDK listVersionByFunction; requires functionName)\n- `getFunctionAlias`: get function alias and traffic routing (aligns with tcb fn get-route / SDK getFunctionAlias; requires functionName; aliasName defaults to $DEFAULT)\n- `getFunctionUploadUrl`: get a COS presigned upload URL for a function code package (two-phase ZIP deployment phase A): PUT the code zip to uploadUrl (include the corresponding request headers when uploadHeaders is non-empty), then call manageFunctions createFunction/updateFunctionCode with the code triplet (phase B). functionName is optional and only shapes the upload object key. The uploadUrl carries credential-bound signatures; never log or persist it; phase B verifies that the triplet's bucket and region are this environment's own bucket, so an address is only usable in the environment it was minted for; the address is valid for 300 seconds by default",
6049
+ "schema.query.action": "Read-only action type:\n- `listFunctions`: list all CloudBase cloud functions\n- `getFunctionDetail`: get function details (requires functionName)\n- `listFunctionLogs`: query function execution logs (requires functionName; retrieved via CLS, defaults to the last day, at most 100 entries per call, pass context to page)\n- `getFunctionLogDetail`: get the full log sequence of one invocation (requires requestId, taken from logs[].requestId of listFunctionLogs)\n- `listFunctionLayers`: list layers bound to a function\n- `listLayers`: list all layers (account-level view, including layers from other environments)\n- `listLayerVersions`: list layer versions (Versions, not Version; account-level view)\n- `getLayerVersionDetail`: get layer version details (account-level view)\n- `listFunctionTriggers`: list function triggers (scheduled task / cron / timer configuration)\n- `getFunctionDownloadUrl`: get the function code download URL\n- `getFunctionDeployStatus`: query async deployment status, stage progress, and final result by taskId. Returns data.build, data.deploy, and data.progress. While status=running, data.result and data.error are null and deployment must not be reported as complete. Keep polling until status=succeeded/failed. status=expired means the task exceeded the 2-hour retention limit; cloud deployment may still be running, so confirm with getFunctionDetail. Tasks exist only in MCP process memory; expired tasks or a restarted MCP Server return errorCode=DEPLOY_TASK_NOT_FOUND. Tasks are isolated by environment. This action is unavailable in cloud mode because real cloud/local builds cannot run there, while image deployment is synchronous and creates no taskId.\n- `listVersionByFunction`: list published function versions (aligns with tcb fn list-function-versions / SDK listVersionByFunction; requires functionName)\n- `getFunctionAlias`: get function alias and traffic routing (aligns with tcb fn get-route / SDK getFunctionAlias; requires functionName; aliasName defaults to $DEFAULT)\n- `getFunctionUploadUrl`: get a COS presigned upload URL for a function code package (two-phase ZIP deployment phase A): PUT the code zip to uploadUrl (include the corresponding request headers when uploadHeaders is non-empty), then call manageFunctions createFunction/updateFunctionCode with the code triplet (phase B). functionName is optional and only shapes the upload object key. The uploadUrl carries credential-bound signatures; never log or persist it; phase B verifies that the triplet's bucket and region are this environment's own bucket, so an address is only usable in the environment it was minted for; the address is valid for 300 seconds by default",
5816
6050
  "schema.query.functionName": "CloudBase cloud function name. Required for `getFunctionDetail`, `listFunctionLogs`, `listFunctionLayers`, `listFunctionTriggers`, `getFunctionDownloadUrl`, `listVersionByFunction`, and `getFunctionAlias`. Optional for `getFunctionUploadUrl` (only used to generate the upload object key).",
5817
- "schema.query.limit": "Page size (limit). Optional for list actions; the backend determines the default.",
5818
- "schema.query.offset": "Pagination offset. Optional for list actions; defaults to 0.",
6051
+ "schema.query.limit": "Page size (limit). Optional for list actions; the backend determines the default. Log actions return at most 100 entries and default to 20.",
6052
+ "schema.query.offset": "Pagination offset. Optional for list actions; defaults to 0. For log actions it only works within the first 100 entries — use the returned context to page further back.",
5819
6053
  "schema.query.codeSecret": "Code protection secret used to decrypt function code.",
5820
6054
  "schema.query.revealEnvValues": "Whether `getFunctionDetail` / `listFunctionTriggers` / `listFunctionLayers` returns plaintext environment variable values. Defaults to false: Value is masked as ***, while Key and ValueLength remain, which is sufficient to verify configured variables and changes. true returns plaintext and may expose secrets to the model context; use cautiously. Prefer the console or CLI for plaintext values.",
5821
- "schema.query.startTime": "Log query start time in YYYY-MM-DD HH:mm:ss format (for example, 2024-01-01 00:00:00). The interval to endTime must not exceed one day. Defaults to the most recent day when omitted.",
5822
- "schema.query.endTime": "Log query end time in YYYY-MM-DD HH:mm:ss format (for example, 2024-01-01 23:59:59). The interval from startTime must not exceed one day. Defaults to the current time when omitted.",
5823
- "schema.query.requestId": "Log request ID. Required for `getFunctionLogDetail`; obtain it from `listFunctionLogs`.",
5824
- "schema.query.qualifier": "Function version alias, such as $LATEST or $DEFAULT. Optional for log queries.",
6055
+ "schema.query.startTime": "Log query start time in YYYY-MM-DD HH:mm:ss format (for example, 2024-01-01 00:00:00, interpreted as Beijing time). The interval to endTime must not exceed one day. Defaults to the most recent day when omitted.",
6056
+ "schema.query.endTime": "Log query end time in YYYY-MM-DD HH:mm:ss format (for example, 2024-01-01 23:59:59, interpreted as Beijing time). The interval from startTime must not exceed one day. Defaults to the current time when omitted.",
6057
+ "schema.query.requestId": "Log request ID. Required for `getFunctionLogDetail`; take it from logs[].requestId returned by `listFunctionLogs`, or use the RequestId printed in a business log line.",
6058
+ "schema.query.qualifier": "Function version alias, such as $LATEST or $DEFAULT. Optional for log queries; used as a CLS filter.",
6059
+ "schema.query.context": "Log pagination cursor. Pass the `context` returned by the previous `listFunctionLogs` call back unchanged to fetch the next page; never construct it yourself. null means there are no more results.",
5825
6060
  "schema.query.runtime": "Runtime filter for layer queries, such as Nodejs18.15.",
5826
6061
  "schema.query.searchKey": "Layer name search keyword.",
5827
6062
  "schema.query.layerName": "Layer name. Required for `listLayerVersions` and `getLayerVersionDetail`. Layers use an account-level shared namespace; use the fixed format `{layerName}_{current envId}` (for example, common_cloud1-d9ghadgak3edf6b36).",
@@ -5887,9 +6122,9 @@ exports.gateway = void 0;
5887
6122
  const types_js_1 = __webpack_require__(9921);
5888
6123
  exports.gateway = (0, types_js_1.defineModule)({
5889
6124
  "query.title": "查询 CloudBase 网关",
5890
- "query.description": "CloudBase HTTP 网关统一只读入口(Domain/Route)。查询域名下路径路由及其上游:WEB_SCF/SCF=云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。主键为 Domain + Path;listRoutes / getRoute / listCustomDomains / getPrivilege。getPrivilege 查询 HTTP 网关总开关(enableService)与访问鉴权(enableAuth)状态。实现自定义域名访问前,先 listCustomDomains:若已有自定义域名,优先 createRoute 挂路由(无需证书 ID);仅在没有可用自定义域名时才 bindCustomDomain。",
6125
+ "query.description": "CloudBase HTTP 网关统一只读入口(Domain/Route)。查询域名下路径路由及其上游:WEB_SCF/SCF=云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器。主键为 Domain + Path;listRoutes / getRoute / listCustomDomains / getPrivilege / cachePurgeTask。getPrivilege 查询 HTTP 网关总开关(enableService)与访问鉴权(enableAuth)状态。cachePurgeTask 查询域名缓存清除任务(先 manageGateway purgeCache 拿 TaskId)。实现自定义域名访问前,先 listCustomDomains:若已有自定义域名,优先 createRoute 挂路由(无需证书 ID);仅在没有可用自定义域名时才 bindCustomDomain。",
5891
6126
  "manage.title": "管理 CloudBase 网关",
5892
- "manage.description": "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path 转到上游;enableRoute/disableRoute 启用或禁用已有路由(底层 ModifyHTTPServiceRoute 的 Routes[].Enable,不是 ModifyGatewayRoute)。未传 domain 时用 DomainType=HTTPSERVICE 的 IsDefault 默认 HTTP 域名(形如 *.{region}.app.tcloudbase.com),不会使用静态托管 CDN 域名(*.tcloudbaseapp.com,DomainType=STATIC_STORE)。这是网关默认域上的路径路由,不是 STATIC_STORE 上游绑定;STATIC_STORE 上游必须显式传 upstreamResourceType=STATIC_STORE。关闭静态托管默认域名(*.tcloudbaseapp.com):先 queryGateway(listRoutes) 找到 DomainType=STATIC_STORE 且 IsDefault=true 的 domain,再 manageGateway(action=\"disableRoute\", domain=该域名, path=\"/\");勿用 manageHosting。创建后可用 queryGateway(action=\"listRoutes\") 核对 Domain / DomainType / Path / UpstreamResourceType。上游类型只用一个参数 upstreamResourceType(也可写在 route.upstreamResourceType,route 优先):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器;配合 targetName 或 route.serviceName(云函数名/云托管服务名/静态托管实例名,常见 staticstore)。createRoute 只建网关入口,不改上游权限。enablePathTransmission:默认 false 剥触发路径前缀;true 透传完整路径(CBR 多路由、WEB_SCF 自管子路径常需 true;STATIC_STORE 自定义触发路径映射站点根通常 false)。⚠️ 自定义域名访问:若环境已有自定义域名(先 queryGateway listCustomDomains),优先 createRoute 并显式传入该 domain,无需 certificateId;仅首次绑定全新自定义域名时用 bindCustomDomain(certificateId 可选:未传时按域名自动检索证书,单证书自动选用、多证书返回选择指引)。createRoute / bindCustomDomain 创建前会调用 VerifyHTTPServiceRoute 做归属权等预检(探测→创建);失败时返回 data.checks 与 DNS TXT 指引,配置后重试。CORS/安全域名(浏览器跨域白名单,与本工具的网关自定义域名无关)用 manageEnv(action=addSecurityDomain/removeSecurityDomain)。enableService/authSwitch:HTTP 网关总开关与访问鉴权开关;createRoute 后若访问报 HTTPSERVICE_NONACTIVATED,通常是总开关未开启(用 queryGateway getPrivilege 查询、enableService 开启)。",
6127
+ "manage.description": "CloudBase HTTP 网关统一写入口(Domain/Route)。createRoute/updateRoute/deleteRoute 把域名下的 path 转到上游;enableRoute/disableRoute 启用或禁用已有路由(底层 ModifyHTTPServiceRoute 的 Routes[].Enable,不是 ModifyGatewayRoute)。未传 domain 时用 DomainType=HTTPSERVICE 的 IsDefault 默认 HTTP 域名(形如 *.{region}.app.tcloudbase.com),不会使用静态托管 CDN 域名(*.tcloudbaseapp.com,DomainType=STATIC_STORE)。这是网关默认域上的路径路由,不是 STATIC_STORE 上游绑定;STATIC_STORE 上游必须显式传 upstreamResourceType=STATIC_STORE。关闭静态托管默认域名(*.tcloudbaseapp.com):先 queryGateway(listRoutes) 找到 DomainType=STATIC_STORE 且 IsDefault=true 的 domain,再 manageGateway(action=\"disableRoute\", domain=该域名, path=\"/\");勿用 manageHosting。创建后可用 queryGateway(action=\"listRoutes\") 核对 Domain / DomainType / Path / UpstreamResourceType。上游类型只用一个参数 upstreamResourceType(也可写在 route.upstreamResourceType,route 优先):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器;配合 targetName 或 route.serviceName(云函数名/云托管服务名/静态托管实例名,常见 staticstore/云存储实例名)。createRoute 只建网关入口,不改上游权限。enablePathTransmission:默认 false 剥触发路径前缀;true 透传完整路径(CBR 多路由、WEB_SCF 自管子路径常需 true;STATIC_STORE 自定义触发路径映射站点根通常 false)。⚠️ 自定义域名访问:若环境已有自定义域名(先 queryGateway listCustomDomains),优先 createRoute 并显式传入该 domain,无需 certificateId;仅首次绑定全新自定义域名时用 bindCustomDomain(certificateId 可选:未传时按域名自动检索证书,单证书自动选用、多证书返回选择指引)。createRoute / bindCustomDomain 创建前会调用 VerifyHTTPServiceRoute 做归属权等预检(探测→创建);失败时返回 data.checks 与 DNS TXT 指引,配置后重试。CORS/安全域名(浏览器跨域白名单,与本工具的网关自定义域名无关)用 manageEnv(action=addSecurityDomain/removeSecurityDomain)。enableService/authSwitch:HTTP 网关总开关与访问鉴权开关;createRoute 后若访问报 HTTPSERVICE_NONACTIVATED,通常是总开关未开启(用 queryGateway getPrivilege 查询、enableService 开启)。purgeCache:清除域名缓存(CDN/EO,按 URL/前缀/整站),返回 TaskId 后用 queryGateway cachePurgeTask 查进度。路由级还支持 enableSafeDomain(跨域/合法域名校验)、pathRewrite(路径重写)、qpsPolicy(限频)、extension(自定义请求/响应头与缓存规则);域名级支持 protocol 与 domainExtension。updateRoute 只传要改的字段即可,未传的 CORS/重写/限频/扩展配置会保持原值。",
5893
6128
  "verify.failWithDns": "HTTP 服务域名配置预检未通过:域名归属权校验失败。请按 data.checks 中 ownership.dnsRecords 配置 DNS TXT 记录后重试。",
5894
6129
  "verify.fail": "HTTP 服务域名配置预检未通过。请根据 data.checks 修复问题后重试。",
5895
6130
  "verify.failWithDnsReason": "配置 DNS TXT(主机记录/记录类型/记录值见 data.checks[].dnsRecords)后重新调用本 action",
@@ -5907,7 +6142,7 @@ exports.gateway = (0, types_js_1.defineModule)({
5907
6142
  "privilege.nonActivatedHint": ";若路由创建成功但访问报 HTTPSERVICE_NONACTIVATED(403),请先开启 HTTP 网关",
5908
6143
  "privilege.enableServiceReason": "开启 HTTP 网关总开关(EnableService),开启后路由即可通过默认域访问",
5909
6144
  "defaultDomain.notReady": "环境默认 HTTP 访问域名未就绪或未开通。请先在控制台开通 HTTP 访问服务,或用 queryGateway(action=\"listRoutes\") 确认 Domains 中是否存在 DomainType=HTTPSERVICE 且 IsDefault=true 的域名(形如 *.{region}.app.tcloudbase.com);不要使用静态托管域名(*.tcloudbaseapp.com)。也可以显式传入 domain 后重试 createRoute/updateRoute/deleteRoute。",
5910
- "upstreamTypeRequired": "必须提供 upstreamResourceType(或 route.upstreamResourceType):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。禁止仅凭 targetName/serviceName 推断上游类型。",
6145
+ "upstreamTypeRequired": "必须提供 upstreamResourceType(或 route.upstreamResourceType):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器。禁止仅凭 targetName/serviceName 推断上游类型。",
5911
6146
  "upstreamNameRequired": "必须提供 targetName 或 route.serviceName 作为上游资源名称:云函数名、云托管服务名,或静态托管实例名(常见为 staticstore)。",
5912
6147
  "toggle.pathRequired": "action=enableRoute/disableRoute 时必须提供 path 或 route.path(例如 \"/\" 或 \"/api\")",
5913
6148
  "toggle.notFound": "未找到路径 {path} 的路由{domainSuffix}{upstreamSuffix}。请先用 queryGateway(action=\"listRoutes\") 确认 Domain / Path,再调用 enableRoute/disableRoute。关闭静态托管默认域名(*.tcloudbaseapp.com)时,请显式传 domain=该 STATIC_STORE IsDefault 域名,并通常 path=\"/\"。",
@@ -5973,14 +6208,32 @@ exports.gateway = (0, types_js_1.defineModule)({
5973
6208
  "authSwitch.onMessage": "HTTP 访问服务鉴权开启成功",
5974
6209
  "authSwitch.offMessage": "HTTP 访问服务鉴权关闭成功",
5975
6210
  "authSwitch.verifyReason": "复核 HTTP 网关总开关与访问鉴权状态",
6211
+ "purgeCache.domainRequired": "action=purgeCache 时必须提供 domain",
6212
+ "purgeCache.targetsRequired": "action=purgeCache 时必须提供 targets:PURGE_URL 传完整 URL、PURGE_PREFIX 传路径前缀、PURGE_HOST 传待刷新的域名",
6213
+ "purgeCache.message": "已提交 {count} 个目标的缓存清除任务;CDN/EO 刷新通常数秒到数分钟,请用 queryGateway(action=\"cachePurgeTask\") 按 TaskId 查进度",
6214
+ "purgeCache.verifyReason": "查询缓存清除任务状态",
6215
+ "query.cachePurgeTask": "已获取 {count} 条缓存清除任务记录",
6216
+ "query.cachePurgeTaskDomainRequired": "action=cachePurgeTask 时必须提供 domain",
6217
+ "cache.domainNotFound": "未找到域名 {domain} 的接入配置,无法确定缓存层。请先用 queryGateway(action=\"listRoutes\") 确认域名。",
6218
+ "cache.notManaged": "域名 {domain} 的接入方式是 {accessType},CloudBase 侧没有该域名的 CDN/EO 缓存,缓存刷新不适用(DIRECT 直连与 CUSTOM 自有 CDN 的缓存不由平台托管)。如需刷新,请先把域名切到 CDN 或 EO 接入。",
6219
+ "cache.typeMismatch": "域名 {domain} 的接入方式是 {accessType},不能按 cacheType={requested} 刷新。省略 cacheType 就会按接入方式自动匹配。",
5976
6220
  // Input schema parameter descriptions
5977
- "schema.query.action": "只读操作类型:listRoutes、getRoute、listCustomDomains、getPrivilege。" +
6221
+ "schema.query.action": "只读操作类型:listRoutes、getRoute、listCustomDomains、getPrivilege、cachePurgeTask。" +
5978
6222
  "getPrivilege 无需其他参数,直接返回 HTTP 网关总开关与访问鉴权状态。" +
6223
+ "cachePurgeTask 需要 domain(可选 cacheType/taskId/purgeType/startTime/endTime/offset/limit)。" +
5979
6224
  "自定义域名访问场景先 listCustomDomains 确认是否已有域名可复用。",
5980
6225
  "schema.query.targetName": "上游资源名过滤(UpstreamResourceName):云函数名、云托管服务名,或静态托管实例名(常见 staticstore)。",
5981
6226
  "schema.query.routeId": "路由 ID。getRoute 时可选",
5982
6227
  "schema.query.path": "路由路径。getRoute / listRoutes 过滤时可选",
5983
- "schema.query.domain": "域名。getRoute / listRoutes 过滤时可选",
6228
+ "schema.query.domain": "域名。getRoute / listRoutes 过滤时可选;cachePurgeTask 时必填",
6229
+ "schema.query.cacheType": "缓存层(仅 cachePurgeTask):CDN 或 EO。省略时按域名接入方式自动匹配(EO→EO、CDN→CDN);" +
6230
+ "DIRECT / CUSTOM / NONE 的域没有 CloudBase 托管的缓存,会直接拒绝并说明原因。",
6231
+ "schema.query.taskId": "缓存清除任务 ID(仅 cachePurgeTask,可选过滤)",
6232
+ "schema.query.purgeType": "清除方式过滤(仅 cachePurgeTask):PURGE_URL=按 URL、PURGE_PREFIX=按前缀、PURGE_HOST=整站",
6233
+ "schema.query.startTime": "任务开始时间(仅 cachePurgeTask,可选)。格式 YYYY-MM-DD HH:mm:ss",
6234
+ "schema.query.endTime": "任务结束时间(仅 cachePurgeTask,可选)。格式 YYYY-MM-DD HH:mm:ss",
6235
+ "schema.query.offset": "分页偏移(仅 cachePurgeTask,可选)",
6236
+ "schema.query.limit": "返回数量上限(仅 cachePurgeTask,可选)",
5984
6237
  "schema.manage.action": "写操作:createRoute/updateRoute/deleteRoute 管理路由;enableRoute/disableRoute 启用或禁用已有路由(需 path,建议显式传 domain);" +
5985
6238
  "bindCustomDomain/deleteCustomDomain 管理自定义域名;" +
5986
6239
  "enableService/authSwitch 开关 HTTP 网关总开关与访问鉴权(需配合 enable 参数)。" +
@@ -5990,13 +6243,15 @@ exports.gateway = (0, types_js_1.defineModule)({
5990
6243
  "已有自定义域名时优先 createRoute(domain=已有域名) 实现访问,不必再次 bindCustomDomain / 传入 certificateId;" +
5991
6244
  "bindCustomDomain 仅用于首次绑定新域名(certificateId 可选,未传则按域名自动检索;可选 accessType=DIRECT|CDN|CUSTOM,CUSTOM 需 customCname;普通场景用默认 DIRECT)。" +
5992
6245
  "createRoute/bindCustomDomain 创建前会 VerifyHTTPServiceRoute 预检;失败时按返回的 DNS TXT 指引配置后重试。" +
6246
+ "purgeCache 清除域名缓存:需 domain 与 targets(配 purgeType=PURGE_URL|PURGE_PREFIX|PURGE_HOST,可选 cacheType=CDN|EO)。" +
5993
6247
  "接入说明:https://docs.cloudbase.net/service/custom-domain",
5994
6248
  "schema.manage.targetName": "上游资源名称(UpstreamResourceName),与 route.serviceName 二选一(route 优先)。" +
5995
6249
  "云函数=函数名;云托管=服务名;静态托管=实例名(常见 staticstore)。不会自动推断上游类型。",
5996
6250
  "schema.manage.path": "触发路径(网关匹配前缀),默认 /{上游名}。例:云函数 /api/hello、云托管 /api、静态托管 / 或 /app。" +
5997
6251
  "只建网关入口;与 enablePathTransmission 共同决定上游实际收到的路径。",
5998
6252
  "schema.manage.upstreamResourceType": "上游类型(与 route.upstreamResourceType 二选一,route 优先)。" +
5999
- "WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
6253
+ "WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器。" +
6254
+ "STORAGE 的 UpstreamResourceName 是云存储实例名,可先用 queryStorage 查实例。" +
6000
6255
  "createRoute/updateRoute 必填其一;勿把 manageFunctions 的 type=HTTP|Event 传到本字段。",
6001
6256
  "schema.manage.auth": "网关路径鉴权(EnableAuth)。匿名/浏览器公网访问通常 false。" +
6002
6257
  "只控制网关入口;云函数安全规则、云托管鉴权、静态托管权限需各自工具另行配置。",
@@ -6005,6 +6260,30 @@ exports.gateway = (0, types_js_1.defineModule)({
6005
6260
  "CBR 云托管(Express 等自管子路由)与 WEB_SCF 多路径函数常需 true;" +
6006
6261
  "STATIC_STORE 把触发路径映射到站点根目录(如 /app → 托管 /)时通常 false;" +
6007
6262
  "单入口/根路径处理保持 false。也可用 route.enablePathTransmission(route 优先)。",
6263
+ "schema.manage.enableSafeDomain": "跨域/合法域名校验(EnableSafeDomain),即控制台的「跨域设置」。true 时校验请求来源域名," +
6264
+ "false 时不校验(浏览器直连调用 API 的常见选择)。也可用 route.enableSafeDomain(route 优先)。",
6265
+ "schema.manage.pathRewrite": "路径重写(PathRewrite),形如 {prefix:\"/v1\", staticStorePrefix:\"app\"}。" +
6266
+ "prefix=命中路径前缀后替换为的内容;staticStorePrefix=静态托管场景下的目标前缀。" +
6267
+ "不需要改写就省略;也可用 route.pathRewrite(route 优先)。",
6268
+ "schema.manage.qpsPolicy": "限频(QPSPolicy),即控制台的「频限」。{total:总 QPS, perClient:{limitBy:\"ClientIP\"|\"UserID\", limitValue:单客户端 QPS}}。" +
6269
+ "不传则不限频;也可用 route.qpsPolicy(route 优先)。",
6270
+ "schema.manage.extension": "路由级扩展(Extension),可配自定义请求/响应头与缓存规则。" +
6271
+ "{headersHandler:{requestHeadersToAdd,requestHeadersToRemove,responseHeadersToAdd,responseHeadersToRemove}," +
6272
+ "cache:{rules:[{description,enable,condition:{target,matchType,values},actions:[{type:\"Cache\"|\"CacheKey\",cache,cacheKey}]}]}}。" +
6273
+ "⚠️ 默认域(*.{region}.app.tcloudbase.com)不支持自定义响应头,后端会拒绝(实测 2026-10-21),需要自定义域名才能配。" +
6274
+ "也可用 route.extension(route 优先)。",
6275
+ "schema.manage.extension.header.key": "请求/响应头名称,如 X-Frame-Options",
6276
+ "schema.manage.extension.header.value": "请求/响应头取值,如 SAMEORIGIN",
6277
+ "schema.manage.extension.header.action": "写入策略(可选):APPEND_IF_EXISTS_OR_ADD 追加;ADD_IF_ABSENT 不存在才加;" +
6278
+ "OVERWRITE_IF_EXISTS_OR_ADD 覆盖或新增;OVERWRITE_IF_EXISTS 仅覆盖已存在的",
6279
+ "schema.manage.extension.cacheCondition": "缓存规则匹配条件:target=url_path/file_extension/full_uri,matchType=prefix/suffix/contains/exact,values=匹配值数组",
6280
+ "schema.manage.extension.cacheActions": "缓存动作数组:type=Cache 配 cache{followOrigin,noCache,cacheTime,maxAgeTime}(三者互斥,需开启其一);" +
6281
+ "type=CacheKey 配 cacheKey{fullUrlCache,queryStringSwitch,queryStringAction,queryStringValues}",
6282
+ "schema.manage.domainExtension": "域名级扩展(Extension),作用于整个域名(含该域名下所有路径)的自定义头与缓存规则,结构同 extension。" +
6283
+ "缓存规则通常配在域名级;仅 createRoute/updateRoute/bindCustomDomain 生效。" +
6284
+ "⚠️ 同 extension:默认域不支持自定义响应头,需自定义域名。",
6285
+ "schema.manage.protocol": "域名协议(Protocol),仅 createRoute/updateRoute/bindCustomDomain 生效:" +
6286
+ "HTTP / HTTPS / HTTP_AND_HTTPS / HTTP_TO_HTTPS / HTTPS_TO_HTTP。省略则沿用域名现有配置。",
6008
6287
  "schema.manage.route.serviceName": "上游实例名:云函数名 / 云托管服务名 / 静态托管实例名(常见 staticstore)/ LH 实例。优先于顶层 targetName。",
6009
6288
  "schema.manage.route.upstreamResourceType": "同顶层 upstreamResourceType。route 内设置时优先于顶层。",
6010
6289
  "schema.manage.route.enablePathTransmission": "同顶层 enablePathTransmission。route 内设置时优先于顶层。",
@@ -6012,6 +6291,10 @@ exports.gateway = (0, types_js_1.defineModule)({
6012
6291
  "enable=false 禁用该 Domain+Path(访问返回 GATEWAY_ROUTE_DISABLED);" +
6013
6292
  "enable=true 重新启用。updateRoute 也可用顶层 enable 表达同一语义;" +
6014
6293
  "也可用专用 action enableRoute/disableRoute。route.enable 优先于顶层 enable。",
6294
+ "schema.manage.route.enableSafeDomain": "同顶层 enableSafeDomain(跨域/合法域名校验)。route 内设置时优先于顶层。",
6295
+ "schema.manage.route.pathRewrite": "同顶层 pathRewrite。route 内设置时优先于顶层。",
6296
+ "schema.manage.route.qpsPolicy": "同顶层 qpsPolicy(限频)。route 内设置时优先于顶层。",
6297
+ "schema.manage.route.extension": "同顶层 extension(路由级自定义头与缓存规则)。route 内设置时优先于顶层。",
6015
6298
  "schema.manage.route": "路由对象(可选写法)。例:云函数 {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"};" +
6016
6299
  "云托管 {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"};" +
6017
6300
  "静态托管 {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"};" +
@@ -6036,11 +6319,15 @@ exports.gateway = (0, types_js_1.defineModule)({
6036
6319
  "updateRoute 可选:映射到 Routes[].Enable(也可用 route.enable,route 优先;" +
6037
6320
  "也可用专用 action enableRoute/disableRoute)。" +
6038
6321
  "省略或非布尔值在 enableService/authSwitch 会返回参数错误。",
6322
+ "schema.manage.cacheType": "缓存层(仅 purgeCache):CDN 或 EO。省略时按域名接入方式自动匹配(EO→EO、CDN→CDN);" +
6323
+ "DIRECT / CUSTOM / NONE 的域没有 CloudBase 托管的缓存,会直接拒绝并说明原因。",
6324
+ "schema.manage.purgeType": "清除方式(仅 purgeCache,默认 PURGE_URL):PURGE_URL=按完整 URL、PURGE_PREFIX=按路径前缀、PURGE_HOST=整个域名",
6325
+ "schema.manage.targets": "清除目标(仅 purgeCache,必填):PURGE_URL 传 https://域名/路径 形式;PURGE_PREFIX 传 /static/ 这样的前缀;PURGE_HOST 传域名本身",
6039
6326
  }, {
6040
6327
  "query.title": "Query CloudBase Gateway",
6041
- "query.description": "Unified read-only entry for the CloudBase HTTP gateway (Domain/Route). Query path routes under a domain and their upstreams: WEB_SCF/SCF=cloud functions, CBR=CloudRun, STATIC_STORE=static hosting, LH=Lighthouse. The primary key is Domain + Path; listRoutes / getRoute / listCustomDomains / getPrivilege. getPrivilege queries the HTTP gateway master switch (enableService) and access auth (enableAuth) status. Before setting up custom domain access, run listCustomDomains first: if a custom domain already exists, prefer createRoute to attach a route (no certificate ID needed); use bindCustomDomain only when no usable custom domain exists.",
6328
+ "query.description": "Unified read-only entry for the CloudBase HTTP gateway (Domain/Route). Query path routes under a domain and their upstreams: WEB_SCF/SCF=cloud functions, CBR=CloudRun, STATIC_STORE=static hosting, STORAGE=cloud storage, LH=Lighthouse. The primary key is Domain + Path; listRoutes / getRoute / listCustomDomains / getPrivilege / cachePurgeTask. getPrivilege queries the HTTP gateway master switch (enableService) and access auth (enableAuth) status. cachePurgeTask queries cache purge tasks for a domain (use the TaskId from manageGateway purgeCache). Before setting up custom domain access, run listCustomDomains first: if a custom domain already exists, prefer createRoute to attach a route (no certificate ID needed); use bindCustomDomain only when no usable custom domain exists.",
6042
6329
  "manage.title": "Manage CloudBase Gateway",
6043
- "manage.description": "Unified write entry for the CloudBase HTTP gateway (Domain/Route). createRoute/updateRoute/deleteRoute forward a path under a domain to an upstream; enableRoute/disableRoute enable or disable existing routes (Routes[].Enable of ModifyHTTPServiceRoute underneath, not ModifyGatewayRoute). When domain is omitted, the IsDefault default HTTP domain with DomainType=HTTPSERVICE (like *.{region}.app.tcloudbase.com) is used; static-hosting CDN domains (*.tcloudbaseapp.com, DomainType=STATIC_STORE) are never used. These are path routes on the gateway default domain, not STATIC_STORE upstream bindings; a STATIC_STORE upstream must pass upstreamResourceType=STATIC_STORE explicitly. To disable the static-hosting default domain (*.tcloudbaseapp.com): first queryGateway(listRoutes) to find the domain with DomainType=STATIC_STORE and IsDefault=true, then manageGateway(action=\"disableRoute\", domain=that domain, path=\"/\"); do not use manageHosting. After creation, verify Domain / DomainType / Path / UpstreamResourceType via queryGateway(action=\"listRoutes\"). The upstream type uses a single parameter upstreamResourceType (or route.upstreamResourceType, route wins): WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, LH=Lighthouse; together with targetName or route.serviceName (function name / CloudRun service name / static-hosting instance name, commonly staticstore). createRoute only creates the gateway entry and does not change upstream permissions. enablePathTransmission: default false strips the trigger path prefix; true forwards the full path (CBR multi-route and WEB_SCF self-managed sub-paths often need true; STATIC_STORE custom trigger path mapping to site root usually false). ⚠️ Custom domain access: if the env already has a custom domain (queryGateway listCustomDomains first), prefer createRoute with that domain explicitly, no certificateId needed; use bindCustomDomain only when binding a brand-new custom domain for the first time (certificateId optional: when omitted, certificates are auto-searched by domain — a single cert is auto-selected, multiple certs return selection guidance). createRoute / bindCustomDomain call VerifyHTTPServiceRoute for ownership pre-checks before creation (probe → create); on failure, data.checks and DNS TXT guidance are returned; configure and retry. CORS/security domains (browser cross-origin allowlist, unrelated to the gateway custom domains of this tool) use manageEnv(action=addSecurityDomain/removeSecurityDomain). enableService/authSwitch: HTTP gateway master switch and access auth switch; if access returns HTTPSERVICE_NONACTIVATED after createRoute, the master switch is usually off (query via queryGateway getPrivilege, enable via enableService).",
6330
+ "manage.description": "Unified write entry for the CloudBase HTTP gateway (Domain/Route). createRoute/updateRoute/deleteRoute forward a path under a domain to an upstream; enableRoute/disableRoute enable or disable existing routes (Routes[].Enable of ModifyHTTPServiceRoute underneath, not ModifyGatewayRoute). When domain is omitted, the IsDefault default HTTP domain with DomainType=HTTPSERVICE (like *.{region}.app.tcloudbase.com) is used; static-hosting CDN domains (*.tcloudbaseapp.com, DomainType=STATIC_STORE) are never used. These are path routes on the gateway default domain, not STATIC_STORE upstream bindings; a STATIC_STORE upstream must pass upstreamResourceType=STATIC_STORE explicitly. To disable the static-hosting default domain (*.tcloudbaseapp.com): first queryGateway(listRoutes) to find the domain with DomainType=STATIC_STORE and IsDefault=true, then manageGateway(action=\"disableRoute\", domain=that domain, path=\"/\"); do not use manageHosting. After creation, verify Domain / DomainType / Path / UpstreamResourceType via queryGateway(action=\"listRoutes\"). The upstream type uses a single parameter upstreamResourceType (or route.upstreamResourceType, route wins): WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, STORAGE=cloud storage, LH=Lighthouse; together with targetName or route.serviceName (function name / CloudRun service name / static-hosting instance name, commonly staticstore / cloud storage instance name). createRoute only creates the gateway entry and does not change upstream permissions. enablePathTransmission: default false strips the trigger path prefix; true forwards the full path (CBR multi-route and WEB_SCF self-managed sub-paths often need true; STATIC_STORE custom trigger path mapping to site root usually false). ⚠️ Custom domain access: if the env already has a custom domain (queryGateway listCustomDomains first), prefer createRoute with that domain explicitly, no certificateId needed; use bindCustomDomain only when binding a brand-new custom domain for the first time (certificateId optional: when omitted, certificates are auto-searched by domain — a single cert is auto-selected, multiple certs return selection guidance). createRoute / bindCustomDomain call VerifyHTTPServiceRoute for ownership pre-checks before creation (probe → create); on failure, data.checks and DNS TXT guidance are returned; configure and retry. CORS/security domains (browser cross-origin allowlist, unrelated to the gateway custom domains of this tool) use manageEnv(action=addSecurityDomain/removeSecurityDomain). enableService/authSwitch: HTTP gateway master switch and access auth switch; if access returns HTTPSERVICE_NONACTIVATED after createRoute, the master switch is usually off (query via queryGateway getPrivilege, enable via enableService). purgeCache clears domain cache (CDN/EO by URL/prefix/host) and returns a TaskId; check progress with queryGateway cachePurgeTask. Route-level extras: enableSafeDomain (CORS / legit-domain check), pathRewrite (path rewriting), qpsPolicy (rate limiting), extension (custom request/response headers and cache rules); domain-level: protocol and domainExtension. updateRoute only needs the fields you want to change — untouched CORS/rewrite/QPS/extension settings are carried over from the live route.",
6044
6331
  "verify.failWithDns": "HTTP service domain pre-check failed: domain ownership verification failed. Configure the DNS TXT records per ownership.dnsRecords in data.checks, then retry.",
6045
6332
  "verify.fail": "HTTP service domain pre-check failed. Fix the issues per data.checks, then retry.",
6046
6333
  "verify.failWithDnsReason": "Configure the DNS TXT records (host/record type/record value in data.checks[].dnsRecords), then call this action again",
@@ -6058,7 +6345,7 @@ exports.gateway = (0, types_js_1.defineModule)({
6058
6345
  "privilege.nonActivatedHint": "; if the route is created successfully but access returns HTTPSERVICE_NONACTIVATED (403), enable the HTTP gateway first",
6059
6346
  "privilege.enableServiceReason": "Turn on the HTTP gateway master switch (EnableService); once on, routes are accessible via the default domain",
6060
6347
  "defaultDomain.notReady": "The env default HTTP access domain is not ready or not provisioned. Enable the HTTP access service in the console first, or use queryGateway(action=\"listRoutes\") to confirm that Domains contains a domain with DomainType=HTTPSERVICE and IsDefault=true (shaped like *.{region}.app.tcloudbase.com); do not use static-hosting domains (*.tcloudbaseapp.com). You may also pass domain explicitly and retry createRoute/updateRoute/deleteRoute.",
6061
- "upstreamTypeRequired": "upstreamResourceType (or route.upstreamResourceType) is required: WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, LH=Lighthouse. Do not infer the upstream type from targetName/serviceName alone.",
6348
+ "upstreamTypeRequired": "upstreamResourceType (or route.upstreamResourceType) is required: WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, STORAGE=cloud storage, LH=Lighthouse. Do not infer the upstream type from targetName/serviceName alone.",
6062
6349
  "upstreamNameRequired": "targetName or route.serviceName is required as the upstream resource name: a cloud function name, CloudRun service name, or static-hosting instance name (commonly staticstore).",
6063
6350
  "toggle.pathRequired": "path or route.path is required for action=enableRoute/disableRoute (e.g. \"/\" or \"/api\")",
6064
6351
  "toggle.notFound": "No route found for path {path}{domainSuffix}{upstreamSuffix}. Use queryGateway(action=\"listRoutes\") first to confirm Domain / Path, then call enableRoute/disableRoute. When disabling the static-hosting default domain (*.tcloudbaseapp.com), pass domain=that STATIC_STORE IsDefault domain explicitly, usually with path=\"/\".",
@@ -6124,14 +6411,32 @@ exports.gateway = (0, types_js_1.defineModule)({
6124
6411
  "authSwitch.onMessage": "HTTP access service auth enabled successfully",
6125
6412
  "authSwitch.offMessage": "HTTP access service auth disabled successfully",
6126
6413
  "authSwitch.verifyReason": "Verify the HTTP gateway master switch and access auth status",
6414
+ "purgeCache.domainRequired": "domain is required for action=purgeCache",
6415
+ "purgeCache.targetsRequired": "targets is required for action=purgeCache: full URLs for PURGE_URL, path prefixes for PURGE_PREFIX, or the domain itself for PURGE_HOST",
6416
+ "purgeCache.message": "Submitted a cache purge task for {count} target(s); CDN/EO purges usually take seconds to minutes — check progress with queryGateway(action=\"cachePurgeTask\") by TaskId",
6417
+ "purgeCache.verifyReason": "Query the cache purge task status",
6418
+ "query.cachePurgeTask": "Retrieved {count} cache purge task record(s)",
6419
+ "query.cachePurgeTaskDomainRequired": "domain is required for action=cachePurgeTask",
6420
+ "cache.domainNotFound": "No access configuration found for domain {domain}, so the cache layer is undetermined. Confirm the domain with queryGateway(action=\"listRoutes\") first.",
6421
+ "cache.notManaged": "Domain {domain} uses access type {accessType}; CloudBase does not host a CDN/EO cache for it, so cache purging does not apply (DIRECT and CUSTOM own-CDN caches are not managed by the platform). Switch the domain to CDN or EO access first.",
6422
+ "cache.typeMismatch": "Domain {domain} uses access type {accessType} and cannot be purged with cacheType={requested}. Omit cacheType to match the access type automatically.",
6127
6423
  // Input schema parameter descriptions
6128
- "schema.query.action": "Read-only action: listRoutes, getRoute, listCustomDomains, getPrivilege. " +
6424
+ "schema.query.action": "Read-only action: listRoutes, getRoute, listCustomDomains, getPrivilege, cachePurgeTask. " +
6129
6425
  "getPrivilege needs no other parameter and returns the HTTP gateway master switch and access auth status directly. " +
6426
+ "cachePurgeTask requires domain (optional cacheType/taskId/purgeType/startTime/endTime/offset/limit). " +
6130
6427
  "For custom domain access, run listCustomDomains first to check whether an existing domain can be reused.",
6131
6428
  "schema.query.targetName": "Upstream resource name filter (UpstreamResourceName): a cloud function name, CloudRun service name, or static-hosting instance name (commonly staticstore).",
6132
6429
  "schema.query.routeId": "Route ID. Optional for getRoute",
6133
6430
  "schema.query.path": "Route path. Optional as a getRoute / listRoutes filter",
6134
- "schema.query.domain": "Domain. Optional as a getRoute / listRoutes filter",
6431
+ "schema.query.domain": "Domain. Optional as a getRoute / listRoutes filter; required for cachePurgeTask",
6432
+ "schema.query.cacheType": "Cache layer (cachePurgeTask only): CDN or EO. When omitted it is matched from the domain access type (EO→EO, CDN→CDN); " +
6433
+ "DIRECT / CUSTOM / NONE domains have no CloudBase-hosted cache and are rejected with an explanation.",
6434
+ "schema.query.taskId": "Cache purge task ID (cachePurgeTask only, optional filter)",
6435
+ "schema.query.purgeType": "Purge type filter (cachePurgeTask only): PURGE_URL=by URL, PURGE_PREFIX=by path prefix, PURGE_HOST=whole domain",
6436
+ "schema.query.startTime": "Task start time (cachePurgeTask only, optional). Format YYYY-MM-DD HH:mm:ss",
6437
+ "schema.query.endTime": "Task end time (cachePurgeTask only, optional). Format YYYY-MM-DD HH:mm:ss",
6438
+ "schema.query.offset": "Pagination offset (cachePurgeTask only, optional)",
6439
+ "schema.query.limit": "Max records to return (cachePurgeTask only, optional)",
6135
6440
  "schema.manage.action": "Write actions: createRoute/updateRoute/deleteRoute manage routes; enableRoute/disableRoute enable or disable an existing route (path required, passing domain explicitly is recommended); " +
6136
6441
  "bindCustomDomain/deleteCustomDomain manage custom domains; " +
6137
6442
  "enableService/authSwitch toggle the HTTP gateway master switch and access auth (requires the enable parameter). " +
@@ -6141,13 +6446,15 @@ exports.gateway = (0, types_js_1.defineModule)({
6141
6446
  "When a custom domain already exists, prefer createRoute(domain=existing domain) for access instead of calling bindCustomDomain / passing certificateId again; " +
6142
6447
  "bindCustomDomain is only for binding a new domain the first time (certificateId is optional — when omitted, certificates are searched by domain; optional accessType=DIRECT|CDN|CUSTOM, CUSTOM requires customCname; use the default DIRECT for normal cases). " +
6143
6448
  "createRoute/bindCustomDomain run a VerifyHTTPServiceRoute pre-check before creation; on failure, follow the returned DNS TXT guidance and retry. " +
6449
+ "purgeCache clears domain cache: requires domain and targets (with purgeType=PURGE_URL|PURGE_PREFIX|PURGE_HOST, optional cacheType=CDN|EO). " +
6144
6450
  "Setup guide: https://docs.cloudbase.net/service/custom-domain",
6145
6451
  "schema.manage.targetName": "Upstream resource name (UpstreamResourceName); use either this or route.serviceName (route wins). " +
6146
6452
  "Cloud function=function name; CloudRun=service name; static hosting=instance name (commonly staticstore). The upstream type is never inferred automatically.",
6147
6453
  "schema.manage.path": "Trigger path (the gateway match prefix), defaults to /{upstream name}. Examples: cloud function /api/hello, CloudRun /api, static hosting / or /app. " +
6148
6454
  "This only creates the gateway entry; together with enablePathTransmission it determines the path the upstream actually receives.",
6149
6455
  "schema.manage.upstreamResourceType": "Upstream type (use either this or route.upstreamResourceType, route wins). " +
6150
- "WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, LH=Lighthouse. " +
6456
+ "WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, STORAGE=cloud storage, LH=Lighthouse. " +
6457
+ "For STORAGE, UpstreamResourceName is the cloud storage instance name — look it up with queryStorage first. " +
6151
6458
  "One of them is required for createRoute/updateRoute; do not pass the manageFunctions type=HTTP|Event value into this field.",
6152
6459
  "schema.manage.auth": "Gateway path auth (EnableAuth). Usually false for anonymous/browser public access. " +
6153
6460
  "It only controls the gateway entry; cloud function security rules, CloudRun auth, and static hosting permissions must be configured with their own tools.",
@@ -6156,6 +6463,31 @@ exports.gateway = (0, types_js_1.defineModule)({
6156
6463
  "CBR CloudRun (Express and similar self-managed sub-routes) and multi-path WEB_SCF functions often need true; " +
6157
6464
  "STATIC_STORE mapping a trigger path to the site root (e.g. /app → hosting /) is usually false; " +
6158
6465
  "keep false for single-entry/root-path handling. route.enablePathTransmission also works (route wins).",
6466
+ "schema.manage.enableSafeDomain": "CORS / legit-domain check (EnableSafeDomain), the console's \"cross-origin settings\". " +
6467
+ "true validates the request origin; false skips the check (common for browser-to-API calls). " +
6468
+ "route.enableSafeDomain also works (route wins).",
6469
+ "schema.manage.pathRewrite": "Path rewriting (PathRewrite), e.g. {prefix:\"/v1\", staticStorePrefix:\"app\"}. " +
6470
+ "prefix replaces the matched path prefix; staticStorePrefix is the target prefix for static hosting. " +
6471
+ "Omit when no rewriting is needed; route.pathRewrite also works (route wins).",
6472
+ "schema.manage.qpsPolicy": "Rate limiting (QPSPolicy), the console's \"frequency limit\". {total: total QPS, perClient:{limitBy:\"ClientIP\"|\"UserID\", limitValue: per-client QPS}}. " +
6473
+ "Omit for no limit; route.qpsPolicy also works (route wins).",
6474
+ "schema.manage.extension": "Route-level extension (Extension): custom request/response headers and cache rules. " +
6475
+ "{headersHandler:{requestHeadersToAdd,requestHeadersToRemove,responseHeadersToAdd,responseHeadersToRemove}," +
6476
+ "cache:{rules:[{description,enable,condition:{target,matchType,values},actions:[{type:\"Cache\"|\"CacheKey\",cache,cacheKey}]}]}}. " +
6477
+ "⚠️ The default domain (*.{region}.app.tcloudbase.com) rejects custom response headers (verified 2026-10-21); a custom domain is required. " +
6478
+ "route.extension also works (route wins).",
6479
+ "schema.manage.extension.header.key": "Header name, e.g. X-Frame-Options",
6480
+ "schema.manage.extension.header.value": "Header value, e.g. SAMEORIGIN",
6481
+ "schema.manage.extension.header.action": "Write strategy (optional): APPEND_IF_EXISTS_OR_ADD appends; ADD_IF_ABSENT only adds when missing; " +
6482
+ "OVERWRITE_IF_EXISTS_OR_ADD overwrites or adds; OVERWRITE_IF_EXISTS only overwrites existing",
6483
+ "schema.manage.extension.cacheCondition": "Cache rule match condition: target=url_path/file_extension/full_uri, matchType=prefix/suffix/contains/exact, values=match value list",
6484
+ "schema.manage.extension.cacheActions": "Cache action list: type=Cache with cache{followOrigin,noCache,cacheTime,maxAgeTime} (mutually exclusive — enable one); " +
6485
+ "type=CacheKey with cacheKey{fullUrlCache,queryStringSwitch,queryStringAction,queryStringValues}",
6486
+ "schema.manage.domainExtension": "Domain-level extension (Extension) applied to the whole domain (all paths): custom headers and cache rules, same shape as extension. " +
6487
+ "Cache rules usually belong here; only effective for createRoute/updateRoute/bindCustomDomain. " +
6488
+ "⚠️ Same as extension: the default domain rejects custom response headers; a custom domain is required.",
6489
+ "schema.manage.protocol": "Domain protocol (Protocol), effective for createRoute/updateRoute/bindCustomDomain only: " +
6490
+ "HTTP / HTTPS / HTTP_AND_HTTPS / HTTP_TO_HTTPS / HTTPS_TO_HTTP. Omit to keep the domain's current setting.",
6159
6491
  "schema.manage.route.serviceName": "Upstream instance name: cloud function name / CloudRun service name / static-hosting instance name (commonly staticstore) / LH instance. Takes precedence over the top-level targetName.",
6160
6492
  "schema.manage.route.upstreamResourceType": "Same as the top-level upstreamResourceType. When set inside route, it takes precedence over the top level.",
6161
6493
  "schema.manage.route.enablePathTransmission": "Same as the top-level enablePathTransmission. When set inside route, it takes precedence over the top level.",
@@ -6163,6 +6495,10 @@ exports.gateway = (0, types_js_1.defineModule)({
6163
6495
  "enable=false disables this Domain+Path (access returns GATEWAY_ROUTE_DISABLED); " +
6164
6496
  "enable=true re-enables it. updateRoute can express the same semantics with the top-level enable; " +
6165
6497
  "the dedicated actions enableRoute/disableRoute also work. route.enable takes precedence over the top-level enable.",
6498
+ "schema.manage.route.enableSafeDomain": "Same as the top-level enableSafeDomain (CORS / legit-domain check). When set inside route, it takes precedence.",
6499
+ "schema.manage.route.pathRewrite": "Same as the top-level pathRewrite. When set inside route, it takes precedence.",
6500
+ "schema.manage.route.qpsPolicy": "Same as the top-level qpsPolicy (rate limiting). When set inside route, it takes precedence.",
6501
+ "schema.manage.route.extension": "Same as the top-level extension (route-level custom headers and cache rules). When set inside route, it takes precedence.",
6166
6502
  "schema.manage.route": "Route object (an optional form). Examples: cloud function {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"}; " +
6167
6503
  "CloudRun {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"}; " +
6168
6504
  "static hosting {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"}; " +
@@ -6187,6 +6523,10 @@ exports.gateway = (0, types_js_1.defineModule)({
6187
6523
  "optional for updateRoute: maps to Routes[].Enable (route.enable also works and wins; " +
6188
6524
  "the dedicated actions enableRoute/disableRoute are available too). " +
6189
6525
  "Omitting it or passing a non-boolean returns a parameter error for enableService/authSwitch.",
6526
+ "schema.manage.cacheType": "Cache layer (purgeCache only): CDN or EO. When omitted it is matched from the domain access type (EO→EO, CDN→CDN); " +
6527
+ "DIRECT / CUSTOM / NONE domains have no CloudBase-hosted cache and are rejected with an explanation.",
6528
+ "schema.manage.purgeType": "Purge type (purgeCache only, default PURGE_URL): PURGE_URL=by full URL, PURGE_PREFIX=by path prefix, PURGE_HOST=whole domain",
6529
+ "schema.manage.targets": "Purge targets (purgeCache only, required): full https://domain/path URLs for PURGE_URL; prefixes such as /static/ for PURGE_PREFIX; the domain itself for PURGE_HOST",
6190
6530
  });
6191
6531
 
6192
6532
 
@@ -9361,7 +9701,7 @@ __webpack_unused_export__ = buildEnvCreditsHtml;
9361
9701
  exports.registerEnvCreditsResource = registerEnvCreditsResource;
9362
9702
  const mcp_js_1 = __webpack_require__(4619);
9363
9703
  const index_js_1 = __webpack_require__(4908);
9364
- const cloudbase_manager_js_1 = __webpack_require__(3109);
9704
+ const env_binding_js_1 = __webpack_require__(1498);
9365
9705
  const site_map_js_1 = __webpack_require__(4533);
9366
9706
  const logger_js_1 = __webpack_require__(781);
9367
9707
  const telemetry_js_1 = __webpack_require__(5198);
@@ -9431,17 +9771,12 @@ function buildEnvCreditsConsoleUrl(key, envId, site) {
9431
9771
  return (0, site_map_js_1.getConsoleDevUrl)(envId ?? undefined, exports.BL[key], site);
9432
9772
  }
9433
9773
  /**
9434
- * 从 server 实例派生静态兜底值。
9435
- *
9436
- * 环境 ID 的取值优先级(与 get-started 同口径,新增来源时同步两处):
9437
- * ① 本次工具结果携带(面板侧覆盖,见 EnvCreditsPayload)
9438
- * → ② `server.cloudBaseOptions.envId` / `process.env.CLOUDBASE_ENV_ID`
9439
- * → ③ 进程缓存 `getCachedEnvId()`
9440
- * → ④ 降级:不显示 ID
9441
- * hosted 是无状态的,进程缓存不可靠,因此 ③ 只是本地兜底;面板永远以工具结果为准。
9774
+ * Static fallback until a tool result arrives.
9775
+ * Explicit `cloudBaseOptions.envId` wins. Otherwise the current slot.
9776
+ * Does not read `CLOUDBASE_ENV_ID` or the process cache.
9442
9777
  */
9443
9778
  function resolveEnvCreditsFallback(server) {
9444
- const envId = server.cloudBaseOptions?.envId || process.env.CLOUDBASE_ENV_ID || (0, cloudbase_manager_js_1.getCachedEnvId)() || null;
9779
+ const envId = (0, env_binding_js_1.readDisplayedEnvId)(server.cloudBaseOptions?.envId);
9445
9780
  const { site, region } = (0, site_map_js_1.resolveSiteAndRegion)({
9446
9781
  site: server.cloudBaseOptions?.site,
9447
9782
  region: server.cloudBaseOptions?.region,
@@ -10602,7 +10937,7 @@ exports.registerGetStartedResource = registerGetStartedResource;
10602
10937
  * 注入;`t()` 在 read 回调里求值,晚于 `setInstanceLang`,英文实例自动出英文。
10603
10938
  * 注入分两路:HTML 文本走 `escapeHtml`,脚本里的字符串走 `jsLiteral`(JSON 转义 +
10604
10939
  * 转义 `<`,避免 `</script>` 截断)。
10605
- * 3. **只读无密钥**:状态值只来自 `server.cloudBaseOptions` / 环境变量 / 缓存 / 站点解析,
10940
+ * 3. **只读无密钥**:环境 ID 来自这次调用槽;槽是空的才用 `server.cloudBaseOptions.envId`。不读环境变量和缓存。站点仍走站点解析。
10606
10941
  * 读资源时不发网络请求;面板自己只会发两类调用——只读的
10607
10942
  * `auth(action:"status")`(「重新检查」)和只改本地绑定的
10608
10943
  * `auth(action:"set_env")`(点「可选环境」里的一行)。两者都不动云端资源,也不碰密钥。
@@ -10623,7 +10958,7 @@ exports.registerGetStartedResource = registerGetStartedResource;
10623
10958
  */
10624
10959
  const mcp_js_1 = __webpack_require__(4619);
10625
10960
  const index_js_1 = __webpack_require__(4908);
10626
- const cloudbase_manager_js_1 = __webpack_require__(3109);
10961
+ const env_binding_js_1 = __webpack_require__(1498);
10627
10962
  const site_map_js_1 = __webpack_require__(4533);
10628
10963
  const logger_js_1 = __webpack_require__(781);
10629
10964
  const telemetry_js_1 = __webpack_require__(5198);
@@ -10667,12 +11002,9 @@ function escapeHtml(value) {
10667
11002
  function jsLiteral(value) {
10668
11003
  return JSON.stringify(value).replace(/</g, "\\u003c");
10669
11004
  }
10670
- /** 从 server 实例派生静态兜底状态;只读本地状态,不发起任何请求。 */
11005
+ /** Static fallback for the panel. Reads the current slot, then this server's explicit envId. No network. */
10671
11006
  function resolveGetStartedStatus(server) {
10672
- const envId = server.cloudBaseOptions?.envId ||
10673
- process.env.CLOUDBASE_ENV_ID ||
10674
- (0, cloudbase_manager_js_1.getCachedEnvId)() ||
10675
- null;
11007
+ const envId = (0, env_binding_js_1.readDisplayedEnvId)(server.cloudBaseOptions?.envId);
10676
11008
  const { site, region } = (0, site_map_js_1.resolveSiteAndRegion)({
10677
11009
  site: server.cloudBaseOptions?.site,
10678
11010
  region: server.cloudBaseOptions?.region,
@@ -11680,6 +12012,7 @@ const index_js_1 = __webpack_require__(4908);
11680
12012
  const schema_js_1 = __webpack_require__(6273);
11681
12013
  const server_instructions_js_1 = __webpack_require__(1808);
11682
12014
  const cloud_mode_js_1 = __webpack_require__(8694);
12015
+ const env_binding_js_1 = __webpack_require__(1498);
11683
12016
  const logger_js_1 = __webpack_require__(781);
11684
12017
  const site_map_js_1 = __webpack_require__(4533);
11685
12018
  const tool_result_js_1 = __webpack_require__(9645);
@@ -11868,6 +12201,8 @@ async function createCloudBaseMcpServer(options) {
11868
12201
  });
11869
12202
  // 初始化 toolDefs,用于外部提取工具列表(如微信 IDE)
11870
12203
  server.toolDefs = [];
12204
+ const envSlot = { binding: null };
12205
+ server.envSlot = envSlot;
11871
12206
  // 实例语言解析(须在工具注册前完成:registerTool 包装层按 lang 解析词典 key 形式的 description)
11872
12207
  server.lang = instanceLang;
11873
12208
  // 同步到 i18n 模块级 instanceLang:否则 t() 无 langOverride 时会退化成
@@ -12025,6 +12360,23 @@ async function createCloudBaseMcpServer(options) {
12025
12360
  // 产物卡片:挂在三个部署类工具上,只读展示部署状态与访问地址,写意图一律交给会话。
12026
12361
  (0, artifact_card_js_1.registerArtifactCardResource)(server);
12027
12362
  }
12363
+ const originalConnect = server.connect.bind(server);
12364
+ server.connect = async (transport) => {
12365
+ await originalConnect(transport);
12366
+ const deliver = transport.onmessage;
12367
+ if (!deliver) {
12368
+ return;
12369
+ }
12370
+ transport.onmessage = (message, extra) => {
12371
+ if ((0, env_binding_js_1.hasEnvSlot)()) {
12372
+ deliver(message, extra);
12373
+ return;
12374
+ }
12375
+ (0, env_binding_js_1.runWithEnvSlot)(envSlot, () => {
12376
+ deliver(message, extra);
12377
+ });
12378
+ };
12379
+ };
12028
12380
  return server;
12029
12381
  }
12030
12382
  /**
@@ -24062,6 +24414,7 @@ var __webpack_unused_export__;
24062
24414
  * - Error handling and telemetry reporting
24063
24415
  */
24064
24416
  __webpack_unused_export__ = ({ value: true });
24417
+ exports.TCB_PURCHASE_URL = void 0;
24065
24418
  exports.checkAndInitTcbService = checkAndInitTcbService;
24066
24419
  exports.checkAndCreateFreeEnv = checkAndCreateFreeEnv;
24067
24420
  __webpack_unused_export__ = getUinForTelemetry;
@@ -24069,6 +24422,17 @@ const auth_js_1 = __webpack_require__(561);
24069
24422
  const index_js_1 = __webpack_require__(4908);
24070
24423
  const logger_js_1 = __webpack_require__(781);
24071
24424
  const telemetry_js_1 = __webpack_require__(5198);
24425
+ /**
24426
+ * 开通 CloudBase 环境的购买页。
24427
+ *
24428
+ * ⚠️ 别再写回 `/lowcode?buyType=tcb&channel=mcp`:那条路径现在 302 到 `/CloudBase`,
24429
+ * **重定向时把 query 全丢了**(实测 `location: https://buy.cloud.tencent.com/CloudBase`),
24430
+ * 用户会落到购买页首页、来源标记也一起丢。
24431
+ *
24432
+ * ⚠️ `region` / `sqlType` 是写死的:国内站默认 ap-shanghai + PG 版。国际站
24433
+ * (`buy.tencentcloud.com`)的域名与路径同构性未验证,见 `specs/site-domain-governance`。
24434
+ */
24435
+ exports.TCB_PURCHASE_URL = "https://buy.cloud.tencent.com/CloudBase?buyType=tcb&region=ap-shanghai&sqlType=pg";
24072
24436
  /**
24073
24437
  * Parse InitTcb error and extract user-friendly information
24074
24438
  */
@@ -24091,7 +24455,7 @@ function parseInitTcbError(err) {
24091
24455
  const errorInfo = {
24092
24456
  code: errorCode,
24093
24457
  message: friendlyMessage,
24094
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp",
24458
+ helpUrl: exports.TCB_PURCHASE_URL,
24095
24459
  actionText,
24096
24460
  requestId
24097
24461
  };
@@ -24327,7 +24691,7 @@ async function checkAndCreateFreeEnv(cloudbase, context) {
24327
24691
  newContext.createEnvError = {
24328
24692
  code: "NoPromotionalActivity",
24329
24693
  message: (0, index_js_1.t)("envSetup.noPromotionalActivity"),
24330
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp"
24694
+ helpUrl: exports.TCB_PURCHASE_URL
24331
24695
  };
24332
24696
  return {
24333
24697
  success: false,
@@ -24393,7 +24757,7 @@ async function checkAndCreateFreeEnv(cloudbase, context) {
24393
24757
  newContext.createEnvError = {
24394
24758
  code: "InvalidEnvId",
24395
24759
  message: (0, index_js_1.t)("envSetup.invalidEnvId"),
24396
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp"
24760
+ helpUrl: exports.TCB_PURCHASE_URL
24397
24761
  };
24398
24762
  return {
24399
24763
  success: false,
@@ -24454,7 +24818,7 @@ async function checkAndCreateFreeEnv(cloudbase, context) {
24454
24818
  newContext.createEnvError = {
24455
24819
  code: createErr.code || createErr.Code || "UnknownError",
24456
24820
  message: errorMessage,
24457
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp"
24821
+ helpUrl: exports.TCB_PURCHASE_URL
24458
24822
  };
24459
24823
  (0, logger_js_1.debug)('[env-setup] CreateFreeEnvByActivity failed:', {
24460
24824
  code: newContext.createEnvError.code,
@@ -24553,12 +24917,14 @@ const cloudbase_manager_js_1 = __webpack_require__(3109);
24553
24917
  const logger_js_1 = __webpack_require__(781);
24554
24918
  const site_map_js_1 = __webpack_require__(4533);
24555
24919
  const project_config_js_1 = __webpack_require__(9175);
24920
+ const describe_envs_query_js_1 = __webpack_require__(1284);
24556
24921
  const index_js_1 = __webpack_require__(4908);
24557
24922
  const tool_result_js_1 = __webpack_require__(9645);
24558
24923
  const gateway_access_urls_js_1 = __webpack_require__(4173);
24559
24924
  const env_setup_js_1 = __webpack_require__(7528);
24560
24925
  const env_credits_payload_js_1 = __webpack_require__(3880);
24561
24926
  const mcp_ui_js_1 = __webpack_require__(5880);
24927
+ const env_setup_js_2 = __webpack_require__(7528);
24562
24928
  /**
24563
24929
  * Resources accepted by CreateEnv. Matches Cloud API / Manager SDK contract.
24564
24930
  * `postgresql` enables CloudBase PostgreSQL (PG mode) when supported by the package.
@@ -25347,7 +25713,7 @@ async function prepareAuthEnvironment(params) {
25347
25713
  reason: "tcb_init_failed",
25348
25714
  errorCode: "TCB_INIT_FAILED",
25349
25715
  message: (0, index_js_1.t)("env.prepare.tcbInitFailed"),
25350
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp",
25716
+ helpUrl: env_setup_js_2.TCB_PURCHASE_URL,
25351
25717
  });
25352
25718
  return {
25353
25719
  currentEnvId: null,
@@ -25400,7 +25766,7 @@ async function prepareAuthEnvironment(params) {
25400
25766
  reason: "env_creation_failed",
25401
25767
  errorCode: "ENV_CREATION_FAILED",
25402
25768
  message: (0, index_js_1.t)("env.prepare.envCreateFailed"),
25403
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp",
25769
+ helpUrl: env_setup_js_2.TCB_PURCHASE_URL,
25404
25770
  });
25405
25771
  const failureMessage = userNotice
25406
25772
  ? `${userNotice}\n${createFailure.message}`
@@ -26499,42 +26865,68 @@ function registerEnvTools(server) {
26499
26865
  // API Key mode: exchange for temporary credentials
26500
26866
  const apiKeyFromEnv = (0, auth_js_1.getCloudBaseApiKeyFromEnv)();
26501
26867
  if (apiKeyFromEnv && process.env.CLOUDBASE_ENV_ID) {
26868
+ // 项目级 key 与账号登录并存时,项目级优先——设备码/网页授权即使走完也不生效,
26869
+ // 所以短路段必须说清「怎么切」,否则用户会以为换号成功
26870
+ const removable = (0, auth_js_1.isEnvCredentialRemovableByLogout)();
26871
+ const apiKeyEnvId = process.env.CLOUDBASE_ENV_ID;
26872
+ const switchHint = "\n" +
26873
+ (removable
26874
+ ? (0, index_js_1.t)("env.auth.apiKeySwitchHintRemovable", undefined, outLang)
26875
+ : (0, index_js_1.t)("env.auth.apiKeySwitchHintExternal", {
26876
+ varName: process.env.CLOUDBASE_API_KEY
26877
+ ? "CLOUDBASE_API_KEY"
26878
+ : "CLOUDBASE_APIKEY",
26879
+ }, outLang));
26880
+ const logoutNextStep = removable
26881
+ ? (0, tool_result_js_1.buildAuthNextStep)("logout", {
26882
+ suggestedArgs: { action: "logout", confirm: "yes" },
26883
+ })
26884
+ : undefined;
26885
+ // peek 发现回填的那份已失效时会把 env 清掉:那就不是「登录失败」,
26886
+ // 而是「旧 key 已经作废」,直接往下走正常登录流程
26887
+ let adoptedKeyDropped = false;
26502
26888
  try {
26503
26889
  const existingLoginState = await (0, auth_js_1.peekLoginState)();
26504
26890
  if (existingLoginState) {
26505
26891
  return (0, tool_result_js_1.buildJsonToolResult)({
26506
26892
  ok: true,
26507
26893
  code: "AUTH_READY",
26508
- message: await appendApiKeyCamWarningIfNeeded(existingLoginState, (0, index_js_1.t)("env.auth.apiKeyAutoLogin", undefined, outLang)),
26894
+ message: (await appendApiKeyCamWarningIfNeeded(existingLoginState, (0, index_js_1.t)("env.auth.apiKeyAutoLogin", undefined, outLang))) + switchHint,
26509
26895
  auth_mode: "api_key",
26510
- envId: process.env.CLOUDBASE_ENV_ID,
26896
+ envId: apiKeyEnvId,
26897
+ ...(logoutNextStep ? { next_step: logoutNextStep } : {}),
26511
26898
  });
26512
26899
  }
26900
+ adoptedKeyDropped = !(0, auth_js_1.getCloudBaseApiKeyFromEnv)();
26513
26901
  }
26514
26902
  catch (e) {
26515
26903
  // peekLoginState threw; log and fall through to diagnostic
26516
26904
  (0, logger_js_1.debug)("start_auth: peekLoginState threw", { error: e instanceof Error ? e.message : String(e) });
26517
26905
  }
26518
- // API Key exchange failed: return diagnostic details
26519
- const exchangeRegion = (0, site_map_js_1.resolveApiKeyExchangeRegion)();
26520
- const endpoint = process.env.CLOUDBASE_API_ENDPOINT ||
26521
- `https://${process.env.CLOUDBASE_ENV_ID}.${exchangeRegion ?? "ap-shanghai"}.tcb-api.tencentcloudapi.com`;
26522
- const diagMessage = (0, index_js_1.t)("env.auth.apiKeyEnvExchangeFailed", undefined, outLang) +
26523
- (0, index_js_1.t)("env.auth.apiKeyDiagEnv", {
26524
- envId: String(process.env.CLOUDBASE_ENV_ID),
26525
- apiKeyPrefix: apiKeyFromEnv.slice(0, 20),
26526
- site: process.env.TCB_SITE || (0, index_js_1.t)("env.auth.siteUnset", undefined, outLang),
26527
- gatewayRegion: exchangeRegion ?? (0, index_js_1.t)("env.auth.gatewayRegionDefault", undefined, outLang),
26906
+ if (!adoptedKeyDropped) {
26907
+ // API Key exchange failed: return diagnostic details
26908
+ const exchangeRegion = (0, site_map_js_1.resolveApiKeyExchangeRegion)();
26909
+ const endpoint = process.env.CLOUDBASE_API_ENDPOINT ||
26910
+ `https://${apiKeyEnvId}.${exchangeRegion ?? "ap-shanghai"}.tcb-api.tencentcloudapi.com`;
26911
+ const diagMessage = (0, index_js_1.t)("env.auth.apiKeyEnvExchangeFailed", undefined, outLang) +
26912
+ (0, index_js_1.t)("env.auth.apiKeyDiagEnv", {
26913
+ envId: String(apiKeyEnvId),
26914
+ apiKeyPrefix: apiKeyFromEnv.slice(0, 20),
26915
+ site: process.env.TCB_SITE || (0, index_js_1.t)("env.auth.siteUnset", undefined, outLang),
26916
+ gatewayRegion: exchangeRegion ?? (0, index_js_1.t)("env.auth.gatewayRegionDefault", undefined, outLang),
26917
+ endpoint,
26918
+ }, outLang) +
26919
+ switchHint;
26920
+ return (0, tool_result_js_1.buildJsonToolResult)({
26921
+ ok: false,
26922
+ code: "API_KEY_AUTH_FAILED",
26923
+ message: diagMessage,
26924
+ auth_mode: "api_key",
26925
+ envId: apiKeyEnvId,
26528
26926
  endpoint,
26529
- }, outLang);
26530
- return (0, tool_result_js_1.buildJsonToolResult)({
26531
- ok: false,
26532
- code: "API_KEY_AUTH_FAILED",
26533
- message: diagMessage,
26534
- auth_mode: "api_key",
26535
- envId: process.env.CLOUDBASE_ENV_ID,
26536
- endpoint,
26537
- });
26927
+ ...(logoutNextStep ? { next_step: logoutNextStep } : {}),
26928
+ });
26929
+ }
26538
26930
  }
26539
26931
  const region = server.cloudBaseOptions?.region || process.env.TCB_REGION;
26540
26932
  const auth = toolbox_1.AuthSupervisor.getInstance({});
@@ -26863,12 +27255,19 @@ function registerEnvTools(server) {
26863
27255
  });
26864
27256
  }
26865
27257
  if (action === "logout") {
26866
- // Disallow logout in API Key mode
26867
- if ((0, auth_js_1.getCloudBaseApiKeyFromEnv)() && process.env.CLOUDBASE_ENV_ID) {
27258
+ // 用户自己在 shell / 宿主配置里设的 API Key 变量,进程内删不掉,只能提示去源头改;
27259
+ // 按项目保存的那一份(我们回填的,或 login_by_api_key 写下的)能清,走正常登出。
27260
+ if ((0, auth_js_1.getCloudBaseApiKeyFromEnv)() &&
27261
+ process.env.CLOUDBASE_ENV_ID &&
27262
+ !(0, auth_js_1.isEnvCredentialRemovableByLogout)()) {
26868
27263
  return (0, tool_result_js_1.buildJsonToolResult)({
26869
27264
  ok: false,
26870
27265
  code: "LOGOUT_NOT_ALLOWED",
26871
- message: (0, index_js_1.t)("env.auth.logoutNotAllowedApiKey", undefined, outLang),
27266
+ message: (0, index_js_1.t)("env.auth.logoutNotAllowedApiKey", {
27267
+ varName: process.env.CLOUDBASE_API_KEY
27268
+ ? "CLOUDBASE_API_KEY"
27269
+ : "CLOUDBASE_APIKEY",
27270
+ }, outLang),
26872
27271
  auth_mode: "api_key",
26873
27272
  });
26874
27273
  }
@@ -26882,12 +27281,15 @@ function registerEnvTools(server) {
26882
27281
  }),
26883
27282
  });
26884
27283
  }
26885
- await (0, auth_js_1.logout)();
27284
+ const { removed } = await (0, auth_js_1.logout)();
26886
27285
  (0, cloudbase_manager_js_1.resetCloudBaseManagerCache)();
26887
27286
  return (0, tool_result_js_1.buildJsonToolResult)({
26888
27287
  ok: true,
26889
27288
  code: "LOGGED_OUT",
26890
- message: (0, index_js_1.t)("env.auth.loggedOut", undefined, outLang),
27289
+ removed_scopes: removed,
27290
+ message: removed.includes("project")
27291
+ ? (0, index_js_1.t)("env.auth.loggedOutProject", undefined, outLang)
27292
+ : (0, index_js_1.t)("env.auth.loggedOut", undefined, outLang),
26891
27293
  });
26892
27294
  }
26893
27295
  if (action === "get_temp_credentials") {
@@ -27044,11 +27446,8 @@ function registerEnvTools(server) {
27044
27446
  // Filter parameters match the reference conditions provided by user
27045
27447
  result = await cloudbaseList.commonService("tcb", "2018-06-08").call({
27046
27448
  Action: "DescribeEnvs",
27047
- Param: {
27048
- EnvTypes: ["weda", "baas"], // Include weda and baas (normal) environments
27049
- IsVisible: false, // Filter out invisible environments
27050
- Channels: ["dcloud", "iotenable", "tem", "scene_module"], // Filter special channels
27051
- },
27449
+ // 过滤口径与 cloudbase-manager 共用一份,别在这里另写白名单
27450
+ Param: (0, describe_envs_query_js_1.buildDescribeEnvsFilter)(),
27052
27451
  });
27053
27452
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
27054
27453
  // Transform response format to match original listEnvs() format
@@ -29662,8 +30061,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
29662
30061
  return (mod && mod.__esModule) ? mod : { "default": mod };
29663
30062
  };
29664
30063
  Object.defineProperty(exports, "__esModule", ({ value: true }));
29665
- exports.CUSTOM_IMAGE_RUNTIME = exports.MASKED_ENV_VALUE = exports.LAYER_SOFT_WARN = exports.DEFAULT_FUNCTION_ALIAS = exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = exports.FUNCTION_UPLOAD_TARGET_NOT_FOUND_ERROR_CODE = void 0;
30064
+ exports.CUSTOM_IMAGE_RUNTIME = exports.MASKED_ENV_VALUE = exports.LAYER_SOFT_WARN = exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE = exports.FUNCTION_LOG_QUERY_ERROR_CODE = exports.DEFAULT_FUNCTION_ALIAS = exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = exports.FUNCTION_UPLOAD_TARGET_NOT_FOUND_ERROR_CODE = void 0;
29666
30065
  exports.formatRuntimeList = formatRuntimeList;
30066
+ exports.formatClsTime = formatClsTime;
30067
+ exports.buildFunctionLogClsQuery = buildFunctionLogClsQuery;
30068
+ exports.resolveFunctionLogWindow = resolveFunctionLogWindow;
30069
+ exports.parseFunctionLogItem = parseFunctionLogItem;
30070
+ exports.isLogServiceNotEnabledMessage = isLogServiceNotEnabledMessage;
29667
30071
  exports.layerNameIncludesEnvId = layerNameIncludesEnvId;
29668
30072
  exports.buildCreateLayerNameWarning = buildCreateLayerNameWarning;
29669
30073
  exports.maskFunctionDetailEnvValues = maskFunctionDetailEnvValues;
@@ -29827,6 +30231,125 @@ exports.MANAGE_FUNCTION_ACTIONS = [
29827
30231
  ];
29828
30232
  /** Default SCF traffic alias used by CloudBase CLI `fn get-route` / `fn config-route`. */
29829
30233
  exports.DEFAULT_FUNCTION_ALIAS = "$DEFAULT";
30234
+ /**
30235
+ * 云函数日志的 CLS 检索底座。
30236
+ *
30237
+ * 底层 `GetFunctionLogs` / `GetFunctionLogDetail` 已下线(`@cloudbase/manager-node` 5.8.8 起
30238
+ * `getFunctionLogsV2` / `getFunctionLogDetail` 直接抛错),云函数日志只能从 CLS 取。CLS 里每条
30239
+ * 云函数日志是按请求聚合的 JSON,`function_name` / `request_id` / `qualifier` / `src` 都是扁平
30240
+ * 字段,可以直接做检索条件,不需要再扫全量日志。
30241
+ */
30242
+ const FUNCTION_LOG_CLS_SOURCE = "(src:app OR src:system)";
30243
+ /** SearchClsLog 单次返回上限。 */
30244
+ const FUNCTION_LOG_MAX_LIMIT = 100;
30245
+ const FUNCTION_LOG_DEFAULT_LIMIT = 20;
30246
+ const FUNCTION_LOG_WINDOW_MS = 24 * 60 * 60 * 1000;
30247
+ /**
30248
+ * CLS 的时间入参固定按北京时间理解(与返回的 `Timestamp` 一致),
30249
+ * 托管形态的服务进程可能是 UTC,所以这里显式加偏移,不依赖进程时区。
30250
+ */
30251
+ const CLS_TIME_OFFSET_MS = 8 * 60 * 60 * 1000;
30252
+ /** CLS 检索语法里 `key:"value"` 的值需要转义反斜杠与双引号。 */
30253
+ function escapeClsValue(value) {
30254
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
30255
+ }
30256
+ /** CLS 入参/展示用的时间格式:YYYY-MM-DD HH:mm:ss(北京时间)。 */
30257
+ function formatClsTime(time) {
30258
+ return new Date(time + CLS_TIME_OFFSET_MS).toISOString().slice(0, 19).replace("T", " ");
30259
+ }
30260
+ function buildFunctionLogClsQuery(input) {
30261
+ const clauses = [FUNCTION_LOG_CLS_SOURCE];
30262
+ if (input.functionName) {
30263
+ clauses.push(`function_name:"${escapeClsValue(input.functionName)}"`);
30264
+ }
30265
+ if (input.qualifier) {
30266
+ clauses.push(`qualifier:"${escapeClsValue(input.qualifier)}"`);
30267
+ }
30268
+ if (input.requestId) {
30269
+ clauses.push(`request_id:"${escapeClsValue(input.requestId)}"`);
30270
+ }
30271
+ return clauses.join(" AND ");
30272
+ }
30273
+ /** 只传一端时把窗口收敛到 24 小时以内,CLS 与旧接口都限制单次跨度。 */
30274
+ function resolveFunctionLogWindow(startTime, endTime, now = Date.now()) {
30275
+ if (startTime && endTime) {
30276
+ return { StartTime: startTime, EndTime: endTime };
30277
+ }
30278
+ if (startTime) {
30279
+ const start = new Date(startTime).getTime();
30280
+ // 单一 startTime 时至少给出一分钟窗口:起止相同(或 start 在未来)时 CLS 会直接判非法。
30281
+ const end = Number.isFinite(start)
30282
+ ? Math.max(start + 60_000, Math.min(now, start + FUNCTION_LOG_WINDOW_MS))
30283
+ : now;
30284
+ return { StartTime: startTime, EndTime: formatClsTime(end) };
30285
+ }
30286
+ if (endTime) {
30287
+ const end = new Date(endTime).getTime();
30288
+ const start = Number.isFinite(end) ? end - FUNCTION_LOG_WINDOW_MS : now - FUNCTION_LOG_WINDOW_MS;
30289
+ return { StartTime: formatClsTime(start), EndTime: endTime };
30290
+ }
30291
+ return { StartTime: formatClsTime(now - FUNCTION_LOG_WINDOW_MS), EndTime: formatClsTime(now) };
30292
+ }
30293
+ /**
30294
+ * 把 CLS 的日志条目还原成调用方关心的字段。
30295
+ *
30296
+ * 解析失败时(例如用户自己打的纯文本日志)保留原文放进 `log`,不让一条脏数据毁掉整页结果。
30297
+ */
30298
+ function parseFunctionLogItem(item) {
30299
+ const content = typeof item.Content === "string" ? item.Content : "";
30300
+ let parsed = null;
30301
+ try {
30302
+ const value = JSON.parse(content);
30303
+ if (value && typeof value === "object" && !Array.isArray(value)) {
30304
+ parsed = value;
30305
+ }
30306
+ }
30307
+ catch {
30308
+ parsed = null;
30309
+ }
30310
+ const timestamp = item.Timestamp ?? "";
30311
+ if (!parsed) {
30312
+ return {
30313
+ requestId: "",
30314
+ functionName: "",
30315
+ qualifier: "",
30316
+ source: "",
30317
+ log: content,
30318
+ statusCode: "",
30319
+ retCode: "",
30320
+ durationMs: null,
30321
+ memoryMb: null,
30322
+ memUsageBytes: null,
30323
+ requestSource: "",
30324
+ timestamp,
30325
+ };
30326
+ }
30327
+ const str = (value) => value === undefined || value === null ? "" : String(value);
30328
+ const num = (value) => {
30329
+ const parsedNumber = Number(value);
30330
+ return Number.isFinite(parsedNumber) ? parsedNumber : null;
30331
+ };
30332
+ return {
30333
+ requestId: str(parsed.request_id),
30334
+ functionName: str(parsed.function_name),
30335
+ qualifier: str(parsed.qualifier || parsed.alias),
30336
+ source: str(parsed.src),
30337
+ log: str(parsed.log || parsed.tcb_log),
30338
+ statusCode: str(parsed.status_code),
30339
+ retCode: str(parsed.ret_code),
30340
+ durationMs: num(parsed.duration),
30341
+ memoryMb: num(parsed.memory),
30342
+ memUsageBytes: num(parsed.mem_usage),
30343
+ requestSource: str(parsed.request_source),
30344
+ timestamp,
30345
+ };
30346
+ }
30347
+ exports.FUNCTION_LOG_QUERY_ERROR_CODE = "FUNCTION_LOG_QUERY_FAILED";
30348
+ exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE = "LOG_SERVICE_NOT_ENABLED";
30349
+ /** 未开通日志服务时 CLS 报的是 topic 不存在,按这个特征把它翻译成可执行的下一步。 */
30350
+ function isLogServiceNotEnabledMessage(message) {
30351
+ return /TopicNotExist|TopicIsolated|topic[\s\S]{0,20}not\s*exist|日志服务未开通/i.test(message);
30352
+ }
29830
30353
  /** Layer soft-warn copy — account-scoped SCF LayerName guidance (no hard fail). */
29831
30354
  exports.LAYER_SOFT_WARN = {
29832
30355
  createNameFormat: (envId) => (0, index_js_1.t)("functions.layerWarn.createNameFormat", { envId }),
@@ -30294,6 +30817,81 @@ function registerFunctionTools(server) {
30294
30817
  }
30295
30818
  }
30296
30819
  };
30820
+ /**
30821
+ * 云函数日志统一走 CLS。查询失败时分两类给出可执行的下一步:
30822
+ * 日志服务没开通 ⇒ 引导开通;其它失败 ⇒ 引导到 queryLogs 用同一条检索语句重试。
30823
+ */
30824
+ const throwLogQueryError = (error, scope) => {
30825
+ const message = error instanceof Error ? error.message : String(error);
30826
+ if (isLogServiceNotEnabledMessage(message)) {
30827
+ (0, tool_result_js_1.throwToolPayloadError)({
30828
+ success: false,
30829
+ errorCode: exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE,
30830
+ data: { ...scope },
30831
+ message: (0, index_js_1.t)("functions.logs.serviceNotEnabled"),
30832
+ nextActions: [
30833
+ {
30834
+ tool: "manageLogs",
30835
+ action: "createLogService",
30836
+ reason: (0, index_js_1.t)("functions.reason.createLogService"),
30837
+ suggested_args: { action: "createLogService", confirm: true },
30838
+ },
30839
+ ],
30840
+ });
30841
+ }
30842
+ (0, tool_result_js_1.throwToolPayloadError)({
30843
+ success: false,
30844
+ errorCode: exports.FUNCTION_LOG_QUERY_ERROR_CODE,
30845
+ data: { ...scope, rawError: message },
30846
+ message: (0, index_js_1.t)("functions.logs.queryFailed", { message }),
30847
+ nextActions: [
30848
+ {
30849
+ tool: "queryLogs",
30850
+ action: "searchLogs",
30851
+ reason: (0, index_js_1.t)("functions.reason.searchClsFallback"),
30852
+ suggested_args: {
30853
+ action: "searchLogs",
30854
+ service: "tcb",
30855
+ queryString: buildFunctionLogClsQuery({
30856
+ functionName: scope.functionName,
30857
+ requestId: scope.requestId,
30858
+ }),
30859
+ startTime: scope.window.StartTime,
30860
+ endTime: scope.window.EndTime,
30861
+ },
30862
+ },
30863
+ ],
30864
+ });
30865
+ };
30866
+ /**
30867
+ * 空结果不等于「函数没被调用」:日志服务没开通时 CLS 同样返回空。
30868
+ * 这里只在真的查到空结果时多做一次开销很小的探活,避免把「没开通」误报成「没日志」。
30869
+ */
30870
+ const assertLogServiceEnabled = async (logService) => {
30871
+ try {
30872
+ if (await logService.checkLogServiceEnabled()) {
30873
+ return;
30874
+ }
30875
+ }
30876
+ catch {
30877
+ // 探活失败不制造新的错误源:保持「该时间段没有日志」的原始结论。
30878
+ return;
30879
+ }
30880
+ (0, tool_result_js_1.throwToolPayloadError)({
30881
+ success: false,
30882
+ errorCode: exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE,
30883
+ data: { enabled: false },
30884
+ message: (0, index_js_1.t)("functions.logs.serviceNotEnabled"),
30885
+ nextActions: [
30886
+ {
30887
+ tool: "manageLogs",
30888
+ action: "createLogService",
30889
+ reason: (0, index_js_1.t)("functions.reason.createLogService"),
30890
+ suggested_args: { action: "createLogService", confirm: true },
30891
+ },
30892
+ ],
30893
+ });
30894
+ };
30297
30895
  const normalizeManageLayers = (layers) => normalizeFunctionLayers((layers ?? []).map((layer) => ({
30298
30896
  LayerName: layer.layerName ?? layer.LayerName,
30299
30897
  LayerVersion: layer.layerVersion ?? layer.LayerVersion,
@@ -30415,39 +31013,86 @@ function registerFunctionTools(server) {
30415
31013
  }
30416
31014
  validateLogRange(input.startTime, input.endTime, input.offset, input.limit);
30417
31015
  const cloudbase = await getManager();
30418
- let result;
31016
+ const window = resolveFunctionLogWindow(input.startTime, input.endTime);
31017
+ const limit = Math.min(Math.max(input.limit ?? FUNCTION_LOG_DEFAULT_LIMIT, 1), FUNCTION_LOG_MAX_LIMIT);
31018
+ const offset = Math.max(input.offset ?? 0, 0);
31019
+ // SearchClsLog 单次最多 100 条且没有 offset 入参:不带游标时多取 offset+limit 再本地
31020
+ // 切片,带游标(context)时游标本身已经记录了偏移,直接返回当页。
31021
+ const fetchLimit = Math.min(offset + limit, FUNCTION_LOG_MAX_LIMIT);
31022
+ const queryString = buildFunctionLogClsQuery({
31023
+ functionName: input.functionName,
31024
+ requestId: input.requestId,
31025
+ qualifier: input.qualifier,
31026
+ });
31027
+ let logResults = {};
30419
31028
  try {
30420
- result = await cloudbase.functions.getFunctionLogsV2({
30421
- name: input.functionName,
30422
- offset: input.offset,
30423
- limit: input.limit,
30424
- startTime: input.startTime,
30425
- endTime: input.endTime,
30426
- requestId: input.requestId,
30427
- qualifier: input.qualifier,
31029
+ const result = await cloudbase.log.searchClsLog({
31030
+ queryString,
31031
+ StartTime: window.StartTime,
31032
+ EndTime: window.EndTime,
31033
+ Limit: fetchLimit,
31034
+ Context: input.context,
31035
+ Sort: "desc",
31036
+ service: "tcb",
30428
31037
  });
31038
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
31039
+ logResults = result.LogResults ?? {};
30429
31040
  }
30430
31041
  catch (error) {
30431
- const errMsg = error instanceof Error ? error.message : String(error);
30432
- if (/invalid parameter/i.test(errMsg)) {
30433
- throw new Error(`${errMsg}\n\n${(0, index_js_1.t)("functions.logsInvalidParamTips")}`);
30434
- }
30435
- throw error;
31042
+ throwLogQueryError(error, { functionName: input.functionName, window });
30436
31043
  }
30437
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
31044
+ const entries = (logResults.Results ?? []).map(parseFunctionLogItem);
31045
+ const logs = input.context ? entries : entries.slice(offset, offset + limit);
31046
+ if (logs.length === 0) {
31047
+ await assertLogServiceEnabled(cloudbase.log);
31048
+ }
31049
+ const hasMore = !logResults.ListOver && Boolean(logResults.Context);
30438
31050
  return buildEnvelope({
30439
31051
  action: input.action,
30440
31052
  functionName: input.functionName,
30441
- logs: result.LogList || [],
30442
- requestId: result.RequestId,
30443
- raw: result,
30444
- }, (0, index_js_1.t)("functions.gotFunctionLogs", { fnName: input.functionName }), [
30445
- {
30446
- tool: "queryFunctions",
30447
- action: "getFunctionLogDetail",
30448
- reason: (0, index_js_1.t)("functions.reason.logDetail"),
30449
- },
30450
- ]);
31053
+ service: "tcb",
31054
+ queryString,
31055
+ startTime: window.StartTime,
31056
+ endTime: window.EndTime,
31057
+ logs,
31058
+ count: logs.length,
31059
+ context: logResults.Context ?? null,
31060
+ hasMore,
31061
+ }, logs.length > 0
31062
+ ? (0, index_js_1.t)("functions.gotFunctionLogs", { fnName: input.functionName })
31063
+ : (0, index_js_1.t)("functions.logs.emptyByFunction", {
31064
+ fnName: input.functionName,
31065
+ start: window.StartTime,
31066
+ end: window.EndTime,
31067
+ }), hasMore
31068
+ ? [
31069
+ {
31070
+ tool: "queryFunctions",
31071
+ action: "listFunctionLogs",
31072
+ reason: (0, index_js_1.t)("functions.reason.nextLogPage"),
31073
+ suggested_args: {
31074
+ action: "listFunctionLogs",
31075
+ functionName: input.functionName,
31076
+ context: logResults.Context,
31077
+ startTime: window.StartTime,
31078
+ endTime: window.EndTime,
31079
+ limit,
31080
+ },
31081
+ },
31082
+ ]
31083
+ : [
31084
+ {
31085
+ tool: "queryFunctions",
31086
+ action: "getFunctionLogDetail",
31087
+ reason: (0, index_js_1.t)("functions.reason.logDetail"),
31088
+ suggested_args: {
31089
+ action: "getFunctionLogDetail",
31090
+ requestId: logs[0]?.requestId || "<日志里的 requestId>",
31091
+ startTime: window.StartTime,
31092
+ endTime: window.EndTime,
31093
+ },
31094
+ },
31095
+ ]);
30451
31096
  }
30452
31097
  case "getFunctionLogDetail": {
30453
31098
  if (!input.requestId) {
@@ -30455,27 +31100,67 @@ function registerFunctionTools(server) {
30455
31100
  }
30456
31101
  validateLogRange(input.startTime, input.endTime);
30457
31102
  const cloudbase = await getManager();
30458
- let result;
31103
+ const window = resolveFunctionLogWindow(input.startTime, input.endTime);
31104
+ const queryString = buildFunctionLogClsQuery({
31105
+ functionName: input.functionName,
31106
+ requestId: input.requestId,
31107
+ qualifier: input.qualifier,
31108
+ });
31109
+ let logResults = {};
30459
31110
  try {
30460
- result = await cloudbase.functions.getFunctionLogDetail({
30461
- startTime: input.startTime,
30462
- endTime: input.endTime,
30463
- logRequestId: input.requestId,
31111
+ const result = await cloudbase.log.searchClsLog({
31112
+ queryString,
31113
+ StartTime: window.StartTime,
31114
+ EndTime: window.EndTime,
31115
+ Limit: FUNCTION_LOG_MAX_LIMIT,
31116
+ Sort: "asc",
31117
+ service: "tcb",
30464
31118
  });
31119
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
31120
+ logResults = result.LogResults ?? {};
30465
31121
  }
30466
31122
  catch (error) {
30467
- const errMsg = error instanceof Error ? error.message : String(error);
30468
- if (/invalid parameter/i.test(errMsg)) {
30469
- throw new Error(`${errMsg}\n\n${(0, index_js_1.t)("functions.logDetailInvalidParamTips")}`);
30470
- }
30471
- throw error;
31123
+ throwLogQueryError(error, { requestId: input.requestId, window });
31124
+ }
31125
+ // 一次调用在 CLS 里是多条记录(START / 业务日志 / REPORT),按时间升序拼回完整过程。
31126
+ const entries = (logResults.Results ?? []).map(parseFunctionLogItem);
31127
+ if (entries.length === 0) {
31128
+ await assertLogServiceEnabled(cloudbase.log);
31129
+ return buildEnvelope({
31130
+ action: input.action,
31131
+ requestId: input.requestId,
31132
+ queryString,
31133
+ startTime: window.StartTime,
31134
+ endTime: window.EndTime,
31135
+ logs: [],
31136
+ count: 0,
31137
+ }, (0, index_js_1.t)("functions.logs.emptyByRequest", {
31138
+ requestId: input.requestId,
31139
+ start: window.StartTime,
31140
+ end: window.EndTime,
31141
+ }), [
31142
+ {
31143
+ tool: "queryFunctions",
31144
+ action: "listFunctionLogs",
31145
+ reason: (0, index_js_1.t)("functions.reason.widenLogRange"),
31146
+ suggested_args: {
31147
+ action: "listFunctionLogs",
31148
+ functionName: input.functionName || "<函数名>",
31149
+ startTime: window.StartTime,
31150
+ endTime: window.EndTime,
31151
+ },
31152
+ },
31153
+ ]);
30472
31154
  }
30473
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
30474
31155
  return buildEnvelope({
30475
31156
  action: input.action,
30476
31157
  requestId: input.requestId,
30477
- logDetail: result,
30478
- raw: result,
31158
+ functionName: entries[0]?.functionName || input.functionName || null,
31159
+ queryString,
31160
+ startTime: window.StartTime,
31161
+ endTime: window.EndTime,
31162
+ logs: entries,
31163
+ count: entries.length,
30479
31164
  }, (0, index_js_1.t)("functions.gotLogDetail", { requestId: input.requestId }));
30480
31165
  }
30481
31166
  case "listFunctionLayers": {
@@ -31851,6 +32536,7 @@ function registerFunctionTools(server) {
31851
32536
  .optional()
31852
32537
  .describe("functions.schema.query.requestId"),
31853
32538
  qualifier: zod_1.z.string().optional().describe("functions.schema.query.qualifier"),
32539
+ context: zod_1.z.string().optional().describe("functions.schema.query.context"),
31854
32540
  runtime: zod_1.z.string().optional().describe("functions.schema.query.runtime"),
31855
32541
  searchKey: zod_1.z.string().optional().describe("functions.schema.query.searchKey"),
31856
32542
  layerName: zod_1.z
@@ -31977,6 +32663,7 @@ const QUERY_GATEWAY_ACTIONS = [
31977
32663
  "getRoute",
31978
32664
  "listCustomDomains",
31979
32665
  "getPrivilege",
32666
+ "cachePurgeTask",
31980
32667
  ];
31981
32668
  const MANAGE_GATEWAY_ACTIONS = [
31982
32669
  "createRoute",
@@ -31988,6 +32675,7 @@ const MANAGE_GATEWAY_ACTIONS = [
31988
32675
  "deleteCustomDomain",
31989
32676
  "enableService",
31990
32677
  "authSwitch",
32678
+ "purgeCache",
31991
32679
  ];
31992
32680
  /** DomainType of the environment default HTTP service (gateway) domain. */
31993
32681
  const HTTP_SERVICE_DOMAIN_TYPE = "HTTPSERVICE";
@@ -31997,6 +32685,24 @@ const UPSTREAM_RESOURCE_TYPES = [
31997
32685
  "CBR",
31998
32686
  "STATIC_STORE",
31999
32687
  "LH",
32688
+ "STORAGE",
32689
+ ];
32690
+ /** Domain-level protocol accepted by HTTPServiceDomainParam. */
32691
+ const DOMAIN_PROTOCOLS = [
32692
+ "HTTP",
32693
+ "HTTPS",
32694
+ "HTTP_AND_HTTPS",
32695
+ "HTTP_TO_HTTPS",
32696
+ "HTTPS_TO_HTTP",
32697
+ ];
32698
+ /** Cache layer that owns the purged objects (CDN vs EdgeOne). */
32699
+ const CACHE_TYPES = ["CDN", "EO"];
32700
+ const PURGE_TYPES = ["PURGE_URL", "PURGE_PREFIX", "PURGE_HOST"];
32701
+ const HEADER_ADD_ACTIONS = [
32702
+ "APPEND_IF_EXISTS_OR_ADD",
32703
+ "ADD_IF_ABSENT",
32704
+ "OVERWRITE_IF_EXISTS_OR_ADD",
32705
+ "OVERWRITE_IF_EXISTS",
32000
32706
  ];
32001
32707
  function normalizeAccessPath(path) {
32002
32708
  if (!path) {
@@ -32004,6 +32710,225 @@ function normalizeAccessPath(path) {
32004
32710
  }
32005
32711
  return path.startsWith("/") ? path : `/${path}`;
32006
32712
  }
32713
+ // ---------------------------------------------------------------------------
32714
+ // Route/Domain extension schemas (aligned with manager-node HTTPServiceRouteParam)
32715
+ // ---------------------------------------------------------------------------
32716
+ const headerToAddSchema = zod_1.z.object({
32717
+ key: zod_1.z.string().describe("gateway.schema.manage.extension.header.key"),
32718
+ value: zod_1.z.string().describe("gateway.schema.manage.extension.header.value"),
32719
+ action: zod_1.z
32720
+ .enum(HEADER_ADD_ACTIONS)
32721
+ .optional()
32722
+ .describe("gateway.schema.manage.extension.header.action"),
32723
+ });
32724
+ const headersHandlerSchema = zod_1.z.object({
32725
+ requestHeadersToAdd: zod_1.z.array(headerToAddSchema).optional(),
32726
+ requestHeadersToRemove: zod_1.z.array(zod_1.z.string()).optional(),
32727
+ responseHeadersToAdd: zod_1.z.array(headerToAddSchema).optional(),
32728
+ responseHeadersToRemove: zod_1.z.array(zod_1.z.string()).optional(),
32729
+ });
32730
+ const cacheRuleSchema = zod_1.z.object({
32731
+ description: zod_1.z.string().optional(),
32732
+ enable: zod_1.z.boolean().optional(),
32733
+ condition: zod_1.z
32734
+ .object({
32735
+ target: zod_1.z.enum(["url_path", "file_extension", "full_uri"]).optional(),
32736
+ matchType: zod_1.z.enum(["prefix", "suffix", "contains", "exact"]).optional(),
32737
+ values: zod_1.z.array(zod_1.z.string()).optional(),
32738
+ })
32739
+ .describe("gateway.schema.manage.extension.cacheCondition"),
32740
+ actions: zod_1.z
32741
+ .array(zod_1.z.object({
32742
+ type: zod_1.z.enum(["Cache", "CacheKey"]).optional(),
32743
+ cache: zod_1.z
32744
+ .object({
32745
+ followOrigin: zod_1.z.boolean().optional(),
32746
+ noCache: zod_1.z.boolean().optional(),
32747
+ cacheTime: zod_1.z.number().int().min(0).optional(),
32748
+ maxAgeTime: zod_1.z.number().int().min(0).optional(),
32749
+ })
32750
+ .optional(),
32751
+ cacheKey: zod_1.z
32752
+ .object({
32753
+ fullUrlCache: zod_1.z.enum(["on", "off"]).optional(),
32754
+ queryStringSwitch: zod_1.z.enum(["on", "off"]).optional(),
32755
+ queryStringAction: zod_1.z.enum(["includeCustom", "excludeCustom"]).optional(),
32756
+ queryStringValues: zod_1.z.array(zod_1.z.string()).optional(),
32757
+ })
32758
+ .optional(),
32759
+ }))
32760
+ .describe("gateway.schema.manage.extension.cacheActions"),
32761
+ });
32762
+ const extensionSchema = zod_1.z.object({
32763
+ headersHandler: headersHandlerSchema.optional(),
32764
+ cache: zod_1.z.object({ rules: zod_1.z.array(cacheRuleSchema).optional() }).optional(),
32765
+ });
32766
+ const pathRewriteSchema = zod_1.z.object({
32767
+ prefix: zod_1.z.string().optional(),
32768
+ staticStorePrefix: zod_1.z.string().optional(),
32769
+ });
32770
+ const qpsPolicySchema = zod_1.z.object({
32771
+ total: zod_1.z.number().int().min(0).optional(),
32772
+ perClient: zod_1.z
32773
+ .object({
32774
+ limitBy: zod_1.z.enum(["ClientIP", "UserID"]).optional(),
32775
+ limitValue: zod_1.z.number().int().min(1).optional(),
32776
+ })
32777
+ .optional(),
32778
+ });
32779
+ function toHeadersHandler(input) {
32780
+ if (!input) {
32781
+ return undefined;
32782
+ }
32783
+ const output = {};
32784
+ const toHeader = (item) => item.action
32785
+ ? { Key: item.key, Value: item.value, Action: item.action }
32786
+ : { Key: item.key, Value: item.value };
32787
+ if (input.requestHeadersToAdd?.length) {
32788
+ output.RequestHeadersToAdd = input.requestHeadersToAdd.map(toHeader);
32789
+ }
32790
+ if (input.requestHeadersToRemove?.length) {
32791
+ output.RequestHeadersToRemove = input.requestHeadersToRemove;
32792
+ }
32793
+ if (input.responseHeadersToAdd?.length) {
32794
+ output.ResponseHeadersToAdd = input.responseHeadersToAdd.map(toHeader);
32795
+ }
32796
+ if (input.responseHeadersToRemove?.length) {
32797
+ output.ResponseHeadersToRemove = input.responseHeadersToRemove;
32798
+ }
32799
+ return Object.keys(output).length ? output : undefined;
32800
+ }
32801
+ function toCacheSet(input) {
32802
+ if (!input?.rules?.length) {
32803
+ return undefined;
32804
+ }
32805
+ return {
32806
+ Rules: input.rules.map((rule) => ({
32807
+ ...(rule.description !== undefined ? { Description: rule.description } : {}),
32808
+ ...(rule.enable !== undefined ? { Enable: rule.enable } : {}),
32809
+ Condition: {
32810
+ ...(rule.condition?.target ? { Target: rule.condition.target } : {}),
32811
+ ...(rule.condition?.matchType ? { MatchType: rule.condition.matchType } : {}),
32812
+ ...(rule.condition?.values ? { Values: rule.condition.values } : {}),
32813
+ },
32814
+ Actions: (rule.actions ?? []).map((action) => ({
32815
+ ...(action.type ? { Type: action.type } : {}),
32816
+ ...(action.cache
32817
+ ? {
32818
+ Cache: {
32819
+ ...(action.cache.followOrigin !== undefined
32820
+ ? { FollowOrigin: action.cache.followOrigin }
32821
+ : {}),
32822
+ ...(action.cache.noCache !== undefined
32823
+ ? { NoCache: action.cache.noCache }
32824
+ : {}),
32825
+ ...(action.cache.cacheTime !== undefined
32826
+ ? { CacheTime: action.cache.cacheTime }
32827
+ : {}),
32828
+ ...(action.cache.maxAgeTime !== undefined
32829
+ ? { MaxAgeTime: action.cache.maxAgeTime }
32830
+ : {}),
32831
+ },
32832
+ }
32833
+ : {}),
32834
+ ...(action.cacheKey
32835
+ ? {
32836
+ CacheKey: {
32837
+ ...(action.cacheKey.fullUrlCache
32838
+ ? { FullURLCache: action.cacheKey.fullUrlCache }
32839
+ : {}),
32840
+ ...(action.cacheKey.queryStringSwitch
32841
+ ? { QueryStringSwitch: action.cacheKey.queryStringSwitch }
32842
+ : {}),
32843
+ ...(action.cacheKey.queryStringAction
32844
+ ? { QueryStringAction: action.cacheKey.queryStringAction }
32845
+ : {}),
32846
+ ...(action.cacheKey.queryStringValues
32847
+ ? { QueryStringValues: action.cacheKey.queryStringValues }
32848
+ : {}),
32849
+ },
32850
+ }
32851
+ : {}),
32852
+ })),
32853
+ })),
32854
+ };
32855
+ }
32856
+ function toExtension(input) {
32857
+ if (!input) {
32858
+ return undefined;
32859
+ }
32860
+ const output = {};
32861
+ const headersHandler = toHeadersHandler(input.headersHandler);
32862
+ if (headersHandler) {
32863
+ output.HeadersHandler = headersHandler;
32864
+ }
32865
+ const cache = toCacheSet(input.cache);
32866
+ if (cache) {
32867
+ output.Cache = cache;
32868
+ }
32869
+ return Object.keys(output).length ? output : undefined;
32870
+ }
32871
+ function toPathRewrite(input) {
32872
+ if (!input) {
32873
+ return undefined;
32874
+ }
32875
+ const output = {};
32876
+ if (typeof input.prefix === "string") {
32877
+ output.Prefix = input.prefix;
32878
+ }
32879
+ if (typeof input.staticStorePrefix === "string") {
32880
+ output.StaticStorePrefix = input.staticStorePrefix;
32881
+ }
32882
+ return Object.keys(output).length ? output : undefined;
32883
+ }
32884
+ function toQpsPolicy(input) {
32885
+ if (!input) {
32886
+ return undefined;
32887
+ }
32888
+ const output = {};
32889
+ if (typeof input.total === "number") {
32890
+ output.QPSTotal = input.total;
32891
+ }
32892
+ if (input.perClient) {
32893
+ const perClient = {};
32894
+ if (input.perClient.limitBy) {
32895
+ perClient.LimitBy = input.perClient.limitBy;
32896
+ }
32897
+ if (typeof input.perClient.limitValue === "number") {
32898
+ perClient.LimitValue = input.perClient.limitValue;
32899
+ }
32900
+ if (Object.keys(perClient).length) {
32901
+ output.QPSPerClient = perClient;
32902
+ }
32903
+ }
32904
+ return Object.keys(output).length ? output : undefined;
32905
+ }
32906
+ /**
32907
+ * Route fields that ModifyHTTPServiceRoute replaces wholesale. When an update
32908
+ * omits one of them, the existing value must be carried over — otherwise a
32909
+ * partial update (e.g. only bumping QPS) would silently wipe CORS / rewrite /
32910
+ * headers / cache rules.
32911
+ */
32912
+ const PRESERVED_ROUTE_FIELDS = [
32913
+ "EnableAuth",
32914
+ "EnableSafeDomain",
32915
+ "EnablePathTransmission",
32916
+ "PathRewrite",
32917
+ "QPSPolicy",
32918
+ "Extension",
32919
+ "Enable",
32920
+ ];
32921
+ function carryOverRouteFields(route, existing) {
32922
+ if (!existing) {
32923
+ return route;
32924
+ }
32925
+ for (const field of PRESERVED_ROUTE_FIELDS) {
32926
+ if (route[field] === undefined && existing[field] !== undefined) {
32927
+ route[field] = existing[field];
32928
+ }
32929
+ }
32930
+ return route;
32931
+ }
32007
32932
  /**
32008
32933
  * Collect FAIL items from VerifyHTTPServiceRoute, mapping DNS records to
32009
32934
  * lowercase field names for MCP structured output (aligned with CLI 3.8.1).
@@ -32446,16 +33371,13 @@ function registerGatewayTools(server) {
32446
33371
  });
32447
33372
  const path = normalizeAccessPath(input.route?.path ?? input.path ?? `/${upstreamResourceName}`);
32448
33373
  const domain = await resolveRouteDomain(input.domain);
32449
- const enableAuth = input.route?.auth !== undefined
32450
- ? input.route.auth
32451
- : input.auth !== undefined
32452
- ? input.auth
32453
- : undefined;
32454
- const enablePathTransmission = input.route?.enablePathTransmission !== undefined
32455
- ? input.route.enablePathTransmission
32456
- : input.enablePathTransmission !== undefined
32457
- ? input.enablePathTransmission
32458
- : undefined;
33374
+ const pick = (routeValue, topValue) => routeValue !== undefined ? routeValue : topValue;
33375
+ const enableAuth = pick(input.route?.auth, input.auth);
33376
+ const enableSafeDomain = pick(input.route?.enableSafeDomain, input.enableSafeDomain);
33377
+ const enablePathTransmission = pick(input.route?.enablePathTransmission, input.enablePathTransmission);
33378
+ const routePathRewrite = toPathRewrite(pick(input.route?.pathRewrite, input.pathRewrite));
33379
+ const routeQpsPolicy = toQpsPolicy(pick(input.route?.qpsPolicy, input.qpsPolicy));
33380
+ const routeExtension = toExtension(pick(input.route?.extension, input.extension));
32459
33381
  // Route enable: prefer route.enable. For updateRoute only, top-level
32460
33382
  // enable also maps to Route.Enable (enableService/authSwitch use the
32461
33383
  // same field name but different actions).
@@ -32469,18 +33391,35 @@ function registerGatewayTools(server) {
32469
33391
  Path: path,
32470
33392
  UpstreamResourceType: upstreamResourceType,
32471
33393
  UpstreamResourceName: upstreamResourceName,
32472
- EnableAuth: enableAuth,
32473
33394
  };
33395
+ if (enableAuth !== undefined) {
33396
+ route.EnableAuth = enableAuth;
33397
+ }
33398
+ if (enableSafeDomain !== undefined) {
33399
+ route.EnableSafeDomain = enableSafeDomain;
33400
+ }
32474
33401
  if (enablePathTransmission !== undefined) {
32475
33402
  route.EnablePathTransmission = enablePathTransmission;
32476
33403
  }
33404
+ if (routePathRewrite) {
33405
+ route.PathRewrite = routePathRewrite;
33406
+ }
33407
+ if (routeQpsPolicy) {
33408
+ route.QPSPolicy = routeQpsPolicy;
33409
+ }
33410
+ if (routeExtension) {
33411
+ route.Extension = routeExtension;
33412
+ }
32477
33413
  if (routeEnable !== undefined) {
32478
33414
  route.Enable = routeEnable;
32479
33415
  }
33416
+ const domainExtension = toExtension(input.domainExtension);
32480
33417
  return {
32481
33418
  EnvId: await resolveEnvId(),
32482
33419
  Domain: {
32483
33420
  Domain: domain,
33421
+ ...(input.protocol ? { Protocol: input.protocol } : {}),
33422
+ ...(domainExtension ? { Extension: domainExtension } : {}),
32484
33423
  Routes: [route],
32485
33424
  },
32486
33425
  resolved: {
@@ -32489,6 +33428,7 @@ function registerGatewayTools(server) {
32489
33428
  upstreamResourceType,
32490
33429
  upstreamResourceName,
32491
33430
  enableAuth,
33431
+ enableSafeDomain,
32492
33432
  enablePathTransmission,
32493
33433
  enable: routeEnable,
32494
33434
  },
@@ -32537,6 +33477,57 @@ function registerGatewayTools(server) {
32537
33477
  }
32538
33478
  return matches[0];
32539
33479
  };
33480
+ /**
33481
+ * Locate the live record (domain + route) so partial updates can carry over
33482
+ * fields the caller did not touch.
33483
+ */
33484
+ const findExistingRouteRecord = async (domain, path) => {
33485
+ const result = await listHttpServiceRoutes({ domain });
33486
+ const domainRecord = (result.Domains ?? []).find((item) => item.Domain === domain);
33487
+ const route = (domainRecord?.Routes ?? []).find((item) => normalizeAccessPath(String(item.Path ?? "")) ===
33488
+ path);
33489
+ return {
33490
+ domainRecord,
33491
+ route: route,
33492
+ };
33493
+ };
33494
+ /** CDN vs EO is decided by the domain's access type; default to CDN. */
33495
+ /**
33496
+ * 缓存层由域名的接入方式决定,不能猜。
33497
+ *
33498
+ * - EO / CDN → 对应缓存层
33499
+ * - DIRECT / CUSTOM / NONE → CloudBase 侧没有托管缓存,刷新不适用。
33500
+ * 实测(2026-10-21)对 DIRECT 域发 CDN 刷新,PurgeHTTPServiceCache 会「成功」登记
33501
+ * 一个任务,而 DescribeHTTPServiceCachePurgeTask 随后抛 Third Service Error ——
33502
+ * 即后端对 DIRECT 域缺校验、返回不可读错误。这里提前拦住并说明原因。
33503
+ */
33504
+ const resolveDomainCacheType = async (domain, preferred) => {
33505
+ const findDomain = (result) => (result.Domains ?? []).find((item) => item.Domain === domain);
33506
+ let record = findDomain(await listHttpServiceRoutes({ domain }));
33507
+ if (!record) {
33508
+ // 域名下暂时没有路由时,筛 Domain 可能查不到,退回全量再找一次。
33509
+ record = findDomain(await listHttpServiceRoutes());
33510
+ }
33511
+ if (!record) {
33512
+ throw new Error((0, index_js_1.t)("gateway.cache.domainNotFound", { domain }));
33513
+ }
33514
+ const accessType = String(record.AccessType ?? "");
33515
+ const boundCacheType = accessType === "EO" ? "EO" : accessType === "CDN" ? "CDN" : undefined;
33516
+ if (!boundCacheType) {
33517
+ throw new Error((0, index_js_1.t)("gateway.cache.notManaged", {
33518
+ domain,
33519
+ accessType: accessType || "UNKNOWN",
33520
+ }));
33521
+ }
33522
+ if (preferred && preferred !== boundCacheType) {
33523
+ throw new Error((0, index_js_1.t)("gateway.cache.typeMismatch", {
33524
+ domain,
33525
+ accessType: boundCacheType,
33526
+ requested: preferred,
33527
+ }));
33528
+ }
33529
+ return boundCacheType;
33530
+ };
32540
33531
  const routeMutationNextActions = (targetName) => [
32541
33532
  {
32542
33533
  tool: "queryGateway",
@@ -32649,6 +33640,34 @@ function registerGatewayTools(server) {
32649
33640
  },
32650
33641
  ]);
32651
33642
  }
33643
+ case "cachePurgeTask": {
33644
+ if (!input.domain) {
33645
+ throw new Error((0, index_js_1.t)("gateway.query.cachePurgeTaskDomainRequired"));
33646
+ }
33647
+ const cacheType = await resolveDomainCacheType(input.domain, input.cacheType);
33648
+ const cloudbase = await getManager();
33649
+ const result = await cloudbase.env.describeHttpServiceCachePurgeTask({
33650
+ EnvId: await resolveEnvId(),
33651
+ Domain: input.domain,
33652
+ CacheType: cacheType,
33653
+ ...(input.taskId ? { TaskId: input.taskId } : {}),
33654
+ ...(input.purgeType ? { PurgeType: input.purgeType } : {}),
33655
+ ...(input.startTime ? { StartTime: input.startTime } : {}),
33656
+ ...(input.endTime ? { EndTime: input.endTime } : {}),
33657
+ ...(input.offset !== undefined ? { Offset: input.offset } : {}),
33658
+ ...(input.limit !== undefined ? { Limit: input.limit } : {}),
33659
+ });
33660
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
33661
+ const tasks = Array.isArray(result?.Tasks) ? result.Tasks : [];
33662
+ return buildEnvelope({
33663
+ action: input.action,
33664
+ domain: input.domain,
33665
+ cacheType,
33666
+ tasks,
33667
+ total: result?.TotalCount ?? tasks.length,
33668
+ raw: result,
33669
+ }, (0, index_js_1.t)("gateway.query.cachePurgeTask", { count: tasks.length }));
33670
+ }
32652
33671
  default:
32653
33672
  throw new Error((0, index_js_1.t)("gateway.error.unsupportedAction", { action: input.action }));
32654
33673
  }
@@ -32757,6 +33776,7 @@ function registerGatewayTools(server) {
32757
33776
  upstreamResourceType: payload.resolved.upstreamResourceType,
32758
33777
  upstreamResourceName: payload.resolved.upstreamResourceName,
32759
33778
  auth: payload.resolved.enableAuth ?? null,
33779
+ enableSafeDomain: payload.resolved.enableSafeDomain ?? null,
32760
33780
  enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
32761
33781
  accessUrl: `https://${payload.resolved.domain}${payload.resolved.path}`,
32762
33782
  accessUrls: [`https://${payload.resolved.domain}${payload.resolved.path}`],
@@ -32785,6 +33805,14 @@ function registerGatewayTools(server) {
32785
33805
  case "updateRoute": {
32786
33806
  const cloudbase = await getManager();
32787
33807
  const payload = await normalizeRoutePayload(input);
33808
+ // Preserve untouched route/domain fields from the live record: the
33809
+ // backend replaces the whole route entry, so a partial update would
33810
+ // otherwise wipe CORS / rewrite / QPS / headers / cache settings.
33811
+ const existing = await findExistingRouteRecord(payload.resolved.domain, payload.resolved.path);
33812
+ carryOverRouteFields(payload.Domain.Routes[0], existing.route);
33813
+ if (!payload.Domain.Protocol && existing.domainRecord?.Protocol) {
33814
+ payload.Domain.Protocol = existing.domainRecord.Protocol;
33815
+ }
32788
33816
  const result = await cloudbase.env.modifyHttpServiceRoute({
32789
33817
  EnvId: payload.EnvId,
32790
33818
  Domain: payload.Domain,
@@ -32802,6 +33830,7 @@ function registerGatewayTools(server) {
32802
33830
  upstreamResourceType: payload.resolved.upstreamResourceType,
32803
33831
  upstreamResourceName: payload.resolved.upstreamResourceName,
32804
33832
  auth: payload.resolved.enableAuth ?? null,
33833
+ enableSafeDomain: payload.resolved.enableSafeDomain ?? null,
32805
33834
  enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
32806
33835
  enable: payload.resolved.enable ?? null,
32807
33836
  ...(accessUrl
@@ -32883,6 +33912,11 @@ function registerGatewayTools(server) {
32883
33912
  if (enablePathTransmission !== undefined) {
32884
33913
  route.EnablePathTransmission = enablePathTransmission;
32885
33914
  }
33915
+ if (input.enableSafeDomain !== undefined) {
33916
+ route.EnableSafeDomain = input.enableSafeDomain;
33917
+ }
33918
+ // Toggling Enable must not drop CORS / rewrite / QPS / headers / cache.
33919
+ carryOverRouteFields(route, existing);
32886
33920
  const cloudbase = await getManager();
32887
33921
  const result = await cloudbase.env.modifyHttpServiceRoute({
32888
33922
  EnvId: await resolveEnvId(),
@@ -32977,6 +34011,7 @@ function registerGatewayTools(server) {
32977
34011
  CertId: input.certificateId || "",
32978
34012
  AccessType: accessType,
32979
34013
  Enable: input.enable !== undefined ? input.enable : true,
34014
+ ...(input.protocol ? { Protocol: input.protocol } : {}),
32980
34015
  ...(input.customCname ? { CustomCname: input.customCname } : {}),
32981
34016
  };
32982
34017
  const verifyFailure = await verifyHttpServiceRouteOrFail({
@@ -32998,6 +34033,7 @@ function registerGatewayTools(server) {
32998
34033
  Domain: input.domain,
32999
34034
  CertId: certificateId,
33000
34035
  AccessType: accessType,
34036
+ ...(input.protocol ? { Protocol: input.protocol } : {}),
33001
34037
  ...(input.enable !== undefined
33002
34038
  ? { Enable: input.enable }
33003
34039
  : {}),
@@ -33128,6 +34164,43 @@ function registerGatewayTools(server) {
33128
34164
  },
33129
34165
  ]);
33130
34166
  }
34167
+ case "purgeCache": {
34168
+ if (!input.domain) {
34169
+ throw new Error((0, index_js_1.t)("gateway.purgeCache.domainRequired"));
34170
+ }
34171
+ const targets = (input.targets ?? [])
34172
+ .map((item) => (typeof item === "string" ? item.trim() : ""))
34173
+ .filter(Boolean);
34174
+ if (!targets.length) {
34175
+ throw new Error((0, index_js_1.t)("gateway.purgeCache.targetsRequired"));
34176
+ }
34177
+ const purgeType = input.purgeType ?? "PURGE_URL";
34178
+ const cacheType = await resolveDomainCacheType(input.domain, input.cacheType);
34179
+ const cloudbase = await getManager();
34180
+ const result = await cloudbase.env.purgeHttpServiceCache({
34181
+ EnvId: await resolveEnvId(),
34182
+ Domain: input.domain,
34183
+ CacheType: cacheType,
34184
+ PurgeType: purgeType,
34185
+ Targets: targets,
34186
+ });
34187
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
34188
+ return buildEnvelope({
34189
+ action: input.action,
34190
+ domain: input.domain,
34191
+ cacheType,
34192
+ purgeType,
34193
+ targets,
34194
+ taskId: result?.TaskId ?? null,
34195
+ raw: result,
34196
+ }, (0, index_js_1.t)("gateway.purgeCache.message", { count: targets.length }), [
34197
+ {
34198
+ tool: "queryGateway",
34199
+ action: "cachePurgeTask",
34200
+ reason: (0, index_js_1.t)("gateway.purgeCache.verifyReason"),
34201
+ },
34202
+ ]);
34203
+ }
33131
34204
  default:
33132
34205
  throw new Error((0, index_js_1.t)("gateway.error.unsupportedAction", { action: input.action }));
33133
34206
  }
@@ -33146,6 +34219,19 @@ function registerGatewayTools(server) {
33146
34219
  routeId: zod_1.z.string().optional().describe("gateway.schema.query.routeId"),
33147
34220
  path: zod_1.z.string().optional().describe("gateway.schema.query.path"),
33148
34221
  domain: zod_1.z.string().optional().describe("gateway.schema.query.domain"),
34222
+ cacheType: zod_1.z
34223
+ .enum(CACHE_TYPES)
34224
+ .optional()
34225
+ .describe("gateway.schema.query.cacheType"),
34226
+ taskId: zod_1.z.string().optional().describe("gateway.schema.query.taskId"),
34227
+ purgeType: zod_1.z
34228
+ .enum(PURGE_TYPES)
34229
+ .optional()
34230
+ .describe("gateway.schema.query.purgeType"),
34231
+ startTime: zod_1.z.string().optional().describe("gateway.schema.query.startTime"),
34232
+ endTime: zod_1.z.string().optional().describe("gateway.schema.query.endTime"),
34233
+ offset: zod_1.z.number().int().min(0).optional().describe("gateway.schema.query.offset"),
34234
+ limit: zod_1.z.number().int().min(1).optional().describe("gateway.schema.query.limit"),
33149
34235
  },
33150
34236
  annotations: {
33151
34237
  readOnlyHint: true,
@@ -33175,6 +34261,26 @@ function registerGatewayTools(server) {
33175
34261
  .boolean()
33176
34262
  .optional()
33177
34263
  .describe("gateway.schema.manage.enablePathTransmission"),
34264
+ enableSafeDomain: zod_1.z
34265
+ .boolean()
34266
+ .optional()
34267
+ .describe("gateway.schema.manage.enableSafeDomain"),
34268
+ pathRewrite: pathRewriteSchema
34269
+ .optional()
34270
+ .describe("gateway.schema.manage.pathRewrite"),
34271
+ qpsPolicy: qpsPolicySchema
34272
+ .optional()
34273
+ .describe("gateway.schema.manage.qpsPolicy"),
34274
+ extension: extensionSchema
34275
+ .optional()
34276
+ .describe("gateway.schema.manage.extension"),
34277
+ domainExtension: extensionSchema
34278
+ .optional()
34279
+ .describe("gateway.schema.manage.domainExtension"),
34280
+ protocol: zod_1.z
34281
+ .enum(DOMAIN_PROTOCOLS)
34282
+ .optional()
34283
+ .describe("gateway.schema.manage.protocol"),
33178
34284
  route: zod_1.z
33179
34285
  .object({
33180
34286
  path: zod_1.z.string().optional(),
@@ -33191,6 +34297,19 @@ function registerGatewayTools(server) {
33191
34297
  .boolean()
33192
34298
  .optional()
33193
34299
  .describe("gateway.schema.manage.route.enablePathTransmission"),
34300
+ enableSafeDomain: zod_1.z
34301
+ .boolean()
34302
+ .optional()
34303
+ .describe("gateway.schema.manage.route.enableSafeDomain"),
34304
+ pathRewrite: pathRewriteSchema
34305
+ .optional()
34306
+ .describe("gateway.schema.manage.route.pathRewrite"),
34307
+ qpsPolicy: qpsPolicySchema
34308
+ .optional()
34309
+ .describe("gateway.schema.manage.route.qpsPolicy"),
34310
+ extension: extensionSchema
34311
+ .optional()
34312
+ .describe("gateway.schema.manage.route.extension"),
33194
34313
  enable: zod_1.z
33195
34314
  .boolean()
33196
34315
  .optional()
@@ -33212,6 +34331,18 @@ function registerGatewayTools(server) {
33212
34331
  .optional()
33213
34332
  .describe("gateway.schema.manage.customCname"),
33214
34333
  enable: zod_1.z.boolean().optional().describe("gateway.schema.manage.enable"),
34334
+ cacheType: zod_1.z
34335
+ .enum(CACHE_TYPES)
34336
+ .optional()
34337
+ .describe("gateway.schema.manage.cacheType"),
34338
+ purgeType: zod_1.z
34339
+ .enum(PURGE_TYPES)
34340
+ .optional()
34341
+ .describe("gateway.schema.manage.purgeType"),
34342
+ targets: zod_1.z
34343
+ .array(zod_1.z.string())
34344
+ .optional()
34345
+ .describe("gateway.schema.manage.targets"),
33215
34346
  },
33216
34347
  annotations: {
33217
34348
  readOnlyHint: false,
@@ -39599,7 +40730,7 @@ __webpack_unused_export__ = readSkipList;
39599
40730
  __webpack_unused_export__ = isSkipped;
39600
40731
  __webpack_unused_export__ = readAutoApprovePolicy;
39601
40732
  exports.guardDestructiveAction = guardDestructiveAction;
39602
- const cloudbase_manager_js_1 = __webpack_require__(3109);
40733
+ const env_binding_js_1 = __webpack_require__(1498);
39603
40734
  const index_js_1 = __webpack_require__(4908);
39604
40735
  const elicitation_js_1 = __webpack_require__(5048);
39605
40736
  const destructive_actions_js_1 = __webpack_require__(2827);
@@ -39668,16 +40799,7 @@ function readAutoApprovePolicy() {
39668
40799
  return "allow";
39669
40800
  }
39670
40801
  function resolveEnvId(server) {
39671
- const fromOptions = server.cloudBaseOptions?.envId;
39672
- if (typeof fromOptions === "string" && fromOptions.length > 0) {
39673
- return fromOptions;
39674
- }
39675
- try {
39676
- return (0, cloudbase_manager_js_1.getCachedEnvId)() ?? (0, index_js_1.t)("confirmation.message.unknownEnv");
39677
- }
39678
- catch {
39679
- return (0, index_js_1.t)("confirmation.message.unknownEnv");
39680
- }
40802
+ return (0, env_binding_js_1.readDisplayedEnvId)(server.cloudBaseOptions?.envId) ?? (0, index_js_1.t)("confirmation.message.unknownEnv");
39681
40803
  }
39682
40804
  function blockedText(status, actionKey, targetInfo, params) {
39683
40805
  const keyByStatus = {
@@ -40045,6 +41167,47 @@ async function guardCredentialWrite(server, params) {
40045
41167
  }
40046
41168
 
40047
41169
 
41170
+ /***/ },
41171
+
41172
+ /***/ 1284
41173
+ (__unused_webpack_module, exports) {
41174
+
41175
+ var __webpack_unused_export__;
41176
+
41177
+ /**
41178
+ * 账号级 DescribeEnvs 的查询口径 —— 唯一真源。
41179
+ *
41180
+ * EnvTypes 是白名单语义(不为空时以该字段为准):少写一个值不会报错,只会静默
41181
+ * 少一类环境。2026-10 的客户案例就是这样:平台侧代建的环境 EnvType=platform,
41182
+ * 控制台能列、MCP 拉不到,用户看到的是「请先创建环境」,现场没有任何错误可看。
41183
+ *
41184
+ * - baas / weda:标准云开发、微搭,MCP 工具主要面向这两类
41185
+ * - platform:平台侧代建的环境(site-* 一类),控制台列这类
41186
+ *
41187
+ * 未纳入 hosting / run(云托管)、tcbgw、paas:MCP 历史上就没列过,加进来会让
41188
+ * 用户在环境候选里看到一堆用不了的托管环境。授权页那份白名单多一个 hosting,
41189
+ * 是历史原因,两边差异是有意的。
41190
+ */
41191
+ __webpack_unused_export__ = ({ value: true });
41192
+ exports.hI = exports.jC = void 0;
41193
+ exports.buildDescribeEnvsFilter = buildDescribeEnvsFilter;
41194
+ /** 允许出现在环境候选列表里的 EnvType */
41195
+ exports.jC = ["baas", "weda", "platform"];
41196
+ /**
41197
+ * IsVisible=false 表示 Channels 是「排除列表」:
41198
+ * 这 4 个渠道创建的环境不参与环境候选,其余渠道(含控制台的 qc_console)正常返回。
41199
+ */
41200
+ exports.hI = ["dcloud", "iotenable", "tem", "scene_module"];
41201
+ /** 生成 DescribeEnvs 的 Param 过滤段;每次返回新数组,避免调用方改到共享常量 */
41202
+ function buildDescribeEnvsFilter() {
41203
+ return {
41204
+ EnvTypes: [...exports.jC],
41205
+ IsVisible: false,
41206
+ Channels: [...exports.hI],
41207
+ };
41208
+ }
41209
+
41210
+
40048
41211
  /***/ },
40049
41212
 
40050
41213
  /***/ 2827
@@ -40395,6 +41558,212 @@ function resolveDestructiveAction(toolName, args) {
40395
41558
  }
40396
41559
 
40397
41560
 
41561
+ /***/ },
41562
+
41563
+ /***/ 6953
41564
+ (__unused_webpack_module, exports, __webpack_require__) {
41565
+
41566
+
41567
+ var __importDefault = (this && this.__importDefault) || function (mod) {
41568
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41569
+ };
41570
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
41571
+ exports.readSystemAnchor = readSystemAnchor;
41572
+ exports.physicalMacAddresses = physicalMacAddresses;
41573
+ exports.collectHostSignal = collectHostSignal;
41574
+ exports.computeDeviceId = computeDeviceId;
41575
+ const crypto_1 = __importDefault(__webpack_require__(7823));
41576
+ const child_process_1 = __webpack_require__(5814);
41577
+ const fs_1 = __importDefault(__webpack_require__(4421));
41578
+ const os_1 = __importDefault(__webpack_require__(8116));
41579
+ /**
41580
+ * 匿名设备指纹。
41581
+ *
41582
+ * 为什么要有这个文件:旧实现是 `sha256(hostname | CPU model | 全部非内部网卡 MAC)`,
41583
+ * 锚点选错了 —— macOS 开着「私有 Wi-Fi 地址」时每个 SSID 一个随机 MAC,`awdl0` / `llw0` /
41584
+ * `bridge0` 的 MAC 随系统状态漂移,容器里 hostname 和 MAC 每次重建都变。结果是同一台 Mac
41585
+ * 一天能上报成几十个「设备」(2026-10-26 实测:Mac 指纹 82% 只在那一天出现过,
41586
+ * Windows 同口径 22%)。
41587
+ *
41588
+ * 现在的取值顺序:
41589
+ * 1. 系统级不可变标识 —— macOS `IOPlatformUUID`、Windows `MachineGuid`、Linux `/etc/machine-id`;
41590
+ * 2. 取不到时退到 `hostname + CPU model + 过滤后的物理网卡 MAC`;
41591
+ * 3. 再取不到只留 `hostname + CPU model`。
41592
+ *
41593
+ * 三条纪律:
41594
+ * - **格式不变**:仍是 SHA256 前 32 位 hex,灯塔 `A2` 字段与后端都不用改。
41595
+ * - **绝不用随机数兜底**。旧实现取不到信息时 `crypto.randomBytes(16)`,等于每次启动都是
41596
+ * 一台新设备 —— 正是要消灭的行为。宁可所有机器共用一个弱指纹,也不要一堆假设备。
41597
+ * - **只在构造时跑一次**。锚点要起子进程(`ioreg` / `reg`),1s 超时就降级,
41598
+ * 绝不放进每次上报的路径。
41599
+ *
41600
+ * 口径变化(换锚点当天就会看到,报表侧要提前打招呼):
41601
+ * - 历史 `A2` 全部失效,老设备会以新指纹重新出现 —— 报表上「装机量跳一下」不是增长。
41602
+ * - 容器内多实例若共享镜像固化的 `/etc/machine-id`,会被合并成同一台设备(以前是各算一个新设备)。
41603
+ */
41604
+ /** 取系统级锚点的子进程超时:超了直接降级,不能拖慢进程启动 */
41605
+ const ANCHOR_TIMEOUT_MS = 1000;
41606
+ /**
41607
+ * 虚拟 / 隧道网卡名前缀(小写比较)。这些接口的 MAC 会随系统状态漂移或每次重建都变,
41608
+ * 拼进指纹就等于给同一台机器发新身份。
41609
+ */
41610
+ const VIRTUAL_NIC_PREFIXES = [
41611
+ 'awdl', // Apple Wireless Direct Link
41612
+ 'llw', // Apple 低延迟 WLAN
41613
+ 'bridge', // macOS / Windows 网桥
41614
+ 'utun', // macOS / iOS VPN 隧道
41615
+ 'tun',
41616
+ 'tap',
41617
+ 'veth', // 容器 veth pair
41618
+ 'docker',
41619
+ 'br-', // docker bridge
41620
+ 'vmnet', // VMware
41621
+ 'vboxnet', // VirtualBox
41622
+ 'vether', // Hyper-V 虚拟交换机(vEthernet)
41623
+ 'zt', // ZeroTier
41624
+ 'tailscale',
41625
+ 'wg', // WireGuard
41626
+ 'lo', // loopback(正常情况下 internal 已过滤,兜一层)
41627
+ ];
41628
+ function sha256Short(input) {
41629
+ return crypto_1.default.createHash('sha256').update(input).digest('hex').substring(0, 32);
41630
+ }
41631
+ /** `machine-id` 合法值:16–64 位 hex,且不能是全 0(有些镜像里就是全 0 的空壳) */
41632
+ function isUsableMachineId(value) {
41633
+ if (!/^[0-9a-f]{16,64}$/i.test(value)) {
41634
+ return false;
41635
+ }
41636
+ return !/^0+$/.test(value);
41637
+ }
41638
+ function readFileQuietly(filePath) {
41639
+ try {
41640
+ return fs_1.default.readFileSync(filePath, 'utf8').trim();
41641
+ }
41642
+ catch {
41643
+ return undefined;
41644
+ }
41645
+ }
41646
+ /** 起一次子进程读锚点。超时 / 不存在 / 权限不足一律返回 undefined,由调用方降级。 */
41647
+ function execQuietly(file, args) {
41648
+ try {
41649
+ return (0, child_process_1.execFileSync)(file, args, {
41650
+ timeout: ANCHOR_TIMEOUT_MS,
41651
+ encoding: 'utf8',
41652
+ // 子进程的 stderr 全部吞掉:`ioreg` 在受限环境会打噪音,`reg` 在 32 位 Node
41653
+ // 下可能报找不到键,这些都不该出现在 MCP 的 stdout(stdio transport 上是协议流)
41654
+ stdio: ['ignore', 'pipe', 'ignore'],
41655
+ windowsHide: true,
41656
+ });
41657
+ }
41658
+ catch {
41659
+ return undefined;
41660
+ }
41661
+ }
41662
+ /**
41663
+ * 读系统级不可变标识。**只读一次**,失败即降级。
41664
+ * `platform` 参数只为测试注入,正常运行不传。
41665
+ */
41666
+ function readSystemAnchor(platform = process.platform) {
41667
+ if (platform === 'darwin') {
41668
+ const out = execQuietly('ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice']);
41669
+ const matched = out && /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out);
41670
+ if (matched?.[1]) {
41671
+ return { value: matched[1].trim(), source: 'platform-uuid' };
41672
+ }
41673
+ return undefined;
41674
+ }
41675
+ if (platform === 'win32') {
41676
+ const out = execQuietly('reg', [
41677
+ 'query',
41678
+ 'HKLM\\SOFTWARE\\Microsoft\\Cryptography',
41679
+ '/v',
41680
+ 'MachineGuid',
41681
+ ]);
41682
+ // 形如:` MachineGuid REG_SZ 3f2a....`(分隔符是多个空格或 tab)
41683
+ const matched = out && /MachineGuid\s+REG_SZ\s+([0-9a-fA-F-]{8,})/.exec(out);
41684
+ if (matched?.[1]) {
41685
+ return { value: matched[1].trim().toLowerCase(), source: 'machine-guid' };
41686
+ }
41687
+ return undefined;
41688
+ }
41689
+ // linux / openharmony / 其他类 Unix:先 /etc/machine-id,再 dbus 那份
41690
+ for (const candidate of ['/etc/machine-id', '/var/lib/dbus/machine-id']) {
41691
+ const raw = readFileQuietly(candidate);
41692
+ if (raw && isUsableMachineId(raw)) {
41693
+ return { value: raw.toLowerCase(), source: 'machine-id' };
41694
+ }
41695
+ }
41696
+ return undefined;
41697
+ }
41698
+ function isVirtualNic(name) {
41699
+ const lower = name.toLowerCase();
41700
+ return VIRTUAL_NIC_PREFIXES.some((prefix) => lower.startsWith(prefix));
41701
+ }
41702
+ /**
41703
+ * 非内部、非虚拟、MAC 不是全 0 / 广播地址的网卡 MAC,去重后排序。
41704
+ * 排序是为了让指纹与网卡枚举顺序无关(枚举顺序在重启后可能变)。
41705
+ */
41706
+ function physicalMacAddresses(interfaces = os_1.default.networkInterfaces()) {
41707
+ const macs = new Set();
41708
+ for (const [name, list] of Object.entries(interfaces)) {
41709
+ if (!list || isVirtualNic(name)) {
41710
+ continue;
41711
+ }
41712
+ for (const nic of list) {
41713
+ const mac = nic?.mac?.toLowerCase();
41714
+ if (!nic || nic.internal || !mac) {
41715
+ continue;
41716
+ }
41717
+ if (mac === '00:00:00:00:00:00' || mac === 'ff:ff:ff:ff:ff:ff') {
41718
+ continue;
41719
+ }
41720
+ macs.add(mac);
41721
+ }
41722
+ }
41723
+ return [...macs].sort();
41724
+ }
41725
+ /** 取不到系统锚点时的兜底信号:**不含虚拟网卡**,主机名与 CPU 型号足够稳。 */
41726
+ function collectHostSignal() {
41727
+ let hostname = 'unknown-host';
41728
+ try {
41729
+ hostname = os_1.default.hostname() || hostname;
41730
+ }
41731
+ catch {
41732
+ // 主机名读不到不影响降级:CPU + MAC 仍可用
41733
+ }
41734
+ let cpu = '';
41735
+ try {
41736
+ cpu = os_1.default
41737
+ .cpus()
41738
+ .map((item) => item.model)
41739
+ .join(',');
41740
+ }
41741
+ catch {
41742
+ // 同上
41743
+ }
41744
+ let macs = [];
41745
+ try {
41746
+ macs = physicalMacAddresses();
41747
+ }
41748
+ catch {
41749
+ // 同上
41750
+ }
41751
+ const parts = [hostname, cpu, ...macs].filter((part) => Boolean(part));
41752
+ return {
41753
+ value: parts.join('|'),
41754
+ source: macs.length > 0 ? 'host-fallback' : 'host-only',
41755
+ };
41756
+ }
41757
+ /**
41758
+ * 计算匿名设备指纹(SHA256 前 32 位 hex),并返回这次用的锚点来源 ——
41759
+ * 来源只进 debug 日志,用于上线后确认「到底有多少机器走到了兜底」。
41760
+ */
41761
+ function computeDeviceId(platform = process.platform) {
41762
+ const anchor = readSystemAnchor(platform) ?? collectHostSignal();
41763
+ return { deviceId: sha256Short(anchor.value), source: anchor.source };
41764
+ }
41765
+
41766
+
40398
41767
  /***/ },
40399
41768
 
40400
41769
  /***/ 5048
@@ -40535,6 +41904,112 @@ async function requestChoiceElicitation(server, params) {
40535
41904
  }
40536
41905
 
40537
41906
 
41907
+ /***/ },
41908
+
41909
+ /***/ 1498
41910
+ (__unused_webpack_module, exports, __webpack_require__) {
41911
+
41912
+
41913
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
41914
+ exports.ENV_SOURCES = void 0;
41915
+ exports.noteEnvBinding = noteEnvBinding;
41916
+ exports.clearEnvBinding = clearEnvBinding;
41917
+ exports.hasEnvSlot = hasEnvSlot;
41918
+ exports.readReportedEnv = readReportedEnv;
41919
+ exports.readDisplayedEnvId = readDisplayedEnvId;
41920
+ exports.runWithEnvSlot = runWithEnvSlot;
41921
+ exports.runInEnvSlot = runInEnvSlot;
41922
+ exports.peekLocalEnvBinding = peekLocalEnvBinding;
41923
+ const node_async_hooks_1 = __webpack_require__(4579);
41924
+ const project_config_js_1 = __webpack_require__(9175);
41925
+ /**
41926
+ * Where this resolution got its env id.
41927
+ * The first four values are already in shipped queries. Do not rename them.
41928
+ * `default` stays `default` (an empty result, not a rename to `unresolved`).
41929
+ */
41930
+ exports.ENV_SOURCES = [
41931
+ "cloudBaseOptions",
41932
+ "process.env.CLOUDBASE_ENV_ID",
41933
+ "envManager.cachedEnvId",
41934
+ "project",
41935
+ "login_state",
41936
+ "auto_bound",
41937
+ "default",
41938
+ ];
41939
+ const envCall = new node_async_hooks_1.AsyncLocalStorage();
41940
+ function isReportableEnvId(envId) {
41941
+ return !!envId && envId !== "local" && envId !== "unknown";
41942
+ }
41943
+ /** Write into the current slot. Outside a slot this is a no-op and does not touch process globals. */
41944
+ function noteEnvBinding(binding) {
41945
+ const slot = envCall.getStore();
41946
+ if (!slot || binding.source === "default" || !isReportableEnvId(binding.envId)) {
41947
+ return;
41948
+ }
41949
+ slot.binding = { envId: binding.envId, source: binding.source };
41950
+ }
41951
+ function clearEnvBinding() {
41952
+ const slot = envCall.getStore();
41953
+ if (slot) {
41954
+ slot.binding = null;
41955
+ }
41956
+ }
41957
+ function hasEnvSlot() {
41958
+ return envCall.getStore() !== undefined;
41959
+ }
41960
+ function readReportedEnv() {
41961
+ const binding = envCall.getStore()?.binding;
41962
+ if (!binding || binding.source === "default" || !isReportableEnvId(binding.envId)) {
41963
+ return { envId: "unknown", envIdSource: "default" };
41964
+ }
41965
+ return { envId: binding.envId, envIdSource: binding.source };
41966
+ }
41967
+ /**
41968
+ * Env id shown on the start page, the usage card, and confirmation text.
41969
+ * An explicit envId on this call wins, matching the resolver ladder.
41970
+ * Otherwise the current slot (login state, project, cache, or env var already noted).
41971
+ * Does not read process.env or the manager cache: on a hosted request those belong to the server machine.
41972
+ */
41973
+ function readDisplayedEnvId(explicitEnvId) {
41974
+ if (isReportableEnvId(explicitEnvId)) {
41975
+ return explicitEnvId;
41976
+ }
41977
+ const reported = readReportedEnv();
41978
+ if (reported.envIdSource !== "default") {
41979
+ return reported.envId;
41980
+ }
41981
+ return null;
41982
+ }
41983
+ function runWithEnvSlot(slot, fn) {
41984
+ return envCall.run(slot, fn);
41985
+ }
41986
+ /** One fresh slot. Tests and any caller that must not share another request's binding. */
41987
+ function runInEnvSlot(fn) {
41988
+ return envCall.run({ binding: null }, fn);
41989
+ }
41990
+ /**
41991
+ * Read-only snapshot for process start. Does not call setEnvId and does not list envs.
41992
+ * `cachedEnvId` is passed in so this module does not import the manager (that import cycles).
41993
+ */
41994
+ function peekLocalEnvBinding(input) {
41995
+ if (isReportableEnvId(input.cachedEnvId)) {
41996
+ return { envId: input.cachedEnvId, source: "envManager.cachedEnvId" };
41997
+ }
41998
+ const fromEnv = process.env.CLOUDBASE_ENV_ID;
41999
+ if (isReportableEnvId(fromEnv)) {
42000
+ return { envId: fromEnv, source: "process.env.CLOUDBASE_ENV_ID" };
42001
+ }
42002
+ const projectEnvId = (0, project_config_js_1.readProjectEnvId)();
42003
+ if (isReportableEnvId(projectEnvId)) {
42004
+ return { envId: projectEnvId, source: "project" };
42005
+ }
42006
+ if (isReportableEnvId(input.loginEnvId)) {
42007
+ return { envId: input.loginEnvId, source: "login_state" };
42008
+ }
42009
+ return { envId: null, source: "default" };
42010
+ }
42011
+
42012
+
40538
42013
  /***/ },
40539
42014
 
40540
42015
  /***/ 1194
@@ -42210,6 +43685,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
42210
43685
  };
42211
43686
  Object.defineProperty(exports, "__esModule", ({ value: true }));
42212
43687
  exports.reportToolkitSessionInitialized = exports.reportToolkitLifecycle = exports.reportResourceRead = exports.reportElicitationResult = exports.reportToolCall = exports.MCP_UI_EXTENSION = exports.telemetryReporter = void 0;
43688
+ exports.seedStdioEnvBinding = seedStdioEnvBinding;
42213
43689
  exports.extractMcpClientInfo = extractMcpClientInfo;
42214
43690
  exports.readMcpClientInfoFromServer = readMcpClientInfoFromServer;
42215
43691
  exports.normalizeClientName = normalizeClientName;
@@ -42223,6 +43699,8 @@ const os_1 = __importDefault(__webpack_require__(8116));
42223
43699
  const auth_js_1 = __webpack_require__(561);
42224
43700
  const cloudbase_manager_js_1 = __webpack_require__(3109);
42225
43701
  const cloud_mode_js_1 = __webpack_require__(8694);
43702
+ const env_binding_js_1 = __webpack_require__(1498);
43703
+ const device_id_js_1 = __webpack_require__(6953);
42226
43704
  const logger_js_1 = __webpack_require__(781);
42227
43705
  const site_map_js_1 = __webpack_require__(4533);
42228
43706
  /**
@@ -42278,7 +43756,7 @@ class TelemetryReporter {
42278
43756
  const arch = os_1.default.arch(); // 系统架构
42279
43757
  // Build-time injected version; guard for vitest / non-webpack runtimes.
42280
43758
  const mcpVersion = process.env.npm_package_version ||
42281
- ( true ? "2.36.0" : 0);
43759
+ ( true ? "2.36.2" : 0);
42282
43760
  return {
42283
43761
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
42284
43762
  deviceId: this.deviceId,
@@ -42291,26 +43769,25 @@ class TelemetryReporter {
42291
43769
  }
42292
43770
  /**
42293
43771
  * 获取设备唯一标识
42294
- * 基于主机名、CPU信息和MAC地址生成匿名设备指纹
43772
+ *
43773
+ * 锚点与降级顺序见 `utils/device-id.ts`:优先系统级不可变标识(IOPlatformUUID /
43774
+ * MachineGuid / machine-id),取不到才退到 hostname + CPU + 过滤后的物理网卡 MAC。
43775
+ * 容器里 hostname 每次重建都变、macOS 私有 Wi-Fi 地址让 MAC 随 SSID 漂移,
43776
+ * 两者都不能当锚点 —— 旧实现把它们全拼进去,同一台 Mac 一天能上报成几十个设备。
43777
+ *
43778
+ * 子进程(ioreg / reg)只在这里跑一次(构造时),1s 超时即降级。
42295
43779
  */
42296
43780
  getDeviceId() {
42297
43781
  try {
42298
- // 获取设备信息组合
42299
- const deviceInfo = [
42300
- os_1.default.hostname(),
42301
- os_1.default.cpus().map((cpu) => cpu.model).join(','),
42302
- Object.values(os_1.default.networkInterfaces())
42303
- .reduce((acc, val) => acc.concat(val || []), [])
42304
- .filter((nic) => nic && !nic.internal && nic.mac)
42305
- .map((nic) => nic.mac)
42306
- .join(',')
42307
- ].join('|');
42308
- // 生成SHA256哈希作为设备ID
42309
- return crypto_1.default.createHash('sha256').update(deviceInfo).digest('hex').substring(0, 32);
43782
+ const { deviceId, source } = (0, device_id_js_1.computeDeviceId)();
43783
+ (0, logger_js_1.debug)('device_id_resolved', { source, deviceId: deviceId.substring(0, 8) + '...' });
43784
+ return deviceId;
42310
43785
  }
42311
43786
  catch (err) {
42312
- // 如果获取设备信息失败,生成随机ID
42313
- return crypto_1.default.randomBytes(16).toString('hex');
43787
+ // 走到这里说明连 hostname / CPU 都读不到:用固定串兜底,**绝不能**用随机数
43788
+ // (随机数等于每次启动都是一台新设备,正是这次要消灭的行为)。
43789
+ (0, logger_js_1.debug)('获取设备标识失败,回落到固定串', err instanceof Error ? err : new Error(String(err)));
43790
+ return crypto_1.default.createHash('sha256').update('cloudbase-mcp|no-device-signal').digest('hex').substring(0, 32);
42314
43791
  }
42315
43792
  }
42316
43793
  /**
@@ -42426,39 +43903,40 @@ class TelemetryReporter {
42426
43903
  // 创建全局实例
42427
43904
  exports.telemetryReporter = new TelemetryReporter();
42428
43905
  /**
42429
- * 解析环境ID
42430
- * 优先级:传入配置 > envManager缓存 > 环境变量 > unknown
43906
+ * Reports read the current AsyncLocalStorage slot.
43907
+ * They do not re-read envManager or CLOUDBASE_ENV_ID.
43908
+ * An explicit envId on this call is only used when the slot is still empty
43909
+ * (hosted initialized, before a resolver runs, when the request already carried one).
43910
+ *
43911
+ * The source list lives in `env-binding.ts` (`ENV_SOURCES`) — one definition only.
42431
43912
  */
42432
43913
  function resolveEnvId(cloudBaseOptions) {
42433
- let envId = 'unknown';
42434
- let envIdSource = 'unknown';
42435
- try {
42436
- if (cloudBaseOptions?.envId) {
42437
- envId = cloudBaseOptions.envId;
42438
- envIdSource = 'cloudBaseOptions';
42439
- }
42440
- else {
42441
- const cachedEnvId = (0, cloudbase_manager_js_1.getCachedEnvId)();
42442
- if (cachedEnvId) {
42443
- envId = cachedEnvId;
42444
- envIdSource = 'envManager.cachedEnvId';
42445
- }
42446
- else if (process.env.CLOUDBASE_ENV_ID) {
42447
- envId = process.env.CLOUDBASE_ENV_ID;
42448
- envIdSource = 'process.env.CLOUDBASE_ENV_ID';
42449
- }
42450
- else {
42451
- envId = 'unknown';
42452
- envIdSource = 'default';
42453
- }
42454
- }
43914
+ const reported = (0, env_binding_js_1.readReportedEnv)();
43915
+ if (reported.envIdSource !== "default") {
43916
+ return reported;
42455
43917
  }
42456
- catch (err) {
42457
- (0, logger_js_1.debug)('获取环境ID失败,遥测数据将使用 unknown', err instanceof Error ? err : new Error(String(err)));
42458
- envId = 'unknown';
42459
- envIdSource = 'error';
43918
+ if (cloudBaseOptions?.envId) {
43919
+ return { envId: cloudBaseOptions.envId, envIdSource: "cloudBaseOptions" };
43920
+ }
43921
+ return { envId: "unknown", envIdSource: "default" };
43922
+ }
43923
+ /**
43924
+ * Stdio start: reuse the login warm (one peekLoginState per process) and one project-file read.
43925
+ * Does not call setEnvId and does not list environments. Cloud mode does not read local files.
43926
+ */
43927
+ async function seedStdioEnvBinding(slot) {
43928
+ if ((0, cloud_mode_js_1.isCloudMode)()) {
43929
+ return;
43930
+ }
43931
+ await warmLocalLoginState();
43932
+ const loginEnvId = typeof localLoginState?.envId === "string" ? localLoginState.envId : null;
43933
+ const binding = (0, env_binding_js_1.peekLocalEnvBinding)({
43934
+ cachedEnvId: (0, cloudbase_manager_js_1.getCachedEnvId)(),
43935
+ loginEnvId,
43936
+ });
43937
+ if (binding.envId && binding.source !== "default") {
43938
+ slot.binding = binding;
42460
43939
  }
42461
- return { envId, envIdSource };
42462
43940
  }
42463
43941
  /**
42464
43942
  * Normalize SDK Implementation / clientInfo into telemetry-safe fields.
@@ -42765,6 +44243,9 @@ const reportToolCall = async (params) => {
42765
44243
  duration: params.duration !== undefined ? String(params.duration) : undefined,
42766
44244
  error: params.error ? params.error.substring(0, 200) : undefined, // 限制错误信息长度
42767
44245
  envId: envId || 'unknown',
44246
+ // 取值来源(cloudBaseOptions / envManager.cachedEnvId / process.env / default):
44247
+ // 这一列就是「envId 为什么是 unknown」的判据,别再靠猜
44248
+ envIdSource,
42768
44249
  region: siteRegion.region,
42769
44250
  site: siteRegion.site,
42770
44251
  login_uin: loginUin,
@@ -42778,6 +44259,13 @@ const reportToolCall = async (params) => {
42778
44259
  if (params.action !== undefined) {
42779
44260
  eventData.action = params.action;
42780
44261
  }
44262
+ // 环境准备结果:拿不到就不占列(与能力字段同一纪律,不写 unknown 假值)
44263
+ if (params.envSetupStatus !== undefined) {
44264
+ eventData.envSetupStatus = params.envSetupStatus;
44265
+ }
44266
+ if (params.envSetupFailReason !== undefined) {
44267
+ eventData.envSetupFailReason = params.envSetupFailReason;
44268
+ }
42781
44269
  // 添加入参信息(如果提供)
42782
44270
  if (params.inputParams !== undefined) {
42783
44271
  try {
@@ -42899,7 +44387,15 @@ const reportToolkitLifecycle = async (params) => {
42899
44387
  // 安全获取环境ID,优先使用传入的配置
42900
44388
  const { envId, envIdSource } = resolveEnvId(params.cloudBaseOptions);
42901
44389
  const siteRegion = resolveTelemetrySiteRegion(params.cloudBaseOptions);
42902
- const loginUin = await resolveLoginUin(params.cloudBaseOptions, { allowResolve: false });
44390
+ // `start` 在进程最早期发出:此时既没有宿主注入的 uin,也还没人 warm 过本地登录态。
44391
+ // 卡死 `allowResolve: false` 就只能读到空缓存 → 恒 'unknown'(2026-10-26 实测:这一列
44392
+ // 有真实 uin 的行是 0)。start 允许解析:`warmLocalLoginState()` 进程内只跑一次
44393
+ // (读本地 auth.json;临时密钥过期时可能续期一次,与工具调用本来就要付的开销同源),
44394
+ // 不会挂到每次上报上。
44395
+ // `exit` 仍保持不允许 —— 退出路径不能等,拿不到就是 unknown,`start` 那次已经把值带出去了。
44396
+ const loginUin = await resolveLoginUin(params.cloudBaseOptions, {
44397
+ allowResolve: params.event !== 'exit',
44398
+ });
42903
44399
  (0, logger_js_1.debug)('[telemetry] 生命周期事件 envId 获取结果', {
42904
44400
  event: params.event,
42905
44401
  envId,
@@ -42914,6 +44410,9 @@ const reportToolkitLifecycle = async (params) => {
42914
44410
  exitCode: params.exitCode !== undefined ? String(params.exitCode) : undefined,
42915
44411
  error: params.error ? params.error.substring(0, 200) : undefined, // 限制错误信息长度
42916
44412
  envId: envId || 'unknown',
44413
+ // 取值来源(cloudBaseOptions / envManager.cachedEnvId / process.env / default):
44414
+ // 这一列就是「envId 为什么是 unknown」的判据,别再靠猜
44415
+ envIdSource,
42917
44416
  region: siteRegion.region,
42918
44417
  site: siteRegion.site,
42919
44418
  login_uin: loginUin,
@@ -42970,12 +44469,15 @@ exports.reportToolkitLifecycle = reportToolkitLifecycle;
42970
44469
  */
42971
44470
  const reportToolkitSessionInitialized = async (params) => {
42972
44471
  const { nodeVersion, osType, osRelease, arch, mcpVersion } = exports.telemetryReporter.getUserAgent();
42973
- const { envId } = resolveEnvId(params.cloudBaseOptions);
44472
+ const { envId, envIdSource } = resolveEnvId(params.cloudBaseOptions);
42974
44473
  const siteRegion = resolveTelemetrySiteRegion(params.cloudBaseOptions);
42975
- const loginUin = await resolveLoginUin(params.cloudBaseOptions, { allowResolve: false });
44474
+ // 与 `start` 同理:`initialized` 紧跟在 `start` 之后,也是不允许等的分支就会白报一条 unknown。
44475
+ // 这里放开解析;若 `start` 已经 warm 过,这一句直接命中进程内缓存,零额外成本。
44476
+ const loginUin = await resolveLoginUin(params.cloudBaseOptions);
42976
44477
  const eventData = {
42977
44478
  event: 'initialized',
42978
44479
  envId: envId || 'unknown',
44480
+ envIdSource,
42979
44481
  region: siteRegion.region,
42980
44482
  site: siteRegion.site,
42981
44483
  login_uin: loginUin,
@@ -43149,7 +44651,6 @@ var __webpack_unused_export__;
43149
44651
  __webpack_unused_export__ = ({ value: true });
43150
44652
  exports.applyCategoryAnnotationMeta = applyCategoryAnnotationMeta;
43151
44653
  exports.wrapServerWithTelemetry = wrapServerWithTelemetry;
43152
- const cloudbase_manager_js_1 = __webpack_require__(3109);
43153
44654
  const cloud_mode_js_1 = __webpack_require__(8694);
43154
44655
  const logger_js_1 = __webpack_require__(781);
43155
44656
  const telemetry_js_1 = __webpack_require__(5198);
@@ -43212,8 +44713,56 @@ function contributionHint(server) {
43212
44713
  ].join("\n");
43213
44714
  }
43214
44715
  /**
43215
- * 创建包装后的处理函数,添加数据上报功能
44716
+ * 从 `auth` 的返回里取出「环境准备」的结果,供遥测上报。
44717
+ *
44718
+ * 为什么要从返回体里抠:`env_setup_status` / `env_setup_failure.reason` 只存在于
44719
+ * `auth` 的 payload 里,不是 `reportToolCall` 的入参。而工具 wrapper 是唯一同时
44720
+ * 看得见 args 和 result 的地方。
44721
+ *
44722
+ * 现状(2026-10-27):这两项只回给模型、没进埋点,于是「无环境的用户卡在购买页、
44723
+ * 实名、还是 CAM 授权」在数据里长得一模一样,加文案也无法验收。
43216
44724
  */
44725
+ function readEnvSetupOutcome(result) {
44726
+ const rec = result;
44727
+ if (!rec || typeof rec !== 'object')
44728
+ return undefined;
44729
+ // 优先 structuredContent(MCP 规范位),退到 content[0].text 里的 JSON
44730
+ const candidates = [];
44731
+ if (rec.structuredContent)
44732
+ candidates.push(rec.structuredContent);
44733
+ const content = Array.isArray(rec.content) ? rec.content : [];
44734
+ for (const item of content) {
44735
+ const text = item?.text;
44736
+ if (typeof text === 'string' && text.trim().startsWith('{')) {
44737
+ try {
44738
+ candidates.push(JSON.parse(text));
44739
+ }
44740
+ catch {
44741
+ /* 不是 JSON 就跳过,不要因为解析失败影响正常返回 */
44742
+ }
44743
+ }
44744
+ }
44745
+ for (const payload of candidates) {
44746
+ const p = payload;
44747
+ const status = typeof p?.env_setup_status === 'string' ? p.env_setup_status : undefined;
44748
+ if (!status)
44749
+ continue;
44750
+ const failure = p?.env_setup_failure;
44751
+ const reason = typeof failure?.reason === 'string' ? failure.reason : undefined;
44752
+ // 把两个布尔压进 reason 的后缀:一列就能看清「卡在哪」,
44753
+ // 单看 reason 只知道「建环境失败」,不知道是实名还是 CAM 授权拦住的
44754
+ const gates = [];
44755
+ if (failure?.need_real_name_auth === true)
44756
+ gates.push('realname');
44757
+ if (failure?.need_cam_auth === true)
44758
+ gates.push('cam');
44759
+ return {
44760
+ status,
44761
+ reason: reason ? (gates.length ? `${reason}+${gates.join('+')}` : reason) : undefined,
44762
+ };
44763
+ }
44764
+ return undefined;
44765
+ }
43217
44766
  /**
43218
44767
  * 工具自己返回的失败:结构化 `{ success: false }`(`isBusinessFailureToolResult` 已覆盖
43219
44768
  * 顶层 / structuredContent / JSON 文本三种挂法),**或**已经显式带 `isError: true` 的回执。
@@ -43233,7 +44782,7 @@ function isReturnedFailureResult(result) {
43233
44782
  * 所以两条路径对同一个底层错误得到的文本逐字相同(🆔 RequestId + 📦 版本页脚都在这里)。
43234
44783
  */
43235
44784
  function buildFailureClientText(errorMessage, requestId, server) {
43236
- const mcpVersion = true ? "2.36.0" : 0;
44785
+ const mcpVersion = true ? "2.36.2" : 0;
43237
44786
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
43238
44787
  return `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n${contributionHint(server)}`;
43239
44788
  }
@@ -43293,12 +44842,18 @@ function withEnhancedFailureText(result, errorMessage, requestId, server) {
43293
44842
  }
43294
44843
  return result;
43295
44844
  }
44845
+ /**
44846
+ * 创建包装后的处理函数,添加数据上报功能
44847
+ */
43296
44848
  function createWrappedHandler(name, handler, server) {
43297
44849
  return async (args, extra) => {
43298
44850
  const startTime = Date.now();
43299
44851
  let success = false;
43300
44852
  let errorMessage;
43301
44853
  let requestId;
44854
+ // 提到 try 外:finally 里要读 auth 的环境准备结果(env_setup_status / failure.reason)。
44855
+ // 护栏拦截的早退路径不会赋值,finally 里按 undefined 处理。
44856
+ let result;
43302
44857
  try {
43303
44858
  (0, logger_js_1.debug)(`开始执行工具: ${name}`, { args: sanitizeArgs(args) });
43304
44859
  // In test environment, skip logger to avoid potential blocking
@@ -43330,7 +44885,7 @@ function createWrappedHandler(name, handler, server) {
43330
44885
  return guardDecision.result;
43331
44886
  }
43332
44887
  // 执行原始处理函数(extra 一并透传,工具可据此拿到在途请求 id)
43333
- const result = await handler(args, extra);
44888
+ result = await handler(args, extra);
43334
44889
  // 工具**自己返回**的失败(`{ success: false }` 结构化载荷,或已经带 `isError: true` 的
43335
44890
  // 失败回执)与"抛错"是同一件事:遥测要按失败记,文案要吃同一套收尾(增强 + RequestId +
43336
44891
  // 版本页脚)。护栏拦截的返回走不到这里——它在上面就 return 了,且它的载荷既没有
@@ -43411,24 +44966,19 @@ function createWrappedHandler(name, handler, server) {
43411
44966
  const isTestEnvironment = false || process.env.VITEST === "true";
43412
44967
  if (!isTestEnvironment) {
43413
44968
  const duration = Date.now() - startTime;
43414
- // 如果 server.cloudBaseOptions 为空或没有 envId,尝试从缓存获取并更新
43415
- let cloudBaseOptions = server.cloudBaseOptions;
43416
- if (!cloudBaseOptions?.envId) {
43417
- const cachedEnvId = (0, cloudbase_manager_js_1.getCachedEnvId)();
43418
- if (cachedEnvId) {
43419
- cloudBaseOptions = { ...cloudBaseOptions, envId: cachedEnvId };
43420
- }
43421
- }
44969
+ const envSetup = name === 'auth' ? readEnvSetupOutcome(result) : undefined;
43422
44970
  // 异步上报,不阻塞工具返回
43423
44971
  (0, telemetry_js_1.reportToolCall)({
43424
44972
  toolName: name,
44973
+ envSetupStatus: envSetup?.status,
44974
+ envSetupFailReason: envSetup?.reason,
43425
44975
  success,
43426
44976
  requestId,
43427
44977
  duration,
43428
44978
  error: errorMessage,
43429
44979
  inputParams: sanitizeArgs(args), // 添加入参上报
43430
44980
  action: args?.action, // 工具级 action(如 auth 的 status/set_env)
43431
- cloudBaseOptions: cloudBaseOptions, // 传递 CloudBase 配置(可能已更新)
44981
+ cloudBaseOptions: server.cloudBaseOptions,
43432
44982
  ide: server.ide || process.env.INTEGRATION_IDE, // 传递集成IDE信息
43433
44983
  client: server.client || process.env.CLOUDBASE_MCP_CLIENT, // 传递 MCP client 来源标识
43434
44984
  mcpClientInfo: (0, telemetry_js_1.readMcpClientInfoFromServer)(server),
@@ -43620,7 +45170,7 @@ var x = (y) => {
43620
45170
  var x = {}; __webpack_require__.d(x, y); return x
43621
45171
  }
43622
45172
  var y = (x) => (() => (x))
43623
- module.exports = x({ ["spawn"]: () => (__WEBPACK_EXTERNAL_MODULE_child_process__.spawn) });
45173
+ module.exports = x({ ["execFileSync"]: () => (__WEBPACK_EXTERNAL_MODULE_child_process__.execFileSync), ["spawn"]: () => (__WEBPACK_EXTERNAL_MODULE_child_process__.spawn) });
43624
45174
 
43625
45175
  /***/ },
43626
45176
 
@@ -43680,6 +45230,17 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_net__;
43680
45230
 
43681
45231
  /***/ },
43682
45232
 
45233
+ /***/ 4579
45234
+ (module, __unused_webpack_exports, __webpack_require__) {
45235
+
45236
+ var x = (y) => {
45237
+ var x = {}; __webpack_require__.d(x, y); return x
45238
+ }
45239
+ var y = (x) => (() => (x))
45240
+ module.exports = x({ ["AsyncLocalStorage"]: () => (__WEBPACK_EXTERNAL_MODULE_node_async_hooks_1fa35564__.AsyncLocalStorage) });
45241
+
45242
+ /***/ },
45243
+
43683
45244
  /***/ 2561
43684
45245
  (module, __unused_webpack_exports, __webpack_require__) {
43685
45246
 
@@ -43817,7 +45378,7 @@ var __webpack_exports__ = {};
43817
45378
  var exports = __webpack_exports__;
43818
45379
 
43819
45380
  Object.defineProperty(exports, "__esModule", ({ value: true }));
43820
- exports.validateTimerCron = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.formatRuntimeList = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = exports.HOSTED_CLIENT_INFO_CACHE_TTL_MS = exports.HOSTED_CLIENT_INFO_CACHE_CAPACITY = exports.handleHostedMcpRequest = exports.warmupHostedResources = exports.RAW_IDE_FILE_MAPPINGS = exports.simplifyEnvList = exports.envManager = exports.createCloudBaseManagerWithOptions = exports.resetCloudBaseManagerCache = exports.getEnvId = exports.getCloudBaseManager = exports.shouldRegisterTool = exports.getCloudModeStatus = exports.enableCloudMode = exports.isCloudMode = exports.logout = exports.getLoginState = exports.warn = exports.error = exports.info = exports.reportToolCall = exports.reportToolkitLifecycle = exports.telemetryReporter = exports.StdioServerTransport = exports.getDefaultServer = exports.createCloudBaseMcpServer = void 0;
45381
+ exports.validateTimerCron = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.formatRuntimeList = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = exports.HOSTED_CLIENT_INFO_CACHE_TTL_MS = exports.HOSTED_CLIENT_INFO_CACHE_CAPACITY = exports.handleHostedMcpRequest = exports.warmupHostedResources = exports.RAW_IDE_FILE_MAPPINGS = exports.simplifyEnvList = exports.envManager = exports.createCloudBaseManagerWithOptions = exports.resetCloudBaseManagerCache = exports.getEnvId = exports.getCloudBaseManager = exports.runWithEnvSlot = exports.ENV_SOURCES = exports.shouldRegisterTool = exports.getCloudModeStatus = exports.enableCloudMode = exports.isCloudMode = exports.logout = exports.getLoginState = exports.warn = exports.error = exports.info = exports.reportToolCall = exports.reportToolkitLifecycle = exports.telemetryReporter = exports.StdioServerTransport = exports.getDefaultServer = exports.createCloudBaseMcpServer = void 0;
43821
45382
  // CloudBase MCP Server Library
43822
45383
  var server_js_1 = __webpack_require__(6324);
43823
45384
  Object.defineProperty(exports, "createCloudBaseMcpServer", ({ enumerable: true, get: function () { return server_js_1.createCloudBaseMcpServer; } }));
@@ -43837,6 +45398,13 @@ Object.defineProperty(exports, "isCloudMode", ({ enumerable: true, get: function
43837
45398
  Object.defineProperty(exports, "enableCloudMode", ({ enumerable: true, get: function () { return cloud_mode_js_1.enableCloudMode; } }));
43838
45399
  Object.defineProperty(exports, "getCloudModeStatus", ({ enumerable: true, get: function () { return cloud_mode_js_1.getCloudModeStatus; } }));
43839
45400
  Object.defineProperty(exports, "shouldRegisterTool", ({ enumerable: true, get: function () { return cloud_mode_js_1.shouldRegisterTool; } }));
45401
+ /**
45402
+ * 环境绑定槽。外部宿主(自己起 transport 的那种)要上报真实的 envId,
45403
+ * 就得在自己的一次 run 里跑 server 的消息处理,否则上报恒为 `default`。
45404
+ */
45405
+ var env_binding_js_1 = __webpack_require__(1498);
45406
+ Object.defineProperty(exports, "ENV_SOURCES", ({ enumerable: true, get: function () { return env_binding_js_1.ENV_SOURCES; } }));
45407
+ Object.defineProperty(exports, "runWithEnvSlot", ({ enumerable: true, get: function () { return env_binding_js_1.runWithEnvSlot; } }));
43840
45408
  var cloudbase_manager_js_1 = __webpack_require__(3109);
43841
45409
  Object.defineProperty(exports, "getCloudBaseManager", ({ enumerable: true, get: function () { return cloudbase_manager_js_1.getCloudBaseManager; } }));
43842
45410
  Object.defineProperty(exports, "getEnvId", ({ enumerable: true, get: function () { return cloudbase_manager_js_1.getEnvId; } }));
@@ -43869,6 +45437,7 @@ Object.defineProperty(exports, "validateTimerCron", ({ enumerable: true, get: fu
43869
45437
  const __webpack_exports__ALL_SUPPORTED_RUNTIMES = __webpack_exports__.ALL_SUPPORTED_RUNTIMES;
43870
45438
  const __webpack_exports__DEFAULT_NODEJS_RUNTIME = __webpack_exports__.DEFAULT_NODEJS_RUNTIME;
43871
45439
  const __webpack_exports__DEFAULT_RUNTIME = __webpack_exports__.DEFAULT_RUNTIME;
45440
+ const __webpack_exports__ENV_SOURCES = __webpack_exports__.ENV_SOURCES;
43872
45441
  const __webpack_exports__HOSTED_CLIENT_INFO_CACHE_CAPACITY = __webpack_exports__.HOSTED_CLIENT_INFO_CACHE_CAPACITY;
43873
45442
  const __webpack_exports__HOSTED_CLIENT_INFO_CACHE_TTL_MS = __webpack_exports__.HOSTED_CLIENT_INFO_CACHE_TTL_MS;
43874
45443
  const __webpack_exports__RAW_IDE_FILE_MAPPINGS = __webpack_exports__.RAW_IDE_FILE_MAPPINGS;
@@ -43895,10 +45464,11 @@ const __webpack_exports__logout = __webpack_exports__.logout;
43895
45464
  const __webpack_exports__reportToolCall = __webpack_exports__.reportToolCall;
43896
45465
  const __webpack_exports__reportToolkitLifecycle = __webpack_exports__.reportToolkitLifecycle;
43897
45466
  const __webpack_exports__resetCloudBaseManagerCache = __webpack_exports__.resetCloudBaseManagerCache;
45467
+ const __webpack_exports__runWithEnvSlot = __webpack_exports__.runWithEnvSlot;
43898
45468
  const __webpack_exports__shouldRegisterTool = __webpack_exports__.shouldRegisterTool;
43899
45469
  const __webpack_exports__simplifyEnvList = __webpack_exports__.simplifyEnvList;
43900
45470
  const __webpack_exports__telemetryReporter = __webpack_exports__.telemetryReporter;
43901
45471
  const __webpack_exports__validateTimerCron = __webpack_exports__.validateTimerCron;
43902
45472
  const __webpack_exports__warmupHostedResources = __webpack_exports__.warmupHostedResources;
43903
45473
  const __webpack_exports__warn = __webpack_exports__.warn;
43904
- export { __webpack_exports__ALL_SUPPORTED_RUNTIMES as ALL_SUPPORTED_RUNTIMES, __webpack_exports__DEFAULT_NODEJS_RUNTIME as DEFAULT_NODEJS_RUNTIME, __webpack_exports__DEFAULT_RUNTIME as DEFAULT_RUNTIME, __webpack_exports__HOSTED_CLIENT_INFO_CACHE_CAPACITY as HOSTED_CLIENT_INFO_CACHE_CAPACITY, __webpack_exports__HOSTED_CLIENT_INFO_CACHE_TTL_MS as HOSTED_CLIENT_INFO_CACHE_TTL_MS, __webpack_exports__RAW_IDE_FILE_MAPPINGS as RAW_IDE_FILE_MAPPINGS, __webpack_exports__RECOMMENDED_RUNTIMES as RECOMMENDED_RUNTIMES, __webpack_exports__SUPPORTED_NODEJS_RUNTIMES as SUPPORTED_NODEJS_RUNTIMES, __webpack_exports__SUPPORTED_RUNTIMES as SUPPORTED_RUNTIMES, __webpack_exports__StdioServerTransport as StdioServerTransport, __webpack_exports___esModule as __esModule, __webpack_exports__createCloudBaseManagerWithOptions as createCloudBaseManagerWithOptions, __webpack_exports__createCloudBaseMcpServer as createCloudBaseMcpServer, __webpack_exports__enableCloudMode as enableCloudMode, __webpack_exports__envManager as envManager, __webpack_exports__error as error, __webpack_exports__formatRuntimeList as formatRuntimeList, __webpack_exports__getCloudBaseManager as getCloudBaseManager, __webpack_exports__getCloudModeStatus as getCloudModeStatus, __webpack_exports__getDefaultServer as getDefaultServer, __webpack_exports__getEnvId as getEnvId, __webpack_exports__getLoginState as getLoginState, __webpack_exports__handleHostedMcpRequest as handleHostedMcpRequest, __webpack_exports__info as info, __webpack_exports__isCloudMode as isCloudMode, __webpack_exports__logout as logout, __webpack_exports__reportToolCall as reportToolCall, __webpack_exports__reportToolkitLifecycle as reportToolkitLifecycle, __webpack_exports__resetCloudBaseManagerCache as resetCloudBaseManagerCache, __webpack_exports__shouldRegisterTool as shouldRegisterTool, __webpack_exports__simplifyEnvList as simplifyEnvList, __webpack_exports__telemetryReporter as telemetryReporter, __webpack_exports__validateTimerCron as validateTimerCron, __webpack_exports__warmupHostedResources as warmupHostedResources, __webpack_exports__warn as warn, __webpack_exports__ as default };
45474
+ export { __webpack_exports__ALL_SUPPORTED_RUNTIMES as ALL_SUPPORTED_RUNTIMES, __webpack_exports__DEFAULT_NODEJS_RUNTIME as DEFAULT_NODEJS_RUNTIME, __webpack_exports__DEFAULT_RUNTIME as DEFAULT_RUNTIME, __webpack_exports__ENV_SOURCES as ENV_SOURCES, __webpack_exports__HOSTED_CLIENT_INFO_CACHE_CAPACITY as HOSTED_CLIENT_INFO_CACHE_CAPACITY, __webpack_exports__HOSTED_CLIENT_INFO_CACHE_TTL_MS as HOSTED_CLIENT_INFO_CACHE_TTL_MS, __webpack_exports__RAW_IDE_FILE_MAPPINGS as RAW_IDE_FILE_MAPPINGS, __webpack_exports__RECOMMENDED_RUNTIMES as RECOMMENDED_RUNTIMES, __webpack_exports__SUPPORTED_NODEJS_RUNTIMES as SUPPORTED_NODEJS_RUNTIMES, __webpack_exports__SUPPORTED_RUNTIMES as SUPPORTED_RUNTIMES, __webpack_exports__StdioServerTransport as StdioServerTransport, __webpack_exports___esModule as __esModule, __webpack_exports__createCloudBaseManagerWithOptions as createCloudBaseManagerWithOptions, __webpack_exports__createCloudBaseMcpServer as createCloudBaseMcpServer, __webpack_exports__enableCloudMode as enableCloudMode, __webpack_exports__envManager as envManager, __webpack_exports__error as error, __webpack_exports__formatRuntimeList as formatRuntimeList, __webpack_exports__getCloudBaseManager as getCloudBaseManager, __webpack_exports__getCloudModeStatus as getCloudModeStatus, __webpack_exports__getDefaultServer as getDefaultServer, __webpack_exports__getEnvId as getEnvId, __webpack_exports__getLoginState as getLoginState, __webpack_exports__handleHostedMcpRequest as handleHostedMcpRequest, __webpack_exports__info as info, __webpack_exports__isCloudMode as isCloudMode, __webpack_exports__logout as logout, __webpack_exports__reportToolCall as reportToolCall, __webpack_exports__reportToolkitLifecycle as reportToolkitLifecycle, __webpack_exports__resetCloudBaseManagerCache as resetCloudBaseManagerCache, __webpack_exports__runWithEnvSlot as runWithEnvSlot, __webpack_exports__shouldRegisterTool as shouldRegisterTool, __webpack_exports__simplifyEnvList as simplifyEnvList, __webpack_exports__telemetryReporter as telemetryReporter, __webpack_exports__validateTimerCron as validateTimerCron, __webpack_exports__warmupHostedResources as warmupHostedResources, __webpack_exports__warn as warn, __webpack_exports__ as default };