@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.cjs CHANGED
@@ -199828,6 +199828,7 @@ exports.resolveAuthProgressState = resolveAuthProgressState;
199828
199828
  exports.rejectAuthProgressState = rejectAuthProgressState;
199829
199829
  exports.resetAuthProgressState = resetAuthProgressState;
199830
199830
  exports.getCredentialSource = getCredentialSource;
199831
+ exports.isEnvCredentialRemovableByLogout = isEnvCredentialRemovableByLogout;
199831
199832
  exports.peekLoginState = peekLoginState;
199832
199833
  exports.ensureLogin = ensureLogin;
199833
199834
  exports.getLoginState = getLoginState;
@@ -200261,6 +200262,21 @@ function readProjectCredentialFile(projectRoot) {
200261
200262
  return null;
200262
200263
  }
200263
200264
  }
200265
+ /**
200266
+ * 删掉项目级凭据文件。只删这一个文件,目录留着——删目录会顺带清掉同在里面的
200267
+ * `project.json` 等项目配置。
200268
+ *
200269
+ * 不走 toolbox 的 `logout({cwd})`:那个接口会**无条件先删全局凭据**,而「退出本项目」
200270
+ * 不该把用户的账号级登录一起注销掉。
200271
+ */
200272
+ function deleteProjectCredentialFile(projectRoot) {
200273
+ try {
200274
+ (0, node_fs_1.unlinkSync)((0, node_path_1.join)((0, toolbox_1.getProjectDir)(projectRoot), "auth.json"));
200275
+ }
200276
+ catch {
200277
+ // 文件本来就不在:登出是幂等的,不报错
200278
+ }
200279
+ }
200264
200280
  /**
200265
200281
  * 项目级凭据优先于账号级全局登录态。
200266
200282
  *
@@ -200318,6 +200334,32 @@ function clearAdoptedProjectEnv() {
200318
200334
  function isAdoptedProjectCredential(apiKey, envId) {
200319
200335
  return adoptedProjectApiKey !== null && apiKey === adoptedProjectApiKey && envId === adoptedProjectEnvId;
200320
200336
  }
200337
+ /**
200338
+ * env 里的这一份凭据能不能被 `logout()` 清掉。
200339
+ *
200340
+ * 能清掉的只有两种情况:是我们从项目级凭据文件回填的,或者它与当前项目级凭据文件里
200341
+ * 那一份相同(`auth(login_by_api_key)` 写进程 env 的同时也把同一个 key 落到项目级)。
200342
+ * 这两种都能沿 `logout()` 清掉:删项目级文件、再清 env。
200343
+ *
200344
+ * 其余情况是用户自己在 shell 或宿主配置里设的变量,进程删不掉,只能提示他去源头改。
200345
+ */
200346
+ function isEnvCredentialRemovableByLogout() {
200347
+ const apiKey = getCloudBaseApiKeyFromEnv();
200348
+ const envId = process.env.CLOUDBASE_ENV_ID;
200349
+ if (!apiKey || !envId) {
200350
+ return false;
200351
+ }
200352
+ if (isAdoptedProjectCredential(apiKey, envId)) {
200353
+ return true;
200354
+ }
200355
+ try {
200356
+ const stored = readProjectCredentialFile((0, project_config_js_1.requireProjectRoot)());
200357
+ return stored?.apiKey === apiKey && stored?.envId === envId;
200358
+ }
200359
+ catch {
200360
+ return false;
200361
+ }
200362
+ }
200321
200363
  /**
200322
200364
  * 全局凭据文件 `~/.config/.cloudbase/auth.json` 与 CloudBase CLI 共用一份:用户会在终端里
200323
200365
  * `tcb login`,宿主连接器的登录按钮执行的也是 `tcb login --flow web --yes`,两者都在
@@ -200359,6 +200401,8 @@ async function peekLoginState(options) {
200359
200401
  dropStaleCredentialStoreCache();
200360
200402
  credentialSource = null;
200361
200403
  const envVarLoginState = normalizeLoginStateFromEnvVars(options);
200404
+ // 回填的那份 key 换不出临时密钥时置位:项目级文件里是同一份,不必再试一次
200405
+ let projectCredentialSkipped = false;
200362
200406
  if (envVarLoginState) {
200363
200407
  // API Key 模式:需要先用 API Key 换取临时密钥
200364
200408
  if ('_type' in envVarLoginState && envVarLoginState._type === 'api_key') {
@@ -200380,16 +200424,28 @@ async function peekLoginState(options) {
200380
200424
  }
200381
200425
  catch (e) {
200382
200426
  (0, logger_js_1.debug)("peekLoginState: API Key login failed", { error: e instanceof Error ? e.message : String(e) });
200383
- return null;
200427
+ // 我们回填的那份失效(密钥被撤、环境被删):先清掉残留,别让它一直占着 env
200428
+ // 把状态查询、登出、重新登录全堵死;项目文件本身不动,用户重新登录会覆盖它。
200429
+ // 清完继续往下走,让账号级登录态接上。
200430
+ if (isAdoptedProjectCredential(envVarLoginState.apiKey, envVarLoginState.envId)) {
200431
+ clearAdoptedProjectEnv();
200432
+ projectCredentialSkipped = true;
200433
+ }
200434
+ else {
200435
+ // 用户自己在 shell / 宿主配置里设的变量,登不登得上由他那边决定
200436
+ return null;
200437
+ }
200384
200438
  }
200385
200439
  }
200386
- // 腾讯云密钥模式:直接返回
200387
- (0, logger_js_1.debug)("loginByApiSecret");
200388
- credentialSource = "env";
200389
- return envVarLoginState;
200440
+ if (!projectCredentialSkipped) {
200441
+ // 腾讯云密钥模式:直接返回
200442
+ (0, logger_js_1.debug)("loginByApiSecret");
200443
+ credentialSource = "env";
200444
+ return envVarLoginState;
200445
+ }
200390
200446
  }
200391
200447
  // 项目级凭据优先于账号级登录态(与 readProjectEnvId 同一原则)
200392
- const projectLoginState = await resolveProjectLoginState();
200448
+ const projectLoginState = projectCredentialSkipped ? null : await resolveProjectLoginState();
200393
200449
  if (projectLoginState) {
200394
200450
  credentialSource = "project";
200395
200451
  return projectLoginState;
@@ -200488,21 +200544,44 @@ async function ensureLogin(options) {
200488
200544
  async function getLoginState(options) {
200489
200545
  return ensureLogin(options);
200490
200546
  }
200547
+ /**
200548
+ * 退出登录:只清「当前生效的那一份」。
200549
+ *
200550
+ * 项目级凭据优先于账号级登录态,所以项目里有 API Key 时,登出只删项目文件、清掉
200551
+ * 回填的环境变量,账号登录原样保留(删完它自动接上)。再调一次才轮到全局。
200552
+ *
200553
+ * 不把 cwd 交给 toolbox 的 `logout()`:那个接口会**无条件先删全局凭据**,
200554
+ * 「退出本项目」不该把用户的账号级登录一起注销掉。
200555
+ */
200491
200556
  async function logout(_options) {
200492
200557
  // 先同步缓存再删:否则 toolbox 会把本进程启动时的陈旧快照写回盘,
200493
200558
  // 把外部(CLI / 宿主登录按钮)刚写入的凭据一起抹掉。
200494
200559
  dropStaleCredentialStoreCache();
200495
- let cwd;
200560
+ const removed = [];
200561
+ let projectRoot;
200496
200562
  try {
200497
- cwd = (0, project_config_js_1.requireProjectRoot)();
200563
+ projectRoot = (0, project_config_js_1.requireProjectRoot)();
200498
200564
  }
200499
200565
  catch {
200500
- cwd = undefined;
200566
+ // 宿主配置目录等非法项目根:没有项目级凭据
200567
+ projectRoot = undefined;
200568
+ }
200569
+ const envApiKey = getCloudBaseApiKeyFromEnv();
200570
+ const hasProjectCredential = !!(projectRoot && readProjectCredentialFile(projectRoot));
200571
+ // env 里那份是用户自己设的(进程删不掉)时,既不算项目级生效,也不该顺手注销账号登录
200572
+ const projectInEffect = hasProjectCredential && (!envApiKey || isEnvCredentialRemovableByLogout());
200573
+ if (projectInEffect && projectRoot) {
200574
+ deleteProjectCredentialFile(projectRoot);
200575
+ clearAdoptedProjectEnv();
200576
+ removed.push("project");
200577
+ }
200578
+ else if (!envApiKey) {
200579
+ await auth.logout();
200580
+ removed.push("global");
200501
200581
  }
200502
- await auth.logout(cwd ? { cwd } : undefined);
200503
- clearAdoptedProjectEnv();
200504
200582
  credentialSource = null;
200505
200583
  resetAuthProgressState();
200584
+ return { removed };
200506
200585
  }
200507
200586
 
200508
200587
 
@@ -200517,7 +200596,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
200517
200596
  return (mod && mod.__esModule) ? mod : { "default": mod };
200518
200597
  };
200519
200598
  Object.defineProperty(exports, "__esModule", ({ value: true }));
200520
- exports.envManager = void 0;
200599
+ exports.envManager = exports.runInEnvSlot = void 0;
200521
200600
  exports.probeApiKeyCamCapability = probeApiKeyCamCapability;
200522
200601
  exports.fetchEnvOwnerUin = fetchEnvOwnerUin;
200523
200602
  exports.probeCamCapabilityForLogin = probeCamCapabilityForLogin;
@@ -200539,7 +200618,10 @@ const logger_js_1 = __webpack_require__(40781);
200539
200618
  const tool_result_js_1 = __webpack_require__(69645);
200540
200619
  const site_map_js_1 = __webpack_require__(34533);
200541
200620
  const project_config_js_1 = __webpack_require__(39175);
200621
+ const cloud_mode_js_1 = __webpack_require__(88694);
200622
+ const env_binding_js_1 = __webpack_require__(61498);
200542
200623
  const local_endpoint_js_1 = __webpack_require__(16156);
200624
+ const describe_envs_query_js_1 = __webpack_require__(31284);
200543
200625
  function toEnvCandidates(envList) {
200544
200626
  if (!Array.isArray(envList)) {
200545
200627
  return [];
@@ -200725,11 +200807,7 @@ async function listAvailableEnvCandidates(options) {
200725
200807
  try {
200726
200808
  const result = await cloudbase.commonService("tcb", "2018-06-08").call({
200727
200809
  Action: "DescribeEnvs",
200728
- Param: {
200729
- EnvTypes: ["weda", "baas"],
200730
- IsVisible: false,
200731
- Channels: ["dcloud", "iotenable", "tem", "scene_module"],
200732
- },
200810
+ Param: (0, describe_envs_query_js_1.buildDescribeEnvsFilter)(),
200733
200811
  });
200734
200812
  const envList = result?.EnvList || result?.Data?.EnvList || [];
200735
200813
  return toEnvCandidates(envList);
@@ -200947,6 +201025,7 @@ class EnvironmentManager {
200947
201025
  // 手动设置环境ID(用于外部调用)
200948
201026
  async setEnvId(envId) {
200949
201027
  this._setCachedEnvId(envId);
201028
+ (0, env_binding_js_1.noteEnvBinding)({ envId, source: "envManager.cachedEnvId" });
200950
201029
  (0, logger_js_1.debug)('手动设置环境ID并更新缓存:', { envId });
200951
201030
  }
200952
201031
  // Get cached envId without triggering fetch (for optimization)
@@ -200957,44 +201036,136 @@ class EnvironmentManager {
200957
201036
  // 全局实例
200958
201037
  const envManager = new EnvironmentManager();
200959
201038
  exports.envManager = envManager;
200960
- // 导出环境ID获取函数
201039
+ function requestHasCloudCredentials(cloudBaseOptions) {
201040
+ return Boolean((cloudBaseOptions?.secretId && cloudBaseOptions?.secretKey) ||
201041
+ cloudBaseOptions?.requestFn);
201042
+ }
201043
+ function throwCloudModeEnvRequired(envCandidates) {
201044
+ const singleEnvId = envCandidates.length === 1 ? envCandidates[0].envId : undefined;
201045
+ (0, tool_result_js_1.throwToolPayloadError)({
201046
+ ok: false,
201047
+ code: "ENV_REQUIRED",
201048
+ message: "当前请求没有带 envId。Cloud mode 不读取本机登录态、项目配置或进程环境变量。",
201049
+ env_candidates: envCandidates,
201050
+ next_step: (0, tool_result_js_1.buildAuthNextStep)("set_env", {
201051
+ requiredParams: singleEnvId ? undefined : ["envId"],
201052
+ suggestedArgs: singleEnvId
201053
+ ? { action: "set_env", envId: singleEnvId }
201054
+ : { action: "set_env" },
201055
+ }),
201056
+ });
201057
+ }
201058
+ /**
201059
+ * Cloud mode only sees this request. No envManager, no CLOUDBASE_ENV_ID, no project file, no auth.json.
201060
+ * A single environment is written back onto this request's options and is not cached on the process.
201061
+ */
201062
+ async function resolveCloudModeEnvId(cloudBaseOptions, requireEnvId) {
201063
+ if (cloudBaseOptions?.envId) {
201064
+ (0, env_binding_js_1.noteEnvBinding)({ envId: cloudBaseOptions.envId, source: "cloudBaseOptions" });
201065
+ return cloudBaseOptions.envId;
201066
+ }
201067
+ if (!requireEnvId) {
201068
+ return undefined;
201069
+ }
201070
+ if (!requestHasCloudCredentials(cloudBaseOptions)) {
201071
+ throwCloudModeEnvRequired([]);
201072
+ }
201073
+ const envCandidates = await listAvailableEnvCandidates({ cloudBaseOptions });
201074
+ if (envCandidates.length === 1) {
201075
+ const singleEnvId = envCandidates[0].envId;
201076
+ if (cloudBaseOptions) {
201077
+ cloudBaseOptions.envId = singleEnvId;
201078
+ }
201079
+ (0, env_binding_js_1.noteEnvBinding)({ envId: singleEnvId, source: "auto_bound" });
201080
+ return singleEnvId;
201081
+ }
201082
+ throwCloudModeEnvRequired(envCandidates);
201083
+ }
201084
+ async function getCloudBaseManagerCloudMode(options) {
201085
+ const requireEnvId = options.requireEnvId !== false;
201086
+ const cloudBaseOptions = options.cloudBaseOptions;
201087
+ if (!requestHasCloudCredentials(cloudBaseOptions)) {
201088
+ throwAuthRequiredError();
201089
+ }
201090
+ const resolvedEnvId = await resolveCloudModeEnvId(cloudBaseOptions, requireEnvId);
201091
+ return createCloudBaseManagerWithOptions({
201092
+ ...(cloudBaseOptions ?? {}),
201093
+ envId: resolvedEnvId,
201094
+ });
201095
+ }
201096
+ /**
201097
+ * Fresh slot for tests. Production stdio/hosted pass their own slot into `runWithEnvSlot`.
201098
+ * Re-exported from `env-binding.ts` so there is only one definition.
201099
+ */
201100
+ var env_binding_js_2 = __webpack_require__(61498);
201101
+ Object.defineProperty(exports, "runInEnvSlot", ({ enumerable: true, get: function () { return env_binding_js_2.runInEnvSlot; } }));
200961
201102
  async function getEnvId(cloudBaseOptions) {
201103
+ if ((0, cloud_mode_js_1.isCloudMode)()) {
201104
+ const envId = await resolveCloudModeEnvId(cloudBaseOptions, true);
201105
+ if (!envId) {
201106
+ throwCloudModeEnvRequired([]);
201107
+ }
201108
+ return envId;
201109
+ }
201110
+ return resolveEnvIdForCall(cloudBaseOptions);
201111
+ }
201112
+ async function resolveEnvIdForCall(cloudBaseOptions) {
200962
201113
  if (cloudBaseOptions?.envId) {
200963
201114
  (0, logger_js_1.debug)('使用传入的 envId:', { envId: cloudBaseOptions.envId });
201115
+ (0, env_binding_js_1.noteEnvBinding)({ envId: cloudBaseOptions.envId, source: "cloudBaseOptions" });
200964
201116
  return cloudBaseOptions.envId;
200965
201117
  }
200966
201118
  if ((0, local_endpoint_js_1.resolveLocalEndpoint)()) {
200967
201119
  return 'local';
200968
201120
  }
200969
- const cachedEnvId = envManager.getCachedEnvId() || process.env.CLOUDBASE_ENV_ID;
201121
+ const cachedEnvId = envManager.getCachedEnvId();
200970
201122
  if (cachedEnvId) {
200971
201123
  (0, logger_js_1.debug)('使用缓存中的 envId:', { envId: cachedEnvId });
201124
+ (0, env_binding_js_1.noteEnvBinding)({ envId: cachedEnvId, source: "envManager.cachedEnvId" });
200972
201125
  return cachedEnvId;
200973
201126
  }
200974
- // 项目级配置固定的环境优先于账号级登录态,保证「打开仓库即连对环境」,
200975
- // 且同一仓库的每个 Git worktree / 每个新进程都不需要重复 set_env。
201127
+ if (process.env.CLOUDBASE_ENV_ID) {
201128
+ (0, logger_js_1.debug)('使用进程环境变量的 envId:', { envId: process.env.CLOUDBASE_ENV_ID });
201129
+ (0, env_binding_js_1.noteEnvBinding)({
201130
+ envId: process.env.CLOUDBASE_ENV_ID,
201131
+ source: "process.env.CLOUDBASE_ENV_ID",
201132
+ });
201133
+ return process.env.CLOUDBASE_ENV_ID;
201134
+ }
201135
+ // Project config stays ahead of the account login env, and still writes the cache.
200976
201136
  const projectEnvId = (0, project_config_js_1.readProjectEnvId)();
200977
201137
  if (projectEnvId) {
200978
201138
  (0, logger_js_1.debug)('使用项目配置(.cloudbase/project.json)的 envId:', { envId: projectEnvId });
200979
201139
  await envManager.setEnvId(projectEnvId);
201140
+ (0, env_binding_js_1.noteEnvBinding)({ envId: projectEnvId, source: "project" });
200980
201141
  return projectEnvId;
200981
201142
  }
200982
201143
  const loginState = await (0, auth_js_1.peekLoginState)();
200983
201144
  if (typeof loginState?.envId === 'string' && loginState.envId.length > 0) {
200984
201145
  (0, logger_js_1.debug)('使用登录态中的 envId:', { envId: loginState.envId });
201146
+ // Do not cache. A project file that appears later in this process must still win.
201147
+ (0, env_binding_js_1.noteEnvBinding)({ envId: loginState.envId, source: "login_state" });
200985
201148
  return loginState.envId;
200986
201149
  }
200987
- // 否则使用默认逻辑
200988
- return envManager.getEnvId();
201150
+ const envId = await envManager.getEnvId();
201151
+ (0, env_binding_js_1.noteEnvBinding)({ envId, source: "auto_bound" });
201152
+ return envId;
200989
201153
  }
200990
- // 导出函数保持兼容性
200991
201154
  function resetCloudBaseManagerCache() {
200992
201155
  envManager.reset();
201156
+ (0, env_binding_js_1.clearEnvBinding)();
200993
201157
  }
200994
201158
  // 导出获取缓存环境ID的函数,供遥测模块使用
200995
201159
  function getCachedEnvId() {
200996
201160
  return envManager.getCachedEnvId();
200997
201161
  }
201162
+ /**
201163
+ * 实例 ID 是环境关联的 PaaS 资源标识,正常情况下与 envId 同生命周期。
201164
+ * 但失效通知跨系统没有出口(invalidate 在生产路径上没有任何调用点),
201165
+ * 所以用一个长 TTL 兜底:宁可每半小时多查一次环境信息,
201166
+ * 也不要拿着一份没人能证明还正确的实例 ID 一直用下去。
201167
+ */
201168
+ const DATABASE_INSTANCE_ID_CACHE_TTL_MS = 30 * 60 * 1000;
200998
201169
  const databaseInstanceIdCache = new Map();
200999
201170
  function buildDatabaseInstanceIdCacheKey(options) {
201000
201171
  const envId = options?.envId ?? options?.cloudBaseOptions?.envId ?? process.env.CLOUDBASE_ENV_ID ?? 'unknown';
@@ -201025,11 +201196,16 @@ async function getDatabaseInstanceId(options) {
201025
201196
  });
201026
201197
  const cachedEntry = databaseInstanceIdCache.get(cacheKey);
201027
201198
  if (cachedEntry?.instanceId) {
201028
- return {
201029
- instanceId: cachedEntry.instanceId,
201030
- source: 'cache',
201031
- cacheKey,
201032
- };
201199
+ if (cachedEntry.expiresAt && cachedEntry.expiresAt <= Date.now()) {
201200
+ databaseInstanceIdCache.delete(cacheKey);
201201
+ }
201202
+ else {
201203
+ return {
201204
+ instanceId: cachedEntry.instanceId,
201205
+ source: 'cache',
201206
+ cacheKey,
201207
+ };
201208
+ }
201033
201209
  }
201034
201210
  if (cachedEntry?.inflightPromise) {
201035
201211
  const instanceId = await cachedEntry.inflightPromise;
@@ -201054,6 +201230,7 @@ async function getDatabaseInstanceId(options) {
201054
201230
  const instanceId = await inflightPromise;
201055
201231
  databaseInstanceIdCache.set(cacheKey, {
201056
201232
  instanceId,
201233
+ expiresAt: Date.now() + DATABASE_INSTANCE_ID_CACHE_TTL_MS,
201057
201234
  });
201058
201235
  return {
201059
201236
  instanceId,
@@ -201070,6 +201247,9 @@ async function getDatabaseInstanceId(options) {
201070
201247
  * 每次都实时获取最新的 token/secretId/secretKey
201071
201248
  */
201072
201249
  async function getCloudBaseManager(options = {}) {
201250
+ if ((0, cloud_mode_js_1.isCloudMode)()) {
201251
+ return getCloudBaseManagerCloudMode(options);
201252
+ }
201073
201253
  const { requireEnvId = true, authStrategy = 'fail_fast', } = options;
201074
201254
  let cloudBaseOptions = options.cloudBaseOptions;
201075
201255
  const localEndpoint = (0, local_endpoint_js_1.resolveLocalEndpoint)();
@@ -201085,12 +201265,14 @@ async function getCloudBaseManager(options = {}) {
201085
201265
  // 如果传入了完整凭据或 requestFn,优先使用显式 CloudBase 配置
201086
201266
  if (cloudBaseOptions && (hasDirectCredentials || hasRequestFn)) {
201087
201267
  let resolvedEnvId = cloudBaseOptions.envId;
201268
+ let explicitSource = cloudBaseOptions.envId ? "cloudBaseOptions" : "default";
201088
201269
  if (requireEnvId && !resolvedEnvId) {
201089
201270
  const envCandidates = await listAvailableEnvCandidates({ cloudBaseOptions });
201090
201271
  if (envCandidates.length === 1) {
201091
201272
  const singleEnvId = envCandidates[0].envId;
201092
201273
  cloudBaseOptions.envId = singleEnvId;
201093
201274
  resolvedEnvId = singleEnvId;
201275
+ explicitSource = "auto_bound";
201094
201276
  (0, logger_js_1.debug)('自动绑定唯一环境(显式配置):', { envId: singleEnvId });
201095
201277
  }
201096
201278
  else if (authStrategy === 'fail_fast') {
@@ -201110,6 +201292,9 @@ async function getCloudBaseManager(options = {}) {
201110
201292
  }
201111
201293
  }
201112
201294
  (0, logger_js_1.debug)('使用传入的 CloudBase 配置');
201295
+ if (resolvedEnvId && explicitSource !== "default") {
201296
+ (0, env_binding_js_1.noteEnvBinding)({ envId: resolvedEnvId, source: explicitSource });
201297
+ }
201113
201298
  return createCloudBaseManagerWithOptions({
201114
201299
  ...cloudBaseOptions,
201115
201300
  envId: resolvedEnvId,
@@ -201131,27 +201316,33 @@ async function getCloudBaseManager(options = {}) {
201131
201316
  }
201132
201317
  const { envId: loginEnvId, secretId, secretKey, token } = loginState;
201133
201318
  let finalEnvId = cloudBaseOptions?.envId;
201319
+ let envSource = finalEnvId ? "cloudBaseOptions" : undefined;
201134
201320
  if (requireEnvId) {
201135
201321
  if (!finalEnvId) {
201136
- // Optimize: Check if envManager has cached envId first (fast path)
201137
- // If cached, use it directly; otherwise check loginEnvId before calling getEnvId()
201138
- // This avoids unnecessary async calls when we have a valid envId available
201139
- const cachedEnvId = envManager.getCachedEnvId() || process.env.CLOUDBASE_ENV_ID;
201140
- const projectEnvId = cachedEnvId ? undefined : (0, project_config_js_1.readProjectEnvId)();
201322
+ const cachedEnvId = envManager.getCachedEnvId();
201323
+ const envFromProcess = process.env.CLOUDBASE_ENV_ID;
201324
+ const projectEnvId = (cachedEnvId || envFromProcess) ? undefined : (0, project_config_js_1.readProjectEnvId)();
201141
201325
  if (cachedEnvId) {
201142
201326
  (0, logger_js_1.debug)('使用 envManager 缓存的环境ID:', { cachedEnvId });
201143
201327
  finalEnvId = cachedEnvId;
201328
+ envSource = "envManager.cachedEnvId";
201329
+ }
201330
+ else if (envFromProcess) {
201331
+ (0, logger_js_1.debug)('使用进程环境变量的环境ID:', { envId: envFromProcess });
201332
+ finalEnvId = envFromProcess;
201333
+ envSource = "process.env.CLOUDBASE_ENV_ID";
201144
201334
  }
201145
201335
  else if (projectEnvId) {
201146
- // 项目级绑定优先于全局登录态 envId:后者可能来自另一个仓库
201336
+ // Project binding stays ahead of the account login env.
201147
201337
  (0, logger_js_1.debug)('使用项目配置(project.json/cloudbaserc.json)的环境ID:', { projectEnvId });
201148
201338
  await envManager.setEnvId(projectEnvId);
201149
201339
  finalEnvId = projectEnvId;
201340
+ envSource = "project";
201150
201341
  }
201151
201342
  else if (loginEnvId) {
201152
- // If no cache but loginState has envId, use it directly
201153
201343
  (0, logger_js_1.debug)('使用 loginState 中的环境ID:', { loginEnvId });
201154
201344
  finalEnvId = loginEnvId;
201345
+ envSource = "login_state";
201155
201346
  }
201156
201347
  else {
201157
201348
  if (authStrategy === 'fail_fast') {
@@ -201160,6 +201351,7 @@ async function getCloudBaseManager(options = {}) {
201160
201351
  const singleEnvId = envCandidates[0].envId;
201161
201352
  await envManager.setEnvId(singleEnvId);
201162
201353
  finalEnvId = singleEnvId;
201354
+ envSource = "auto_bound";
201163
201355
  (0, logger_js_1.debug)('自动绑定唯一环境:', { envId: singleEnvId });
201164
201356
  }
201165
201357
  else {
@@ -201167,14 +201359,19 @@ async function getCloudBaseManager(options = {}) {
201167
201359
  }
201168
201360
  }
201169
201361
  else {
201170
- // ensure 模式下也保持非交互:单环境自动绑定,多环境返回 ENV_REQUIRED
201171
201362
  finalEnvId = await envManager.getEnvId();
201363
+ envSource = "auto_bound";
201172
201364
  }
201173
201365
  }
201174
201366
  }
201175
201367
  }
201176
- // envId priority: explicit option > envManager cache > loginState.envId
201177
201368
  const resolvedEnvId = finalEnvId || loginEnvId;
201369
+ if (!envSource && resolvedEnvId && resolvedEnvId === loginEnvId) {
201370
+ envSource = "login_state";
201371
+ }
201372
+ if (resolvedEnvId && envSource) {
201373
+ (0, env_binding_js_1.noteEnvBinding)({ envId: resolvedEnvId, source: envSource });
201374
+ }
201178
201375
  // region 直接使用 fallbackRegion(resolveSiteAndRegion 统一解析)
201179
201376
  // 不再通过 DescribeEnvs 查询 region,以兼容 STS 临时密钥场景
201180
201377
  const region = fallbackRegion;
@@ -201905,6 +202102,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
201905
202102
  exports.handleHostedMcpRequest = handleHostedMcpRequest;
201906
202103
  const streamableHttp_js_1 = __webpack_require__(52827);
201907
202104
  const server_js_1 = __webpack_require__(46324);
202105
+ const env_binding_js_1 = __webpack_require__(61498);
201908
202106
  const feedback_session_js_1 = __webpack_require__(23379);
201909
202107
  const client_info_cache_js_1 = __webpack_require__(65454);
201910
202108
  function readInitializeClientInfo(body) {
@@ -201982,36 +202180,42 @@ async function handleHostedMcpRequest(input) {
201982
202180
  const closeBoth = () => {
201983
202181
  void Promise.allSettled([transport?.close(), server?.close()]);
201984
202182
  };
202183
+ const injectedEnvId = input.serverOptions.cloudBaseOptions?.envId;
202184
+ const requestSlot = {
202185
+ binding: injectedEnvId
202186
+ ? { envId: injectedEnvId, source: "cloudBaseOptions" }
202187
+ : null,
202188
+ };
201985
202189
  try {
201986
- server = await (0, server_js_1.createCloudBaseMcpServer)({
201987
- name: input.serverOptions.name,
201988
- version: input.serverOptions.version,
201989
- enableTelemetry: input.serverOptions.enableTelemetry,
201990
- cloudBaseOptions: input.serverOptions.cloudBaseOptions,
201991
- cloudMode: input.serverOptions.cloudMode ?? true,
201992
- ide: input.serverOptions.ide,
201993
- client: input.serverOptions.client,
201994
- clientInfo,
201995
- // 会话级遥测的身份来源:把**当前** transport 的 sessionId 交给 server(公开 getter)。
201996
- // 这里是无状态传输(本文件 `:165` `sessionIdGenerator: undefined`)⇒ 今天它恒为 undefined、
201997
- // 上报按"一个实例一个会话"计;将来若有状态化,sessionId 会自动出现在这里。
201998
- getSessionId: () => transport?.sessionId,
201999
- lang: input.serverOptions.lang,
202000
- pluginsEnabled: input.serverOptions.pluginsEnabled,
202001
- pluginsDisabled: input.serverOptions.pluginsDisabled,
202002
- });
202003
- input.logger?.({
202004
- type: "cloudbase-mcp-server-create",
202005
- timeCost: Date.now() - startedAt,
202006
- });
202007
- transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
202008
- sessionIdGenerator: undefined,
202009
- enableJsonResponse: true,
202190
+ await (0, env_binding_js_1.runWithEnvSlot)(requestSlot, async () => {
202191
+ server = await (0, server_js_1.createCloudBaseMcpServer)({
202192
+ name: input.serverOptions.name,
202193
+ version: input.serverOptions.version,
202194
+ enableTelemetry: input.serverOptions.enableTelemetry,
202195
+ cloudBaseOptions: input.serverOptions.cloudBaseOptions,
202196
+ cloudMode: input.serverOptions.cloudMode ?? true,
202197
+ ide: input.serverOptions.ide,
202198
+ client: input.serverOptions.client,
202199
+ clientInfo,
202200
+ // Session id comes from this transport. Stateless mode leaves it undefined.
202201
+ getSessionId: () => transport?.sessionId,
202202
+ lang: input.serverOptions.lang,
202203
+ pluginsEnabled: input.serverOptions.pluginsEnabled,
202204
+ pluginsDisabled: input.serverOptions.pluginsDisabled,
202205
+ });
202206
+ input.logger?.({
202207
+ type: "cloudbase-mcp-server-create",
202208
+ timeCost: Date.now() - startedAt,
202209
+ });
202210
+ transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
202211
+ sessionIdGenerator: undefined,
202212
+ enableJsonResponse: true,
202213
+ });
202214
+ input.res.on("close", closeBoth);
202215
+ input.req.on("aborted", closeBoth);
202216
+ await server.connect(transport);
202217
+ await transport.handleRequest(input.req, input.res, input.body);
202010
202218
  });
202011
- input.res.on("close", closeBoth);
202012
- input.req.on("aborted", closeBoth);
202013
- await server.connect(transport);
202014
- await transport.handleRequest(input.req, input.res, input.body);
202015
202219
  }
202016
202220
  catch (error) {
202017
202221
  await Promise.allSettled([transport?.close(), server?.close()]);
@@ -202665,7 +202869,7 @@ exports.cloudrun = (0, types_js_1.defineModule)({
202665
202869
  "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\")。",
202666
202870
  "error.actionFailed": "[{context}] {baseMessage}",
202667
202871
  "error.buildManage": "[manageCloudRun/{action}] {baseMessage}\n建议:{suggestions}",
202668
- "error.deployTaskRunning": "服务 `{serverName}` 当前已有部署任务在执行,请等待现有任务完成后再重试。",
202872
+ "error.deployTaskRunning": "服务 `{serverName}` 当前已有部署任务在执行。**不要再发起 deploy**:先用 queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\") 读取该任务的真实状态——已结束就重新部署,长时间停在非终态则带上该输出联系 CloudBase 平台排查。",
202669
202873
  "error.deployTaskRunningForce": "注意:`force=true` 仅跳过本工具的确认提示,不会取消或覆盖平台上的发布任务。请先用 queryCloudRun(action=\"getManageTask\", serverName=\"{serverName}\") 确认任务真实状态:若任务已结束却仍报此错,或任务长时间停在非终态,请携带该输出联系 CloudBase 平台排查该环境的发布任务。",
202670
202874
  "error.vpcRequired": "CreateCloudRunServer 需要有效 VPC:请传 serverConfig.VpcConf(VpcId+SubnetId),或在 initEnv 时传入 vpcId/subnetIds(环境开通后 deploy 会自动从 EnvBaseInfo 回填)。若平台拒绝系统创建网络,必须指定上海地域 VPC。",
202671
202875
  "error.genericRetry": "请检查服务状态、部署参数和目标目录后重试。",
@@ -202855,7 +203059,7 @@ exports.cloudrun = (0, types_js_1.defineModule)({
202855
203059
  "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.",
202856
203060
  "error.actionFailed": "[{context}] {baseMessage}",
202857
203061
  "error.buildManage": "[manageCloudRun/{action}] {baseMessage}\nSuggestions: {suggestions}",
202858
- "error.deployTaskRunning": "Service `{serverName}` already has a deploy task running. Wait for the current task to finish before retrying.",
203062
+ "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.",
202859
203063
  "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.",
202860
203064
  "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.",
202861
203065
  "error.genericRetry": "Check the service status, deploy parameters, and target directory, then retry.",
@@ -204459,9 +204663,12 @@ exports.env = (0, types_js_1.defineModule)({
204459
204663
  "auth.envReadyWithHint": "环境设置成功,当前环境: {envId}{regionHint}",
204460
204664
  "auth.regionHint": ",地域: {region}",
204461
204665
  "auth.regionHintUnverified": "。未在当前探测地域中确认该 envId,已按唯一 ID 直绑;若后续接口仍指向错误地域,请设置 TCB_REGION 或 queryEnv(action=\"list\", region=...)",
204462
- "auth.logoutNotAllowedApiKey": "当前使用 API Key 认证模式,不支持退出登录。如需切换认证方式,请移除 CLOUDBASE_API_KEY(或兼容的 CLOUDBASE_APIKEY)环境变量后重启。",
204666
+ "auth.logoutNotAllowedApiKey": "当前是 API Key 认证模式,凭据来自环境变量 {varName}。这个变量不是本进程设的、也不由本进程清除,所以没有执行登出。要切换认证方式:在 shell 里 unset {varName},或从启动本 MCP 的客户端配置里移除它,然后重启。",
204463
204667
  "auth.logoutConfirmRequired": "action=logout 时必须传 confirm=\"yes\"",
204464
204668
  "auth.loggedOut": "✅ 已退出登录",
204669
+ "auth.loggedOutProject": "✅ 已移除本项目的 API Key 登录。这台机器上若还有账号登录,它仍然有效;要一起退出请再执行一次 logout。",
204670
+ "auth.apiKeySwitchHintRemovable": "如需改用账号登录,先执行 logout 退出本项目 API Key,再 start_auth 授权。",
204671
+ "auth.apiKeySwitchHintExternal": "这个 key 来自外部环境变量,MCP 无法退出它;如需改用账号登录,先在 shell 或客户端配置里移除 {varName} 并重启。",
204465
204672
  "auth.tempCredNotLoggedIn": "当前未登录,请先完成管理端认证后再获取临时密钥。",
204466
204673
  "auth.tempCredConfirmRequired": "action=get_temp_credentials 时必须显式传 confirm=\"yes\",以确认你要导出当前管理端临时密钥。",
204467
204674
  "auth.tempCredUnsupportedType": "当前登录态不是可导出的临时密钥。仅支持通过 Web / device 登录得到的临时密钥,永久密钥登录不允许导出。",
@@ -204722,9 +204929,12 @@ exports.env = (0, types_js_1.defineModule)({
204722
204929
  "auth.envReadyWithHint": "Environment bound successfully. Current env: {envId}{regionHint}",
204723
204930
  "auth.regionHint": ", region: {region}",
204724
204931
  "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=...)",
204725
- "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.",
204932
+ "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.",
204726
204933
  "auth.logoutConfirmRequired": "confirm=\"yes\" must be passed when action=logout",
204727
204934
  "auth.loggedOut": "✅ Logged out",
204935
+ "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.",
204936
+ "auth.apiKeySwitchHintRemovable": "To switch to account login, run logout to remove this project's API Key, then start_auth.",
204937
+ "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.",
204728
204938
  "auth.tempCredNotLoggedIn": "Not logged in. Complete management-plane authentication before fetching temporary credentials.",
204729
204939
  "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.",
204730
204940
  "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.",
@@ -205103,9 +205313,12 @@ exports.functions = (0, types_js_1.defineModule)({
205103
205313
  "\n- `limit`: 分页数量,默认值由后端决定" +
205104
205314
  "\n- `offset`: 分页偏移,从 0 开始" +
205105
205315
  "\n- 示例:`queryFunctions(action=\"listFunctions\", offset=10, limit=10)`" +
205106
- "\n\n**查询 CloudBase 云函数日志**:使用 `action=\"listFunctionLogs\"`,需要提供 `functionName` 参数。" +
205316
+ "\n\n**查询 CloudBase 云函数日志**:`action=\"listFunctionLogs\"`,需要提供 `functionName`。日志从 CLS 检索(底层 GetFunctionLogs 接口已下线)。" +
205107
205317
  "\n- 示例:`queryFunctions(action=\"listFunctionLogs\", functionName=\"my-function\")`" +
205108
- "\n- 如需查看日志详情:`queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
205318
+ "\n- 不传 startTime/endTime 时默认查最近 1 天;跨度不能超过 1 天,单次最多返回 100 条" +
205319
+ "\n- 返回 `hasMore=true` 表示还有更早的日志:把返回的 `context` 原样传回取下一页(`offset` 只在前 100 条内生效)" +
205320
+ "\n- `logs[]` 已解析成 requestId / log / statusCode / retCode / durationMs / memoryMb / timestamp" +
205321
+ "\n- 想看某一次调用的完整过程(START、业务日志、REPORT):`queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
205109
205322
  "\n\n**定时任务 / cron / 定时跑**:使用 `listFunctionTriggers` 查询函数的 timer 触发器配置。" +
205110
205323
  "\n\n**版本与流量路由**:`listVersionByFunction` 列出已发布版本(对齐 tcb fn list-function-versions);`getFunctionAlias` 查看别名/灰度配置(对齐 tcb fn get-route,aliasName 默认 `$DEFAULT`)。" +
205111
205324
  "\n\n**层(Layer)说明**:" +
@@ -205175,8 +205388,8 @@ exports.functions = (0, types_js_1.defineModule)({
205175
205388
  endTimeInvalid: "endTime 格式错误: \"{value}\"。必须使用 YYYY-MM-DD HH:mm:ss 格式(如 2024-01-01 23:59:59)",
205176
205389
  logRangeInvalidDatetime: "startTime 和 endTime 必须是有效的日期时间字符串",
205177
205390
  logRangeTooLong: "startTime 和 endTime 间隔不能超过一天",
205178
- 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 时默认查询最近一天的日志。",
205179
- logDetailInvalidParamTips: "常见原因:\n1. startTime/endTime 格式错误,必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00),不支持 ISO 8601 或时间戳\n2. startTime 和 endTime 间隔超过一天\n建议:不传 startTime/endTime 时默认查询最近一天的日志。",
205391
+ 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 时默认查询最近一天的日志。",
205392
+ 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 再查详情。",
205180
205393
  paramRequired: "{action} 操作时,{param} 参数是必需的",
205181
205394
  "deployStatus.cloudMode": "{action} 在 cloud mode 下不可用:异步部署任务只由 buildStrategy=cloud/local 的真实部署创建,而这两种策略的真实执行在 cloud mode 下都不支持(需要读取本地构建上下文或本地 Docker),因此不会存在任何 taskId。buildStrategy=image 走同步部署,直接在 manageFunctions 的返回里拿结果,也不需要查询部署状态。如需异步镜像构建部署,请改用本地 MCP 模式。",
205182
205395
  deployTaskNotFound: "未找到部署任务 {taskId};部署任务不存在,可能已过期、不属于当前环境 {envId},或 MCP Server 已重启(任务仅保存在 MCP 进程内存中)。",
@@ -205191,7 +205404,11 @@ exports.functions = (0, types_js_1.defineModule)({
205191
205404
  "reason.viewLogs": "查看该函数的执行日志",
205192
205405
  "reason.updateConfig": "更新该函数配置",
205193
205406
  "reason.checkGateway": "查看该函数是否已暴露网关访问入口",
205194
- "reason.logDetail": "按 requestId 查看单条日志详情",
205407
+ "reason.logDetail": "按 requestId 查看该次调用的完整日志",
205408
+ "reason.nextLogPage": "带上 context 取下一页(更早的)日志",
205409
+ "reason.widenLogRange": "放宽时间范围后重新查询该函数的日志",
205410
+ "reason.createLogService": "开通环境日志服务后再查询函数日志",
205411
+ "reason.searchClsFallback": "改用 queryLogs 直接按同一条 CLS 语句检索",
205195
205412
  "reason.attachLayer": "为该函数追加绑定层",
205196
205413
  "reason.reorderLayers": "整体调整层顺序或绑定列表",
205197
205414
  "reason.layerVersions": "查看某个层的版本列表",
@@ -205236,6 +205453,10 @@ exports.functions = (0, types_js_1.defineModule)({
205236
205453
  gotFunctionDetail: "已获取函数 {fnName} 的详情",
205237
205454
  gotFunctionLogs: "已获取函数 {fnName} 的日志列表",
205238
205455
  gotLogDetail: "已获取 requestId={requestId} 的日志详情",
205456
+ "logs.emptyByFunction": "在 {start} ~ {end} 之间没有查到函数 {fnName} 的日志。常见原因:该时间段内函数没有被调用、日志已过保留期、或 functionName / qualifier 不匹配。可以放宽时间范围后重试。",
205457
+ "logs.emptyByRequest": "在 {start} ~ {end} 之间没有查到 requestId={requestId} 的日志。请确认时间范围覆盖了这次调用(日志按请求开始时间归档),或先用 listFunctionLogs 定位到的 requestId。",
205458
+ "logs.serviceNotEnabled": "当前环境还没有开通日志服务,云函数日志查不到。请先调用 manageLogs(action=\"createLogService\", confirm=true) 开通,再重新查询。",
205459
+ "logs.queryFailed": "查询云函数日志失败:{message}",
205239
205460
  gotFunctionLayers: "已获取函数 {fnName} 当前绑定的层",
205240
205461
  gotLayers: "已获取 {count} 条层记录",
205241
205462
  gotLayerVersions: "已获取层 {layerName} 的版本列表",
@@ -205316,16 +205537,17 @@ exports.functions = (0, types_js_1.defineModule)({
205316
205537
  "schema.create.layers": "Layer 配置",
205317
205538
  "schema.manageLayer.name": "层名称",
205318
205539
  "schema.manageLayer.version": "层版本号",
205319
- "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 秒内有效",
205540
+ "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 秒内有效",
205320
205541
  "schema.query.functionName": "CloudBase 云函数名称。`getFunctionDetail`、`listFunctionLogs`、`listFunctionLayers`、`listFunctionTriggers`、`getFunctionDownloadUrl`、`listVersionByFunction`、`getFunctionAlias` 时必填;`getFunctionUploadUrl` 可选(仅用于生成上传对象 key)",
205321
- "schema.query.limit": "分页数量(limit)。列表类 action 可选,默认值由后端决定",
205322
- "schema.query.offset": "分页偏移(offset)。列表类 action 可选,默认 0",
205542
+ "schema.query.limit": "分页数量(limit)。列表类 action 可选,默认值由后端决定;日志类 action 单次最多 100 条,默认 20",
205543
+ "schema.query.offset": "分页偏移(offset)。列表类 action 可选,默认 0。日志类 action 只在前 100 条内生效,更早的日志请用返回的 context 翻页",
205323
205544
  "schema.query.codeSecret": "代码保护密钥,用于解密函数代码",
205324
205545
  "schema.query.revealEnvValues": "getFunctionDetail / listFunctionTriggers / listFunctionLayers 时是否返回环境变量明文值。默认 false:Value 脱敏为 ***,仅保留 Key 与 ValueLength,足以确认配置了哪些变量及变更是否生效;true 时返回明文,敏感变量会进入模型上下文,谨慎使用。如需查看明文,建议优先使用控制台或 CLI",
205325
- "schema.query.startTime": "日志查询开始时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00)。与 endTime 间隔不能超过一天。不传时默认查询最近一天",
205326
- "schema.query.endTime": "日志查询结束时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 23:59:59)。与 startTime 间隔不能超过一天。不传时默认为当前时间",
205327
- "schema.query.requestId": "日志请求 ID。`getFunctionLogDetail` 操作必填,可从 `listFunctionLogs` 结果中获取",
205328
- "schema.query.qualifier": "函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选",
205546
+ "schema.query.startTime": "日志查询开始时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 00:00:00,按北京时间理解)。与 endTime 间隔不能超过一天。不传时默认查询最近一天",
205547
+ "schema.query.endTime": "日志查询结束时间,格式必须为 YYYY-MM-DD HH:mm:ss(如 2024-01-01 23:59:59,按北京时间理解)。与 startTime 间隔不能超过一天。不传时默认为当前时间",
205548
+ "schema.query.requestId": "日志请求 ID。`getFunctionLogDetail` 操作必填;取自 `listFunctionLogs` 返回的 logs[].requestId,也可传具体业务日志里出现的 RequestId",
205549
+ "schema.query.qualifier": "函数版本别名,如 $LATEST、$DEFAULT。日志查询时可选,作为 CLS 检索条件",
205550
+ "schema.query.context": "日志翻页游标。把上一次 `listFunctionLogs` 返回的 `context` 原样传回即可取下一页;不要自己构造,null 表示没有更多",
205329
205551
  "schema.query.runtime": "层查询的运行时筛选,如 Nodejs18.15",
205330
205552
  "schema.query.searchKey": "层名称搜索关键字",
205331
205553
  "schema.query.layerName": "层名称。`listLayerVersions`、`getLayerVersionDetail` 操作必填。层为账号级共享命名空间;推荐固定格式 `{layerName}_{当前envId}`(如 common_cloud1-d9ghadgak3edf6b36)",
@@ -205384,9 +205606,12 @@ exports.functions = (0, types_js_1.defineModule)({
205384
205606
  "\n- `limit`: page size, the default is decided by the backend" +
205385
205607
  "\n- `offset`: pagination offset, starting from 0" +
205386
205608
  "\n- Example: `queryFunctions(action=\"listFunctions\", offset=10, limit=10)`" +
205387
- "\n\n**Querying cloud function logs**: use `action=\"listFunctionLogs\"`, which requires the `functionName` parameter." +
205609
+ "\n\n**Querying cloud function logs**: use `action=\"listFunctionLogs\"`, which requires `functionName`. Logs are retrieved from CLS (the underlying GetFunctionLogs API has been retired)." +
205388
205610
  "\n- Example: `queryFunctions(action=\"listFunctionLogs\", functionName=\"my-function\")`" +
205389
- "\n- To view log details: `queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
205611
+ "\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" +
205612
+ "\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)" +
205613
+ "\n- Each `logs[]` item is parsed into requestId / log / statusCode / retCode / durationMs / memoryMb / timestamp" +
205614
+ "\n- To see the full sequence of one invocation (START, business logs, REPORT): `queryFunctions(action=\"getFunctionLogDetail\", requestId=\"xxx\")`" +
205390
205615
  "\n\n**Scheduled tasks / cron / timers**: use `listFunctionTriggers` to query a function's timer trigger configuration." +
205391
205616
  "\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`)." +
205392
205617
  "\n\n**Layers**:" +
@@ -205456,8 +205681,8 @@ exports.functions = (0, types_js_1.defineModule)({
205456
205681
  endTimeInvalid: "Invalid endTime: \"{value}\". It must use the YYYY-MM-DD HH:mm:ss format (e.g. 2024-01-01 23:59:59)",
205457
205682
  logRangeInvalidDatetime: "startTime and endTime must be valid datetime strings",
205458
205683
  logRangeTooLong: "The interval between startTime and endTime must not exceed one day",
205459
- 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.",
205460
- 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.",
205684
+ 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.",
205685
+ 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.",
205461
205686
  paramRequired: "The {param} parameter is required for the {action} action",
205462
205687
  "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.",
205463
205688
  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).",
@@ -205472,7 +205697,11 @@ exports.functions = (0, types_js_1.defineModule)({
205472
205697
  "reason.viewLogs": "View the execution logs of this function",
205473
205698
  "reason.updateConfig": "Update this function's configuration",
205474
205699
  "reason.checkGateway": "Check whether this function has an exposed gateway access URL",
205475
- "reason.logDetail": "View a single log entry by requestId",
205700
+ "reason.logDetail": "View the full logs of that invocation by requestId",
205701
+ "reason.nextLogPage": "Pass context back to fetch the next (older) page of logs",
205702
+ "reason.widenLogRange": "Query this function's logs again with a wider time range",
205703
+ "reason.createLogService": "Enable the environment log service before querying function logs",
205704
+ "reason.searchClsFallback": "Search CLS directly with queryLogs using the same query string",
205476
205705
  "reason.attachLayer": "Attach an additional layer to this function",
205477
205706
  "reason.reorderLayers": "Adjust layer order or the binding list as a whole",
205478
205707
  "reason.layerVersions": "View the version list of a layer",
@@ -205517,6 +205746,10 @@ exports.functions = (0, types_js_1.defineModule)({
205517
205746
  gotFunctionDetail: "Retrieved details of function {fnName}",
205518
205747
  gotFunctionLogs: "Retrieved the log list of function {fnName}",
205519
205748
  gotLogDetail: "Retrieved the log detail for requestId={requestId}",
205749
+ "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.",
205750
+ "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.",
205751
+ "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.",
205752
+ "logs.queryFailed": "Failed to query function logs: {message}",
205520
205753
  gotFunctionLayers: "Retrieved the layers currently bound to function {fnName}",
205521
205754
  gotLayers: "Retrieved {count} layer records",
205522
205755
  gotLayerVersions: "Retrieved the version list of layer {layerName}",
@@ -205597,16 +205830,17 @@ exports.functions = (0, types_js_1.defineModule)({
205597
205830
  "schema.create.layers": "Layer configuration",
205598
205831
  "schema.manageLayer.name": "Layer name",
205599
205832
  "schema.manageLayer.version": "Layer version number",
205600
- "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",
205833
+ "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",
205601
205834
  "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).",
205602
- "schema.query.limit": "Page size (limit). Optional for list actions; the backend determines the default.",
205603
- "schema.query.offset": "Pagination offset. Optional for list actions; defaults to 0.",
205835
+ "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.",
205836
+ "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.",
205604
205837
  "schema.query.codeSecret": "Code protection secret used to decrypt function code.",
205605
205838
  "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.",
205606
- "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.",
205607
- "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.",
205608
- "schema.query.requestId": "Log request ID. Required for `getFunctionLogDetail`; obtain it from `listFunctionLogs`.",
205609
- "schema.query.qualifier": "Function version alias, such as $LATEST or $DEFAULT. Optional for log queries.",
205839
+ "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.",
205840
+ "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.",
205841
+ "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.",
205842
+ "schema.query.qualifier": "Function version alias, such as $LATEST or $DEFAULT. Optional for log queries; used as a CLS filter.",
205843
+ "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.",
205610
205844
  "schema.query.runtime": "Runtime filter for layer queries, such as Nodejs18.15.",
205611
205845
  "schema.query.searchKey": "Layer name search keyword.",
205612
205846
  "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).",
@@ -205672,9 +205906,9 @@ exports.gateway = void 0;
205672
205906
  const types_js_1 = __webpack_require__(89921);
205673
205907
  exports.gateway = (0, types_js_1.defineModule)({
205674
205908
  "query.title": "查询 CloudBase 网关",
205675
- "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。",
205909
+ "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。",
205676
205910
  "manage.title": "管理 CloudBase 网关",
205677
- "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 开启)。",
205911
+ "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/重写/限频/扩展配置会保持原值。",
205678
205912
  "verify.failWithDns": "HTTP 服务域名配置预检未通过:域名归属权校验失败。请按 data.checks 中 ownership.dnsRecords 配置 DNS TXT 记录后重试。",
205679
205913
  "verify.fail": "HTTP 服务域名配置预检未通过。请根据 data.checks 修复问题后重试。",
205680
205914
  "verify.failWithDnsReason": "配置 DNS TXT(主机记录/记录类型/记录值见 data.checks[].dnsRecords)后重新调用本 action",
@@ -205692,7 +205926,7 @@ exports.gateway = (0, types_js_1.defineModule)({
205692
205926
  "privilege.nonActivatedHint": ";若路由创建成功但访问报 HTTPSERVICE_NONACTIVATED(403),请先开启 HTTP 网关",
205693
205927
  "privilege.enableServiceReason": "开启 HTTP 网关总开关(EnableService),开启后路由即可通过默认域访问",
205694
205928
  "defaultDomain.notReady": "环境默认 HTTP 访问域名未就绪或未开通。请先在控制台开通 HTTP 访问服务,或用 queryGateway(action=\"listRoutes\") 确认 Domains 中是否存在 DomainType=HTTPSERVICE 且 IsDefault=true 的域名(形如 *.{region}.app.tcloudbase.com);不要使用静态托管域名(*.tcloudbaseapp.com)。也可以显式传入 domain 后重试 createRoute/updateRoute/deleteRoute。",
205695
- "upstreamTypeRequired": "必须提供 upstreamResourceType(或 route.upstreamResourceType):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。禁止仅凭 targetName/serviceName 推断上游类型。",
205929
+ "upstreamTypeRequired": "必须提供 upstreamResourceType(或 route.upstreamResourceType):WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器。禁止仅凭 targetName/serviceName 推断上游类型。",
205696
205930
  "upstreamNameRequired": "必须提供 targetName 或 route.serviceName 作为上游资源名称:云函数名、云托管服务名,或静态托管实例名(常见为 staticstore)。",
205697
205931
  "toggle.pathRequired": "action=enableRoute/disableRoute 时必须提供 path 或 route.path(例如 \"/\" 或 \"/api\")",
205698
205932
  "toggle.notFound": "未找到路径 {path} 的路由{domainSuffix}{upstreamSuffix}。请先用 queryGateway(action=\"listRoutes\") 确认 Domain / Path,再调用 enableRoute/disableRoute。关闭静态托管默认域名(*.tcloudbaseapp.com)时,请显式传 domain=该 STATIC_STORE IsDefault 域名,并通常 path=\"/\"。",
@@ -205758,14 +205992,32 @@ exports.gateway = (0, types_js_1.defineModule)({
205758
205992
  "authSwitch.onMessage": "HTTP 访问服务鉴权开启成功",
205759
205993
  "authSwitch.offMessage": "HTTP 访问服务鉴权关闭成功",
205760
205994
  "authSwitch.verifyReason": "复核 HTTP 网关总开关与访问鉴权状态",
205995
+ "purgeCache.domainRequired": "action=purgeCache 时必须提供 domain",
205996
+ "purgeCache.targetsRequired": "action=purgeCache 时必须提供 targets:PURGE_URL 传完整 URL、PURGE_PREFIX 传路径前缀、PURGE_HOST 传待刷新的域名",
205997
+ "purgeCache.message": "已提交 {count} 个目标的缓存清除任务;CDN/EO 刷新通常数秒到数分钟,请用 queryGateway(action=\"cachePurgeTask\") 按 TaskId 查进度",
205998
+ "purgeCache.verifyReason": "查询缓存清除任务状态",
205999
+ "query.cachePurgeTask": "已获取 {count} 条缓存清除任务记录",
206000
+ "query.cachePurgeTaskDomainRequired": "action=cachePurgeTask 时必须提供 domain",
206001
+ "cache.domainNotFound": "未找到域名 {domain} 的接入配置,无法确定缓存层。请先用 queryGateway(action=\"listRoutes\") 确认域名。",
206002
+ "cache.notManaged": "域名 {domain} 的接入方式是 {accessType},CloudBase 侧没有该域名的 CDN/EO 缓存,缓存刷新不适用(DIRECT 直连与 CUSTOM 自有 CDN 的缓存不由平台托管)。如需刷新,请先把域名切到 CDN 或 EO 接入。",
206003
+ "cache.typeMismatch": "域名 {domain} 的接入方式是 {accessType},不能按 cacheType={requested} 刷新。省略 cacheType 就会按接入方式自动匹配。",
205761
206004
  // Input schema parameter descriptions
205762
- "schema.query.action": "只读操作类型:listRoutes、getRoute、listCustomDomains、getPrivilege。" +
206005
+ "schema.query.action": "只读操作类型:listRoutes、getRoute、listCustomDomains、getPrivilege、cachePurgeTask。" +
205763
206006
  "getPrivilege 无需其他参数,直接返回 HTTP 网关总开关与访问鉴权状态。" +
206007
+ "cachePurgeTask 需要 domain(可选 cacheType/taskId/purgeType/startTime/endTime/offset/limit)。" +
205764
206008
  "自定义域名访问场景先 listCustomDomains 确认是否已有域名可复用。",
205765
206009
  "schema.query.targetName": "上游资源名过滤(UpstreamResourceName):云函数名、云托管服务名,或静态托管实例名(常见 staticstore)。",
205766
206010
  "schema.query.routeId": "路由 ID。getRoute 时可选",
205767
206011
  "schema.query.path": "路由路径。getRoute / listRoutes 过滤时可选",
205768
- "schema.query.domain": "域名。getRoute / listRoutes 过滤时可选",
206012
+ "schema.query.domain": "域名。getRoute / listRoutes 过滤时可选;cachePurgeTask 时必填",
206013
+ "schema.query.cacheType": "缓存层(仅 cachePurgeTask):CDN 或 EO。省略时按域名接入方式自动匹配(EO→EO、CDN→CDN);" +
206014
+ "DIRECT / CUSTOM / NONE 的域没有 CloudBase 托管的缓存,会直接拒绝并说明原因。",
206015
+ "schema.query.taskId": "缓存清除任务 ID(仅 cachePurgeTask,可选过滤)",
206016
+ "schema.query.purgeType": "清除方式过滤(仅 cachePurgeTask):PURGE_URL=按 URL、PURGE_PREFIX=按前缀、PURGE_HOST=整站",
206017
+ "schema.query.startTime": "任务开始时间(仅 cachePurgeTask,可选)。格式 YYYY-MM-DD HH:mm:ss",
206018
+ "schema.query.endTime": "任务结束时间(仅 cachePurgeTask,可选)。格式 YYYY-MM-DD HH:mm:ss",
206019
+ "schema.query.offset": "分页偏移(仅 cachePurgeTask,可选)",
206020
+ "schema.query.limit": "返回数量上限(仅 cachePurgeTask,可选)",
205769
206021
  "schema.manage.action": "写操作:createRoute/updateRoute/deleteRoute 管理路由;enableRoute/disableRoute 启用或禁用已有路由(需 path,建议显式传 domain);" +
205770
206022
  "bindCustomDomain/deleteCustomDomain 管理自定义域名;" +
205771
206023
  "enableService/authSwitch 开关 HTTP 网关总开关与访问鉴权(需配合 enable 参数)。" +
@@ -205775,13 +206027,15 @@ exports.gateway = (0, types_js_1.defineModule)({
205775
206027
  "已有自定义域名时优先 createRoute(domain=已有域名) 实现访问,不必再次 bindCustomDomain / 传入 certificateId;" +
205776
206028
  "bindCustomDomain 仅用于首次绑定新域名(certificateId 可选,未传则按域名自动检索;可选 accessType=DIRECT|CDN|CUSTOM,CUSTOM 需 customCname;普通场景用默认 DIRECT)。" +
205777
206029
  "createRoute/bindCustomDomain 创建前会 VerifyHTTPServiceRoute 预检;失败时按返回的 DNS TXT 指引配置后重试。" +
206030
+ "purgeCache 清除域名缓存:需 domain 与 targets(配 purgeType=PURGE_URL|PURGE_PREFIX|PURGE_HOST,可选 cacheType=CDN|EO)。" +
205778
206031
  "接入说明:https://docs.cloudbase.net/service/custom-domain",
205779
206032
  "schema.manage.targetName": "上游资源名称(UpstreamResourceName),与 route.serviceName 二选一(route 优先)。" +
205780
206033
  "云函数=函数名;云托管=服务名;静态托管=实例名(常见 staticstore)。不会自动推断上游类型。",
205781
206034
  "schema.manage.path": "触发路径(网关匹配前缀),默认 /{上游名}。例:云函数 /api/hello、云托管 /api、静态托管 / 或 /app。" +
205782
206035
  "只建网关入口;与 enablePathTransmission 共同决定上游实际收到的路径。",
205783
206036
  "schema.manage.upstreamResourceType": "上游类型(与 route.upstreamResourceType 二选一,route 优先)。" +
205784
- "WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,LH=轻量应用服务器。" +
206037
+ "WEB_SCF=HTTP云函数,SCF=Event云函数,CBR=云托管,STATIC_STORE=静态托管,STORAGE=云存储,LH=轻量应用服务器。" +
206038
+ "STORAGE 的 UpstreamResourceName 是云存储实例名,可先用 queryStorage 查实例。" +
205785
206039
  "createRoute/updateRoute 必填其一;勿把 manageFunctions 的 type=HTTP|Event 传到本字段。",
205786
206040
  "schema.manage.auth": "网关路径鉴权(EnableAuth)。匿名/浏览器公网访问通常 false。" +
205787
206041
  "只控制网关入口;云函数安全规则、云托管鉴权、静态托管权限需各自工具另行配置。",
@@ -205790,6 +206044,30 @@ exports.gateway = (0, types_js_1.defineModule)({
205790
206044
  "CBR 云托管(Express 等自管子路由)与 WEB_SCF 多路径函数常需 true;" +
205791
206045
  "STATIC_STORE 把触发路径映射到站点根目录(如 /app → 托管 /)时通常 false;" +
205792
206046
  "单入口/根路径处理保持 false。也可用 route.enablePathTransmission(route 优先)。",
206047
+ "schema.manage.enableSafeDomain": "跨域/合法域名校验(EnableSafeDomain),即控制台的「跨域设置」。true 时校验请求来源域名," +
206048
+ "false 时不校验(浏览器直连调用 API 的常见选择)。也可用 route.enableSafeDomain(route 优先)。",
206049
+ "schema.manage.pathRewrite": "路径重写(PathRewrite),形如 {prefix:\"/v1\", staticStorePrefix:\"app\"}。" +
206050
+ "prefix=命中路径前缀后替换为的内容;staticStorePrefix=静态托管场景下的目标前缀。" +
206051
+ "不需要改写就省略;也可用 route.pathRewrite(route 优先)。",
206052
+ "schema.manage.qpsPolicy": "限频(QPSPolicy),即控制台的「频限」。{total:总 QPS, perClient:{limitBy:\"ClientIP\"|\"UserID\", limitValue:单客户端 QPS}}。" +
206053
+ "不传则不限频;也可用 route.qpsPolicy(route 优先)。",
206054
+ "schema.manage.extension": "路由级扩展(Extension),可配自定义请求/响应头与缓存规则。" +
206055
+ "{headersHandler:{requestHeadersToAdd,requestHeadersToRemove,responseHeadersToAdd,responseHeadersToRemove}," +
206056
+ "cache:{rules:[{description,enable,condition:{target,matchType,values},actions:[{type:\"Cache\"|\"CacheKey\",cache,cacheKey}]}]}}。" +
206057
+ "⚠️ 默认域(*.{region}.app.tcloudbase.com)不支持自定义响应头,后端会拒绝(实测 2026-10-21),需要自定义域名才能配。" +
206058
+ "也可用 route.extension(route 优先)。",
206059
+ "schema.manage.extension.header.key": "请求/响应头名称,如 X-Frame-Options",
206060
+ "schema.manage.extension.header.value": "请求/响应头取值,如 SAMEORIGIN",
206061
+ "schema.manage.extension.header.action": "写入策略(可选):APPEND_IF_EXISTS_OR_ADD 追加;ADD_IF_ABSENT 不存在才加;" +
206062
+ "OVERWRITE_IF_EXISTS_OR_ADD 覆盖或新增;OVERWRITE_IF_EXISTS 仅覆盖已存在的",
206063
+ "schema.manage.extension.cacheCondition": "缓存规则匹配条件:target=url_path/file_extension/full_uri,matchType=prefix/suffix/contains/exact,values=匹配值数组",
206064
+ "schema.manage.extension.cacheActions": "缓存动作数组:type=Cache 配 cache{followOrigin,noCache,cacheTime,maxAgeTime}(三者互斥,需开启其一);" +
206065
+ "type=CacheKey 配 cacheKey{fullUrlCache,queryStringSwitch,queryStringAction,queryStringValues}",
206066
+ "schema.manage.domainExtension": "域名级扩展(Extension),作用于整个域名(含该域名下所有路径)的自定义头与缓存规则,结构同 extension。" +
206067
+ "缓存规则通常配在域名级;仅 createRoute/updateRoute/bindCustomDomain 生效。" +
206068
+ "⚠️ 同 extension:默认域不支持自定义响应头,需自定义域名。",
206069
+ "schema.manage.protocol": "域名协议(Protocol),仅 createRoute/updateRoute/bindCustomDomain 生效:" +
206070
+ "HTTP / HTTPS / HTTP_AND_HTTPS / HTTP_TO_HTTPS / HTTPS_TO_HTTP。省略则沿用域名现有配置。",
205793
206071
  "schema.manage.route.serviceName": "上游实例名:云函数名 / 云托管服务名 / 静态托管实例名(常见 staticstore)/ LH 实例。优先于顶层 targetName。",
205794
206072
  "schema.manage.route.upstreamResourceType": "同顶层 upstreamResourceType。route 内设置时优先于顶层。",
205795
206073
  "schema.manage.route.enablePathTransmission": "同顶层 enablePathTransmission。route 内设置时优先于顶层。",
@@ -205797,6 +206075,10 @@ exports.gateway = (0, types_js_1.defineModule)({
205797
206075
  "enable=false 禁用该 Domain+Path(访问返回 GATEWAY_ROUTE_DISABLED);" +
205798
206076
  "enable=true 重新启用。updateRoute 也可用顶层 enable 表达同一语义;" +
205799
206077
  "也可用专用 action enableRoute/disableRoute。route.enable 优先于顶层 enable。",
206078
+ "schema.manage.route.enableSafeDomain": "同顶层 enableSafeDomain(跨域/合法域名校验)。route 内设置时优先于顶层。",
206079
+ "schema.manage.route.pathRewrite": "同顶层 pathRewrite。route 内设置时优先于顶层。",
206080
+ "schema.manage.route.qpsPolicy": "同顶层 qpsPolicy(限频)。route 内设置时优先于顶层。",
206081
+ "schema.manage.route.extension": "同顶层 extension(路由级自定义头与缓存规则)。route 内设置时优先于顶层。",
205800
206082
  "schema.manage.route": "路由对象(可选写法)。例:云函数 {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"};" +
205801
206083
  "云托管 {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"};" +
205802
206084
  "静态托管 {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"};" +
@@ -205821,11 +206103,15 @@ exports.gateway = (0, types_js_1.defineModule)({
205821
206103
  "updateRoute 可选:映射到 Routes[].Enable(也可用 route.enable,route 优先;" +
205822
206104
  "也可用专用 action enableRoute/disableRoute)。" +
205823
206105
  "省略或非布尔值在 enableService/authSwitch 会返回参数错误。",
206106
+ "schema.manage.cacheType": "缓存层(仅 purgeCache):CDN 或 EO。省略时按域名接入方式自动匹配(EO→EO、CDN→CDN);" +
206107
+ "DIRECT / CUSTOM / NONE 的域没有 CloudBase 托管的缓存,会直接拒绝并说明原因。",
206108
+ "schema.manage.purgeType": "清除方式(仅 purgeCache,默认 PURGE_URL):PURGE_URL=按完整 URL、PURGE_PREFIX=按路径前缀、PURGE_HOST=整个域名",
206109
+ "schema.manage.targets": "清除目标(仅 purgeCache,必填):PURGE_URL 传 https://域名/路径 形式;PURGE_PREFIX 传 /static/ 这样的前缀;PURGE_HOST 传域名本身",
205824
206110
  }, {
205825
206111
  "query.title": "Query CloudBase Gateway",
205826
- "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.",
206112
+ "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.",
205827
206113
  "manage.title": "Manage CloudBase Gateway",
205828
- "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).",
206114
+ "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.",
205829
206115
  "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.",
205830
206116
  "verify.fail": "HTTP service domain pre-check failed. Fix the issues per data.checks, then retry.",
205831
206117
  "verify.failWithDnsReason": "Configure the DNS TXT records (host/record type/record value in data.checks[].dnsRecords), then call this action again",
@@ -205843,7 +206129,7 @@ exports.gateway = (0, types_js_1.defineModule)({
205843
206129
  "privilege.nonActivatedHint": "; if the route is created successfully but access returns HTTPSERVICE_NONACTIVATED (403), enable the HTTP gateway first",
205844
206130
  "privilege.enableServiceReason": "Turn on the HTTP gateway master switch (EnableService); once on, routes are accessible via the default domain",
205845
206131
  "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.",
205846
- "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.",
206132
+ "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.",
205847
206133
  "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).",
205848
206134
  "toggle.pathRequired": "path or route.path is required for action=enableRoute/disableRoute (e.g. \"/\" or \"/api\")",
205849
206135
  "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=\"/\".",
@@ -205909,14 +206195,32 @@ exports.gateway = (0, types_js_1.defineModule)({
205909
206195
  "authSwitch.onMessage": "HTTP access service auth enabled successfully",
205910
206196
  "authSwitch.offMessage": "HTTP access service auth disabled successfully",
205911
206197
  "authSwitch.verifyReason": "Verify the HTTP gateway master switch and access auth status",
206198
+ "purgeCache.domainRequired": "domain is required for action=purgeCache",
206199
+ "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",
206200
+ "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",
206201
+ "purgeCache.verifyReason": "Query the cache purge task status",
206202
+ "query.cachePurgeTask": "Retrieved {count} cache purge task record(s)",
206203
+ "query.cachePurgeTaskDomainRequired": "domain is required for action=cachePurgeTask",
206204
+ "cache.domainNotFound": "No access configuration found for domain {domain}, so the cache layer is undetermined. Confirm the domain with queryGateway(action=\"listRoutes\") first.",
206205
+ "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.",
206206
+ "cache.typeMismatch": "Domain {domain} uses access type {accessType} and cannot be purged with cacheType={requested}. Omit cacheType to match the access type automatically.",
205912
206207
  // Input schema parameter descriptions
205913
- "schema.query.action": "Read-only action: listRoutes, getRoute, listCustomDomains, getPrivilege. " +
206208
+ "schema.query.action": "Read-only action: listRoutes, getRoute, listCustomDomains, getPrivilege, cachePurgeTask. " +
205914
206209
  "getPrivilege needs no other parameter and returns the HTTP gateway master switch and access auth status directly. " +
206210
+ "cachePurgeTask requires domain (optional cacheType/taskId/purgeType/startTime/endTime/offset/limit). " +
205915
206211
  "For custom domain access, run listCustomDomains first to check whether an existing domain can be reused.",
205916
206212
  "schema.query.targetName": "Upstream resource name filter (UpstreamResourceName): a cloud function name, CloudRun service name, or static-hosting instance name (commonly staticstore).",
205917
206213
  "schema.query.routeId": "Route ID. Optional for getRoute",
205918
206214
  "schema.query.path": "Route path. Optional as a getRoute / listRoutes filter",
205919
- "schema.query.domain": "Domain. Optional as a getRoute / listRoutes filter",
206215
+ "schema.query.domain": "Domain. Optional as a getRoute / listRoutes filter; required for cachePurgeTask",
206216
+ "schema.query.cacheType": "Cache layer (cachePurgeTask only): CDN or EO. When omitted it is matched from the domain access type (EO→EO, CDN→CDN); " +
206217
+ "DIRECT / CUSTOM / NONE domains have no CloudBase-hosted cache and are rejected with an explanation.",
206218
+ "schema.query.taskId": "Cache purge task ID (cachePurgeTask only, optional filter)",
206219
+ "schema.query.purgeType": "Purge type filter (cachePurgeTask only): PURGE_URL=by URL, PURGE_PREFIX=by path prefix, PURGE_HOST=whole domain",
206220
+ "schema.query.startTime": "Task start time (cachePurgeTask only, optional). Format YYYY-MM-DD HH:mm:ss",
206221
+ "schema.query.endTime": "Task end time (cachePurgeTask only, optional). Format YYYY-MM-DD HH:mm:ss",
206222
+ "schema.query.offset": "Pagination offset (cachePurgeTask only, optional)",
206223
+ "schema.query.limit": "Max records to return (cachePurgeTask only, optional)",
205920
206224
  "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); " +
205921
206225
  "bindCustomDomain/deleteCustomDomain manage custom domains; " +
205922
206226
  "enableService/authSwitch toggle the HTTP gateway master switch and access auth (requires the enable parameter). " +
@@ -205926,13 +206230,15 @@ exports.gateway = (0, types_js_1.defineModule)({
205926
206230
  "When a custom domain already exists, prefer createRoute(domain=existing domain) for access instead of calling bindCustomDomain / passing certificateId again; " +
205927
206231
  "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). " +
205928
206232
  "createRoute/bindCustomDomain run a VerifyHTTPServiceRoute pre-check before creation; on failure, follow the returned DNS TXT guidance and retry. " +
206233
+ "purgeCache clears domain cache: requires domain and targets (with purgeType=PURGE_URL|PURGE_PREFIX|PURGE_HOST, optional cacheType=CDN|EO). " +
205929
206234
  "Setup guide: https://docs.cloudbase.net/service/custom-domain",
205930
206235
  "schema.manage.targetName": "Upstream resource name (UpstreamResourceName); use either this or route.serviceName (route wins). " +
205931
206236
  "Cloud function=function name; CloudRun=service name; static hosting=instance name (commonly staticstore). The upstream type is never inferred automatically.",
205932
206237
  "schema.manage.path": "Trigger path (the gateway match prefix), defaults to /{upstream name}. Examples: cloud function /api/hello, CloudRun /api, static hosting / or /app. " +
205933
206238
  "This only creates the gateway entry; together with enablePathTransmission it determines the path the upstream actually receives.",
205934
206239
  "schema.manage.upstreamResourceType": "Upstream type (use either this or route.upstreamResourceType, route wins). " +
205935
- "WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, LH=Lighthouse. " +
206240
+ "WEB_SCF=HTTP cloud function, SCF=Event cloud function, CBR=CloudRun, STATIC_STORE=static hosting, STORAGE=cloud storage, LH=Lighthouse. " +
206241
+ "For STORAGE, UpstreamResourceName is the cloud storage instance name — look it up with queryStorage first. " +
205936
206242
  "One of them is required for createRoute/updateRoute; do not pass the manageFunctions type=HTTP|Event value into this field.",
205937
206243
  "schema.manage.auth": "Gateway path auth (EnableAuth). Usually false for anonymous/browser public access. " +
205938
206244
  "It only controls the gateway entry; cloud function security rules, CloudRun auth, and static hosting permissions must be configured with their own tools.",
@@ -205941,6 +206247,31 @@ exports.gateway = (0, types_js_1.defineModule)({
205941
206247
  "CBR CloudRun (Express and similar self-managed sub-routes) and multi-path WEB_SCF functions often need true; " +
205942
206248
  "STATIC_STORE mapping a trigger path to the site root (e.g. /app → hosting /) is usually false; " +
205943
206249
  "keep false for single-entry/root-path handling. route.enablePathTransmission also works (route wins).",
206250
+ "schema.manage.enableSafeDomain": "CORS / legit-domain check (EnableSafeDomain), the console's \"cross-origin settings\". " +
206251
+ "true validates the request origin; false skips the check (common for browser-to-API calls). " +
206252
+ "route.enableSafeDomain also works (route wins).",
206253
+ "schema.manage.pathRewrite": "Path rewriting (PathRewrite), e.g. {prefix:\"/v1\", staticStorePrefix:\"app\"}. " +
206254
+ "prefix replaces the matched path prefix; staticStorePrefix is the target prefix for static hosting. " +
206255
+ "Omit when no rewriting is needed; route.pathRewrite also works (route wins).",
206256
+ "schema.manage.qpsPolicy": "Rate limiting (QPSPolicy), the console's \"frequency limit\". {total: total QPS, perClient:{limitBy:\"ClientIP\"|\"UserID\", limitValue: per-client QPS}}. " +
206257
+ "Omit for no limit; route.qpsPolicy also works (route wins).",
206258
+ "schema.manage.extension": "Route-level extension (Extension): custom request/response headers and cache rules. " +
206259
+ "{headersHandler:{requestHeadersToAdd,requestHeadersToRemove,responseHeadersToAdd,responseHeadersToRemove}," +
206260
+ "cache:{rules:[{description,enable,condition:{target,matchType,values},actions:[{type:\"Cache\"|\"CacheKey\",cache,cacheKey}]}]}}. " +
206261
+ "⚠️ The default domain (*.{region}.app.tcloudbase.com) rejects custom response headers (verified 2026-10-21); a custom domain is required. " +
206262
+ "route.extension also works (route wins).",
206263
+ "schema.manage.extension.header.key": "Header name, e.g. X-Frame-Options",
206264
+ "schema.manage.extension.header.value": "Header value, e.g. SAMEORIGIN",
206265
+ "schema.manage.extension.header.action": "Write strategy (optional): APPEND_IF_EXISTS_OR_ADD appends; ADD_IF_ABSENT only adds when missing; " +
206266
+ "OVERWRITE_IF_EXISTS_OR_ADD overwrites or adds; OVERWRITE_IF_EXISTS only overwrites existing",
206267
+ "schema.manage.extension.cacheCondition": "Cache rule match condition: target=url_path/file_extension/full_uri, matchType=prefix/suffix/contains/exact, values=match value list",
206268
+ "schema.manage.extension.cacheActions": "Cache action list: type=Cache with cache{followOrigin,noCache,cacheTime,maxAgeTime} (mutually exclusive — enable one); " +
206269
+ "type=CacheKey with cacheKey{fullUrlCache,queryStringSwitch,queryStringAction,queryStringValues}",
206270
+ "schema.manage.domainExtension": "Domain-level extension (Extension) applied to the whole domain (all paths): custom headers and cache rules, same shape as extension. " +
206271
+ "Cache rules usually belong here; only effective for createRoute/updateRoute/bindCustomDomain. " +
206272
+ "⚠️ Same as extension: the default domain rejects custom response headers; a custom domain is required.",
206273
+ "schema.manage.protocol": "Domain protocol (Protocol), effective for createRoute/updateRoute/bindCustomDomain only: " +
206274
+ "HTTP / HTTPS / HTTP_AND_HTTPS / HTTP_TO_HTTPS / HTTPS_TO_HTTP. Omit to keep the domain's current setting.",
205944
206275
  "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.",
205945
206276
  "schema.manage.route.upstreamResourceType": "Same as the top-level upstreamResourceType. When set inside route, it takes precedence over the top level.",
205946
206277
  "schema.manage.route.enablePathTransmission": "Same as the top-level enablePathTransmission. When set inside route, it takes precedence over the top level.",
@@ -205948,6 +206279,10 @@ exports.gateway = (0, types_js_1.defineModule)({
205948
206279
  "enable=false disables this Domain+Path (access returns GATEWAY_ROUTE_DISABLED); " +
205949
206280
  "enable=true re-enables it. updateRoute can express the same semantics with the top-level enable; " +
205950
206281
  "the dedicated actions enableRoute/disableRoute also work. route.enable takes precedence over the top-level enable.",
206282
+ "schema.manage.route.enableSafeDomain": "Same as the top-level enableSafeDomain (CORS / legit-domain check). When set inside route, it takes precedence.",
206283
+ "schema.manage.route.pathRewrite": "Same as the top-level pathRewrite. When set inside route, it takes precedence.",
206284
+ "schema.manage.route.qpsPolicy": "Same as the top-level qpsPolicy (rate limiting). When set inside route, it takes precedence.",
206285
+ "schema.manage.route.extension": "Same as the top-level extension (route-level custom headers and cache rules). When set inside route, it takes precedence.",
205951
206286
  "schema.manage.route": "Route object (an optional form). Examples: cloud function {upstreamResourceType:\"WEB_SCF\",serviceName:\"fn\",path:\"/api\"}; " +
205952
206287
  "CloudRun {upstreamResourceType:\"CBR\",serviceName:\"svc\",path:\"/api\"}; " +
205953
206288
  "static hosting {upstreamResourceType:\"STATIC_STORE\",serviceName:\"staticstore\",path:\"/\"}; " +
@@ -205972,6 +206307,10 @@ exports.gateway = (0, types_js_1.defineModule)({
205972
206307
  "optional for updateRoute: maps to Routes[].Enable (route.enable also works and wins; " +
205973
206308
  "the dedicated actions enableRoute/disableRoute are available too). " +
205974
206309
  "Omitting it or passing a non-boolean returns a parameter error for enableService/authSwitch.",
206310
+ "schema.manage.cacheType": "Cache layer (purgeCache only): CDN or EO. When omitted it is matched from the domain access type (EO→EO, CDN→CDN); " +
206311
+ "DIRECT / CUSTOM / NONE domains have no CloudBase-hosted cache and are rejected with an explanation.",
206312
+ "schema.manage.purgeType": "Purge type (purgeCache only, default PURGE_URL): PURGE_URL=by full URL, PURGE_PREFIX=by path prefix, PURGE_HOST=whole domain",
206313
+ "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",
205975
206314
  });
205976
206315
 
205977
206316
 
@@ -208046,7 +208385,7 @@ function localizeZodSchemaShape(shape, lang, seen = new WeakMap()) {
208046
208385
  "use strict";
208047
208386
 
208048
208387
  Object.defineProperty(exports, "__esModule", ({ value: true }));
208049
- 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;
208388
+ 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;
208050
208389
  // CloudBase MCP Server Library
208051
208390
  var server_js_1 = __webpack_require__(46324);
208052
208391
  Object.defineProperty(exports, "createCloudBaseMcpServer", ({ enumerable: true, get: function () { return server_js_1.createCloudBaseMcpServer; } }));
@@ -208066,6 +208405,13 @@ Object.defineProperty(exports, "isCloudMode", ({ enumerable: true, get: function
208066
208405
  Object.defineProperty(exports, "enableCloudMode", ({ enumerable: true, get: function () { return cloud_mode_js_1.enableCloudMode; } }));
208067
208406
  Object.defineProperty(exports, "getCloudModeStatus", ({ enumerable: true, get: function () { return cloud_mode_js_1.getCloudModeStatus; } }));
208068
208407
  Object.defineProperty(exports, "shouldRegisterTool", ({ enumerable: true, get: function () { return cloud_mode_js_1.shouldRegisterTool; } }));
208408
+ /**
208409
+ * 环境绑定槽。外部宿主(自己起 transport 的那种)要上报真实的 envId,
208410
+ * 就得在自己的一次 run 里跑 server 的消息处理,否则上报恒为 `default`。
208411
+ */
208412
+ var env_binding_js_1 = __webpack_require__(61498);
208413
+ Object.defineProperty(exports, "ENV_SOURCES", ({ enumerable: true, get: function () { return env_binding_js_1.ENV_SOURCES; } }));
208414
+ Object.defineProperty(exports, "runWithEnvSlot", ({ enumerable: true, get: function () { return env_binding_js_1.runWithEnvSlot; } }));
208069
208415
  var cloudbase_manager_js_1 = __webpack_require__(33109);
208070
208416
  Object.defineProperty(exports, "getCloudBaseManager", ({ enumerable: true, get: function () { return cloudbase_manager_js_1.getCloudBaseManager; } }));
208071
208417
  Object.defineProperty(exports, "getEnvId", ({ enumerable: true, get: function () { return cloudbase_manager_js_1.getEnvId; } }));
@@ -209202,7 +209548,7 @@ exports.buildEnvCreditsHtml = buildEnvCreditsHtml;
209202
209548
  exports.registerEnvCreditsResource = registerEnvCreditsResource;
209203
209549
  const mcp_js_1 = __webpack_require__(55375);
209204
209550
  const index_js_1 = __webpack_require__(24908);
209205
- const cloudbase_manager_js_1 = __webpack_require__(33109);
209551
+ const env_binding_js_1 = __webpack_require__(61498);
209206
209552
  const site_map_js_1 = __webpack_require__(34533);
209207
209553
  const logger_js_1 = __webpack_require__(40781);
209208
209554
  const telemetry_js_1 = __webpack_require__(5198);
@@ -209272,17 +209618,12 @@ function buildEnvCreditsConsoleUrl(key, envId, site) {
209272
209618
  return (0, site_map_js_1.getConsoleDevUrl)(envId ?? undefined, exports.ENV_CREDITS_ENV_LINK_HASH[key], site);
209273
209619
  }
209274
209620
  /**
209275
- * 从 server 实例派生静态兜底值。
209276
- *
209277
- * 环境 ID 的取值优先级(与 get-started 同口径,新增来源时同步两处):
209278
- * ① 本次工具结果携带(面板侧覆盖,见 EnvCreditsPayload)
209279
- * → ② `server.cloudBaseOptions.envId` / `process.env.CLOUDBASE_ENV_ID`
209280
- * → ③ 进程缓存 `getCachedEnvId()`
209281
- * → ④ 降级:不显示 ID
209282
- * hosted 是无状态的,进程缓存不可靠,因此 ③ 只是本地兜底;面板永远以工具结果为准。
209621
+ * Static fallback until a tool result arrives.
209622
+ * Explicit `cloudBaseOptions.envId` wins. Otherwise the current slot.
209623
+ * Does not read `CLOUDBASE_ENV_ID` or the process cache.
209283
209624
  */
209284
209625
  function resolveEnvCreditsFallback(server) {
209285
- const envId = server.cloudBaseOptions?.envId || process.env.CLOUDBASE_ENV_ID || (0, cloudbase_manager_js_1.getCachedEnvId)() || null;
209626
+ const envId = (0, env_binding_js_1.readDisplayedEnvId)(server.cloudBaseOptions?.envId);
209286
209627
  const { site, region } = (0, site_map_js_1.resolveSiteAndRegion)({
209287
209628
  site: server.cloudBaseOptions?.site,
209288
209629
  region: server.cloudBaseOptions?.region,
@@ -210443,7 +210784,7 @@ exports.registerGetStartedResource = registerGetStartedResource;
210443
210784
  * 注入;`t()` 在 read 回调里求值,晚于 `setInstanceLang`,英文实例自动出英文。
210444
210785
  * 注入分两路:HTML 文本走 `escapeHtml`,脚本里的字符串走 `jsLiteral`(JSON 转义 +
210445
210786
  * 转义 `<`,避免 `</script>` 截断)。
210446
- * 3. **只读无密钥**:状态值只来自 `server.cloudBaseOptions` / 环境变量 / 缓存 / 站点解析,
210787
+ * 3. **只读无密钥**:环境 ID 来自这次调用槽;槽是空的才用 `server.cloudBaseOptions.envId`。不读环境变量和缓存。站点仍走站点解析。
210447
210788
  * 读资源时不发网络请求;面板自己只会发两类调用——只读的
210448
210789
  * `auth(action:"status")`(「重新检查」)和只改本地绑定的
210449
210790
  * `auth(action:"set_env")`(点「可选环境」里的一行)。两者都不动云端资源,也不碰密钥。
@@ -210464,7 +210805,7 @@ exports.registerGetStartedResource = registerGetStartedResource;
210464
210805
  */
210465
210806
  const mcp_js_1 = __webpack_require__(55375);
210466
210807
  const index_js_1 = __webpack_require__(24908);
210467
- const cloudbase_manager_js_1 = __webpack_require__(33109);
210808
+ const env_binding_js_1 = __webpack_require__(61498);
210468
210809
  const site_map_js_1 = __webpack_require__(34533);
210469
210810
  const logger_js_1 = __webpack_require__(40781);
210470
210811
  const telemetry_js_1 = __webpack_require__(5198);
@@ -210508,12 +210849,9 @@ function escapeHtml(value) {
210508
210849
  function jsLiteral(value) {
210509
210850
  return JSON.stringify(value).replace(/</g, "\\u003c");
210510
210851
  }
210511
- /** 从 server 实例派生静态兜底状态;只读本地状态,不发起任何请求。 */
210852
+ /** Static fallback for the panel. Reads the current slot, then this server's explicit envId. No network. */
210512
210853
  function resolveGetStartedStatus(server) {
210513
- const envId = server.cloudBaseOptions?.envId ||
210514
- process.env.CLOUDBASE_ENV_ID ||
210515
- (0, cloudbase_manager_js_1.getCachedEnvId)() ||
210516
- null;
210854
+ const envId = (0, env_binding_js_1.readDisplayedEnvId)(server.cloudBaseOptions?.envId);
210517
210855
  const { site, region } = (0, site_map_js_1.resolveSiteAndRegion)({
210518
210856
  site: server.cloudBaseOptions?.site,
210519
210857
  region: server.cloudBaseOptions?.region,
@@ -211522,6 +211860,7 @@ const index_js_1 = __webpack_require__(24908);
211522
211860
  const schema_js_1 = __webpack_require__(56273);
211523
211861
  const server_instructions_js_1 = __webpack_require__(81808);
211524
211862
  const cloud_mode_js_1 = __webpack_require__(88694);
211863
+ const env_binding_js_1 = __webpack_require__(61498);
211525
211864
  const logger_js_1 = __webpack_require__(40781);
211526
211865
  const site_map_js_1 = __webpack_require__(34533);
211527
211866
  const tool_result_js_1 = __webpack_require__(69645);
@@ -211710,6 +212049,8 @@ async function createCloudBaseMcpServer(options) {
211710
212049
  });
211711
212050
  // 初始化 toolDefs,用于外部提取工具列表(如微信 IDE)
211712
212051
  server.toolDefs = [];
212052
+ const envSlot = { binding: null };
212053
+ server.envSlot = envSlot;
211713
212054
  // 实例语言解析(须在工具注册前完成:registerTool 包装层按 lang 解析词典 key 形式的 description)
211714
212055
  server.lang = instanceLang;
211715
212056
  // 同步到 i18n 模块级 instanceLang:否则 t() 无 langOverride 时会退化成
@@ -211867,6 +212208,23 @@ async function createCloudBaseMcpServer(options) {
211867
212208
  // 产物卡片:挂在三个部署类工具上,只读展示部署状态与访问地址,写意图一律交给会话。
211868
212209
  (0, artifact_card_js_1.registerArtifactCardResource)(server);
211869
212210
  }
212211
+ const originalConnect = server.connect.bind(server);
212212
+ server.connect = async (transport) => {
212213
+ await originalConnect(transport);
212214
+ const deliver = transport.onmessage;
212215
+ if (!deliver) {
212216
+ return;
212217
+ }
212218
+ transport.onmessage = (message, extra) => {
212219
+ if ((0, env_binding_js_1.hasEnvSlot)()) {
212220
+ deliver(message, extra);
212221
+ return;
212222
+ }
212223
+ (0, env_binding_js_1.runWithEnvSlot)(envSlot, () => {
212224
+ deliver(message, extra);
212225
+ });
212226
+ };
212227
+ };
211870
212228
  return server;
211871
212229
  }
211872
212230
  /**
@@ -223907,6 +224265,7 @@ function buildEnvMetricsPayload(input) {
223907
224265
  * - Error handling and telemetry reporting
223908
224266
  */
223909
224267
  Object.defineProperty(exports, "__esModule", ({ value: true }));
224268
+ exports.TCB_PURCHASE_URL = void 0;
223910
224269
  exports.checkAndInitTcbService = checkAndInitTcbService;
223911
224270
  exports.checkAndCreateFreeEnv = checkAndCreateFreeEnv;
223912
224271
  exports.getUinForTelemetry = getUinForTelemetry;
@@ -223914,6 +224273,17 @@ const auth_js_1 = __webpack_require__(561);
223914
224273
  const index_js_1 = __webpack_require__(24908);
223915
224274
  const logger_js_1 = __webpack_require__(40781);
223916
224275
  const telemetry_js_1 = __webpack_require__(5198);
224276
+ /**
224277
+ * 开通 CloudBase 环境的购买页。
224278
+ *
224279
+ * ⚠️ 别再写回 `/lowcode?buyType=tcb&channel=mcp`:那条路径现在 302 到 `/CloudBase`,
224280
+ * **重定向时把 query 全丢了**(实测 `location: https://buy.cloud.tencent.com/CloudBase`),
224281
+ * 用户会落到购买页首页、来源标记也一起丢。
224282
+ *
224283
+ * ⚠️ `region` / `sqlType` 是写死的:国内站默认 ap-shanghai + PG 版。国际站
224284
+ * (`buy.tencentcloud.com`)的域名与路径同构性未验证,见 `specs/site-domain-governance`。
224285
+ */
224286
+ exports.TCB_PURCHASE_URL = "https://buy.cloud.tencent.com/CloudBase?buyType=tcb&region=ap-shanghai&sqlType=pg";
223917
224287
  /**
223918
224288
  * Parse InitTcb error and extract user-friendly information
223919
224289
  */
@@ -223936,7 +224306,7 @@ function parseInitTcbError(err) {
223936
224306
  const errorInfo = {
223937
224307
  code: errorCode,
223938
224308
  message: friendlyMessage,
223939
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp",
224309
+ helpUrl: exports.TCB_PURCHASE_URL,
223940
224310
  actionText,
223941
224311
  requestId
223942
224312
  };
@@ -224172,7 +224542,7 @@ async function checkAndCreateFreeEnv(cloudbase, context) {
224172
224542
  newContext.createEnvError = {
224173
224543
  code: "NoPromotionalActivity",
224174
224544
  message: (0, index_js_1.t)("envSetup.noPromotionalActivity"),
224175
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp"
224545
+ helpUrl: exports.TCB_PURCHASE_URL
224176
224546
  };
224177
224547
  return {
224178
224548
  success: false,
@@ -224238,7 +224608,7 @@ async function checkAndCreateFreeEnv(cloudbase, context) {
224238
224608
  newContext.createEnvError = {
224239
224609
  code: "InvalidEnvId",
224240
224610
  message: (0, index_js_1.t)("envSetup.invalidEnvId"),
224241
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp"
224611
+ helpUrl: exports.TCB_PURCHASE_URL
224242
224612
  };
224243
224613
  return {
224244
224614
  success: false,
@@ -224299,7 +224669,7 @@ async function checkAndCreateFreeEnv(cloudbase, context) {
224299
224669
  newContext.createEnvError = {
224300
224670
  code: createErr.code || createErr.Code || "UnknownError",
224301
224671
  message: errorMessage,
224302
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp"
224672
+ helpUrl: exports.TCB_PURCHASE_URL
224303
224673
  };
224304
224674
  (0, logger_js_1.debug)('[env-setup] CreateFreeEnvByActivity failed:', {
224305
224675
  code: newContext.createEnvError.code,
@@ -224399,12 +224769,14 @@ const cloudbase_manager_js_1 = __webpack_require__(33109);
224399
224769
  const logger_js_1 = __webpack_require__(40781);
224400
224770
  const site_map_js_1 = __webpack_require__(34533);
224401
224771
  const project_config_js_1 = __webpack_require__(39175);
224772
+ const describe_envs_query_js_1 = __webpack_require__(31284);
224402
224773
  const index_js_1 = __webpack_require__(24908);
224403
224774
  const tool_result_js_1 = __webpack_require__(69645);
224404
224775
  const gateway_access_urls_js_1 = __webpack_require__(84173);
224405
224776
  const env_setup_js_1 = __webpack_require__(27528);
224406
224777
  const env_credits_payload_js_1 = __webpack_require__(63880);
224407
224778
  const mcp_ui_js_1 = __webpack_require__(55880);
224779
+ const env_setup_js_2 = __webpack_require__(27528);
224408
224780
  /**
224409
224781
  * Resources accepted by CreateEnv. Matches Cloud API / Manager SDK contract.
224410
224782
  * `postgresql` enables CloudBase PostgreSQL (PG mode) when supported by the package.
@@ -225193,7 +225565,7 @@ async function prepareAuthEnvironment(params) {
225193
225565
  reason: "tcb_init_failed",
225194
225566
  errorCode: "TCB_INIT_FAILED",
225195
225567
  message: (0, index_js_1.t)("env.prepare.tcbInitFailed"),
225196
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp",
225568
+ helpUrl: env_setup_js_2.TCB_PURCHASE_URL,
225197
225569
  });
225198
225570
  return {
225199
225571
  currentEnvId: null,
@@ -225246,7 +225618,7 @@ async function prepareAuthEnvironment(params) {
225246
225618
  reason: "env_creation_failed",
225247
225619
  errorCode: "ENV_CREATION_FAILED",
225248
225620
  message: (0, index_js_1.t)("env.prepare.envCreateFailed"),
225249
- helpUrl: "https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp",
225621
+ helpUrl: env_setup_js_2.TCB_PURCHASE_URL,
225250
225622
  });
225251
225623
  const failureMessage = userNotice
225252
225624
  ? `${userNotice}\n${createFailure.message}`
@@ -226345,42 +226717,68 @@ function registerEnvTools(server) {
226345
226717
  // API Key mode: exchange for temporary credentials
226346
226718
  const apiKeyFromEnv = (0, auth_js_1.getCloudBaseApiKeyFromEnv)();
226347
226719
  if (apiKeyFromEnv && process.env.CLOUDBASE_ENV_ID) {
226720
+ // 项目级 key 与账号登录并存时,项目级优先——设备码/网页授权即使走完也不生效,
226721
+ // 所以短路段必须说清「怎么切」,否则用户会以为换号成功
226722
+ const removable = (0, auth_js_1.isEnvCredentialRemovableByLogout)();
226723
+ const apiKeyEnvId = process.env.CLOUDBASE_ENV_ID;
226724
+ const switchHint = "\n" +
226725
+ (removable
226726
+ ? (0, index_js_1.t)("env.auth.apiKeySwitchHintRemovable", undefined, outLang)
226727
+ : (0, index_js_1.t)("env.auth.apiKeySwitchHintExternal", {
226728
+ varName: process.env.CLOUDBASE_API_KEY
226729
+ ? "CLOUDBASE_API_KEY"
226730
+ : "CLOUDBASE_APIKEY",
226731
+ }, outLang));
226732
+ const logoutNextStep = removable
226733
+ ? (0, tool_result_js_1.buildAuthNextStep)("logout", {
226734
+ suggestedArgs: { action: "logout", confirm: "yes" },
226735
+ })
226736
+ : undefined;
226737
+ // peek 发现回填的那份已失效时会把 env 清掉:那就不是「登录失败」,
226738
+ // 而是「旧 key 已经作废」,直接往下走正常登录流程
226739
+ let adoptedKeyDropped = false;
226348
226740
  try {
226349
226741
  const existingLoginState = await (0, auth_js_1.peekLoginState)();
226350
226742
  if (existingLoginState) {
226351
226743
  return (0, tool_result_js_1.buildJsonToolResult)({
226352
226744
  ok: true,
226353
226745
  code: "AUTH_READY",
226354
- message: await appendApiKeyCamWarningIfNeeded(existingLoginState, (0, index_js_1.t)("env.auth.apiKeyAutoLogin", undefined, outLang)),
226746
+ message: (await appendApiKeyCamWarningIfNeeded(existingLoginState, (0, index_js_1.t)("env.auth.apiKeyAutoLogin", undefined, outLang))) + switchHint,
226355
226747
  auth_mode: "api_key",
226356
- envId: process.env.CLOUDBASE_ENV_ID,
226748
+ envId: apiKeyEnvId,
226749
+ ...(logoutNextStep ? { next_step: logoutNextStep } : {}),
226357
226750
  });
226358
226751
  }
226752
+ adoptedKeyDropped = !(0, auth_js_1.getCloudBaseApiKeyFromEnv)();
226359
226753
  }
226360
226754
  catch (e) {
226361
226755
  // peekLoginState threw; log and fall through to diagnostic
226362
226756
  (0, logger_js_1.debug)("start_auth: peekLoginState threw", { error: e instanceof Error ? e.message : String(e) });
226363
226757
  }
226364
- // API Key exchange failed: return diagnostic details
226365
- const exchangeRegion = (0, site_map_js_1.resolveApiKeyExchangeRegion)();
226366
- const endpoint = process.env.CLOUDBASE_API_ENDPOINT ||
226367
- `https://${process.env.CLOUDBASE_ENV_ID}.${exchangeRegion ?? "ap-shanghai"}.tcb-api.tencentcloudapi.com`;
226368
- const diagMessage = (0, index_js_1.t)("env.auth.apiKeyEnvExchangeFailed", undefined, outLang) +
226369
- (0, index_js_1.t)("env.auth.apiKeyDiagEnv", {
226370
- envId: String(process.env.CLOUDBASE_ENV_ID),
226371
- apiKeyPrefix: apiKeyFromEnv.slice(0, 20),
226372
- site: process.env.TCB_SITE || (0, index_js_1.t)("env.auth.siteUnset", undefined, outLang),
226373
- gatewayRegion: exchangeRegion ?? (0, index_js_1.t)("env.auth.gatewayRegionDefault", undefined, outLang),
226758
+ if (!adoptedKeyDropped) {
226759
+ // API Key exchange failed: return diagnostic details
226760
+ const exchangeRegion = (0, site_map_js_1.resolveApiKeyExchangeRegion)();
226761
+ const endpoint = process.env.CLOUDBASE_API_ENDPOINT ||
226762
+ `https://${apiKeyEnvId}.${exchangeRegion ?? "ap-shanghai"}.tcb-api.tencentcloudapi.com`;
226763
+ const diagMessage = (0, index_js_1.t)("env.auth.apiKeyEnvExchangeFailed", undefined, outLang) +
226764
+ (0, index_js_1.t)("env.auth.apiKeyDiagEnv", {
226765
+ envId: String(apiKeyEnvId),
226766
+ apiKeyPrefix: apiKeyFromEnv.slice(0, 20),
226767
+ site: process.env.TCB_SITE || (0, index_js_1.t)("env.auth.siteUnset", undefined, outLang),
226768
+ gatewayRegion: exchangeRegion ?? (0, index_js_1.t)("env.auth.gatewayRegionDefault", undefined, outLang),
226769
+ endpoint,
226770
+ }, outLang) +
226771
+ switchHint;
226772
+ return (0, tool_result_js_1.buildJsonToolResult)({
226773
+ ok: false,
226774
+ code: "API_KEY_AUTH_FAILED",
226775
+ message: diagMessage,
226776
+ auth_mode: "api_key",
226777
+ envId: apiKeyEnvId,
226374
226778
  endpoint,
226375
- }, outLang);
226376
- return (0, tool_result_js_1.buildJsonToolResult)({
226377
- ok: false,
226378
- code: "API_KEY_AUTH_FAILED",
226379
- message: diagMessage,
226380
- auth_mode: "api_key",
226381
- envId: process.env.CLOUDBASE_ENV_ID,
226382
- endpoint,
226383
- });
226779
+ ...(logoutNextStep ? { next_step: logoutNextStep } : {}),
226780
+ });
226781
+ }
226384
226782
  }
226385
226783
  const region = server.cloudBaseOptions?.region || process.env.TCB_REGION;
226386
226784
  const auth = toolbox_1.AuthSupervisor.getInstance({});
@@ -226709,12 +227107,19 @@ function registerEnvTools(server) {
226709
227107
  });
226710
227108
  }
226711
227109
  if (action === "logout") {
226712
- // Disallow logout in API Key mode
226713
- if ((0, auth_js_1.getCloudBaseApiKeyFromEnv)() && process.env.CLOUDBASE_ENV_ID) {
227110
+ // 用户自己在 shell / 宿主配置里设的 API Key 变量,进程内删不掉,只能提示去源头改;
227111
+ // 按项目保存的那一份(我们回填的,或 login_by_api_key 写下的)能清,走正常登出。
227112
+ if ((0, auth_js_1.getCloudBaseApiKeyFromEnv)() &&
227113
+ process.env.CLOUDBASE_ENV_ID &&
227114
+ !(0, auth_js_1.isEnvCredentialRemovableByLogout)()) {
226714
227115
  return (0, tool_result_js_1.buildJsonToolResult)({
226715
227116
  ok: false,
226716
227117
  code: "LOGOUT_NOT_ALLOWED",
226717
- message: (0, index_js_1.t)("env.auth.logoutNotAllowedApiKey", undefined, outLang),
227118
+ message: (0, index_js_1.t)("env.auth.logoutNotAllowedApiKey", {
227119
+ varName: process.env.CLOUDBASE_API_KEY
227120
+ ? "CLOUDBASE_API_KEY"
227121
+ : "CLOUDBASE_APIKEY",
227122
+ }, outLang),
226718
227123
  auth_mode: "api_key",
226719
227124
  });
226720
227125
  }
@@ -226728,12 +227133,15 @@ function registerEnvTools(server) {
226728
227133
  }),
226729
227134
  });
226730
227135
  }
226731
- await (0, auth_js_1.logout)();
227136
+ const { removed } = await (0, auth_js_1.logout)();
226732
227137
  (0, cloudbase_manager_js_1.resetCloudBaseManagerCache)();
226733
227138
  return (0, tool_result_js_1.buildJsonToolResult)({
226734
227139
  ok: true,
226735
227140
  code: "LOGGED_OUT",
226736
- message: (0, index_js_1.t)("env.auth.loggedOut", undefined, outLang),
227141
+ removed_scopes: removed,
227142
+ message: removed.includes("project")
227143
+ ? (0, index_js_1.t)("env.auth.loggedOutProject", undefined, outLang)
227144
+ : (0, index_js_1.t)("env.auth.loggedOut", undefined, outLang),
226737
227145
  });
226738
227146
  }
226739
227147
  if (action === "get_temp_credentials") {
@@ -226890,11 +227298,8 @@ function registerEnvTools(server) {
226890
227298
  // Filter parameters match the reference conditions provided by user
226891
227299
  result = await cloudbaseList.commonService("tcb", "2018-06-08").call({
226892
227300
  Action: "DescribeEnvs",
226893
- Param: {
226894
- EnvTypes: ["weda", "baas"], // Include weda and baas (normal) environments
226895
- IsVisible: false, // Filter out invisible environments
226896
- Channels: ["dcloud", "iotenable", "tem", "scene_module"], // Filter special channels
226897
- },
227301
+ // 过滤口径与 cloudbase-manager 共用一份,别在这里另写白名单
227302
+ Param: (0, describe_envs_query_js_1.buildDescribeEnvsFilter)(),
226898
227303
  });
226899
227304
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
226900
227305
  // Transform response format to match original listEnvs() format
@@ -229512,8 +229917,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
229512
229917
  return (mod && mod.__esModule) ? mod : { "default": mod };
229513
229918
  };
229514
229919
  Object.defineProperty(exports, "__esModule", ({ value: true }));
229515
- 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;
229920
+ 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;
229516
229921
  exports.formatRuntimeList = formatRuntimeList;
229922
+ exports.formatClsTime = formatClsTime;
229923
+ exports.buildFunctionLogClsQuery = buildFunctionLogClsQuery;
229924
+ exports.resolveFunctionLogWindow = resolveFunctionLogWindow;
229925
+ exports.parseFunctionLogItem = parseFunctionLogItem;
229926
+ exports.isLogServiceNotEnabledMessage = isLogServiceNotEnabledMessage;
229517
229927
  exports.layerNameIncludesEnvId = layerNameIncludesEnvId;
229518
229928
  exports.buildCreateLayerNameWarning = buildCreateLayerNameWarning;
229519
229929
  exports.maskFunctionDetailEnvValues = maskFunctionDetailEnvValues;
@@ -229677,6 +230087,125 @@ exports.MANAGE_FUNCTION_ACTIONS = [
229677
230087
  ];
229678
230088
  /** Default SCF traffic alias used by CloudBase CLI `fn get-route` / `fn config-route`. */
229679
230089
  exports.DEFAULT_FUNCTION_ALIAS = "$DEFAULT";
230090
+ /**
230091
+ * 云函数日志的 CLS 检索底座。
230092
+ *
230093
+ * 底层 `GetFunctionLogs` / `GetFunctionLogDetail` 已下线(`@cloudbase/manager-node` 5.8.8 起
230094
+ * `getFunctionLogsV2` / `getFunctionLogDetail` 直接抛错),云函数日志只能从 CLS 取。CLS 里每条
230095
+ * 云函数日志是按请求聚合的 JSON,`function_name` / `request_id` / `qualifier` / `src` 都是扁平
230096
+ * 字段,可以直接做检索条件,不需要再扫全量日志。
230097
+ */
230098
+ const FUNCTION_LOG_CLS_SOURCE = "(src:app OR src:system)";
230099
+ /** SearchClsLog 单次返回上限。 */
230100
+ const FUNCTION_LOG_MAX_LIMIT = 100;
230101
+ const FUNCTION_LOG_DEFAULT_LIMIT = 20;
230102
+ const FUNCTION_LOG_WINDOW_MS = 24 * 60 * 60 * 1000;
230103
+ /**
230104
+ * CLS 的时间入参固定按北京时间理解(与返回的 `Timestamp` 一致),
230105
+ * 托管形态的服务进程可能是 UTC,所以这里显式加偏移,不依赖进程时区。
230106
+ */
230107
+ const CLS_TIME_OFFSET_MS = 8 * 60 * 60 * 1000;
230108
+ /** CLS 检索语法里 `key:"value"` 的值需要转义反斜杠与双引号。 */
230109
+ function escapeClsValue(value) {
230110
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
230111
+ }
230112
+ /** CLS 入参/展示用的时间格式:YYYY-MM-DD HH:mm:ss(北京时间)。 */
230113
+ function formatClsTime(time) {
230114
+ return new Date(time + CLS_TIME_OFFSET_MS).toISOString().slice(0, 19).replace("T", " ");
230115
+ }
230116
+ function buildFunctionLogClsQuery(input) {
230117
+ const clauses = [FUNCTION_LOG_CLS_SOURCE];
230118
+ if (input.functionName) {
230119
+ clauses.push(`function_name:"${escapeClsValue(input.functionName)}"`);
230120
+ }
230121
+ if (input.qualifier) {
230122
+ clauses.push(`qualifier:"${escapeClsValue(input.qualifier)}"`);
230123
+ }
230124
+ if (input.requestId) {
230125
+ clauses.push(`request_id:"${escapeClsValue(input.requestId)}"`);
230126
+ }
230127
+ return clauses.join(" AND ");
230128
+ }
230129
+ /** 只传一端时把窗口收敛到 24 小时以内,CLS 与旧接口都限制单次跨度。 */
230130
+ function resolveFunctionLogWindow(startTime, endTime, now = Date.now()) {
230131
+ if (startTime && endTime) {
230132
+ return { StartTime: startTime, EndTime: endTime };
230133
+ }
230134
+ if (startTime) {
230135
+ const start = new Date(startTime).getTime();
230136
+ // 单一 startTime 时至少给出一分钟窗口:起止相同(或 start 在未来)时 CLS 会直接判非法。
230137
+ const end = Number.isFinite(start)
230138
+ ? Math.max(start + 60_000, Math.min(now, start + FUNCTION_LOG_WINDOW_MS))
230139
+ : now;
230140
+ return { StartTime: startTime, EndTime: formatClsTime(end) };
230141
+ }
230142
+ if (endTime) {
230143
+ const end = new Date(endTime).getTime();
230144
+ const start = Number.isFinite(end) ? end - FUNCTION_LOG_WINDOW_MS : now - FUNCTION_LOG_WINDOW_MS;
230145
+ return { StartTime: formatClsTime(start), EndTime: endTime };
230146
+ }
230147
+ return { StartTime: formatClsTime(now - FUNCTION_LOG_WINDOW_MS), EndTime: formatClsTime(now) };
230148
+ }
230149
+ /**
230150
+ * 把 CLS 的日志条目还原成调用方关心的字段。
230151
+ *
230152
+ * 解析失败时(例如用户自己打的纯文本日志)保留原文放进 `log`,不让一条脏数据毁掉整页结果。
230153
+ */
230154
+ function parseFunctionLogItem(item) {
230155
+ const content = typeof item.Content === "string" ? item.Content : "";
230156
+ let parsed = null;
230157
+ try {
230158
+ const value = JSON.parse(content);
230159
+ if (value && typeof value === "object" && !Array.isArray(value)) {
230160
+ parsed = value;
230161
+ }
230162
+ }
230163
+ catch {
230164
+ parsed = null;
230165
+ }
230166
+ const timestamp = item.Timestamp ?? "";
230167
+ if (!parsed) {
230168
+ return {
230169
+ requestId: "",
230170
+ functionName: "",
230171
+ qualifier: "",
230172
+ source: "",
230173
+ log: content,
230174
+ statusCode: "",
230175
+ retCode: "",
230176
+ durationMs: null,
230177
+ memoryMb: null,
230178
+ memUsageBytes: null,
230179
+ requestSource: "",
230180
+ timestamp,
230181
+ };
230182
+ }
230183
+ const str = (value) => value === undefined || value === null ? "" : String(value);
230184
+ const num = (value) => {
230185
+ const parsedNumber = Number(value);
230186
+ return Number.isFinite(parsedNumber) ? parsedNumber : null;
230187
+ };
230188
+ return {
230189
+ requestId: str(parsed.request_id),
230190
+ functionName: str(parsed.function_name),
230191
+ qualifier: str(parsed.qualifier || parsed.alias),
230192
+ source: str(parsed.src),
230193
+ log: str(parsed.log || parsed.tcb_log),
230194
+ statusCode: str(parsed.status_code),
230195
+ retCode: str(parsed.ret_code),
230196
+ durationMs: num(parsed.duration),
230197
+ memoryMb: num(parsed.memory),
230198
+ memUsageBytes: num(parsed.mem_usage),
230199
+ requestSource: str(parsed.request_source),
230200
+ timestamp,
230201
+ };
230202
+ }
230203
+ exports.FUNCTION_LOG_QUERY_ERROR_CODE = "FUNCTION_LOG_QUERY_FAILED";
230204
+ exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE = "LOG_SERVICE_NOT_ENABLED";
230205
+ /** 未开通日志服务时 CLS 报的是 topic 不存在,按这个特征把它翻译成可执行的下一步。 */
230206
+ function isLogServiceNotEnabledMessage(message) {
230207
+ return /TopicNotExist|TopicIsolated|topic[\s\S]{0,20}not\s*exist|日志服务未开通/i.test(message);
230208
+ }
229680
230209
  /** Layer soft-warn copy — account-scoped SCF LayerName guidance (no hard fail). */
229681
230210
  exports.LAYER_SOFT_WARN = {
229682
230211
  createNameFormat: (envId) => (0, index_js_1.t)("functions.layerWarn.createNameFormat", { envId }),
@@ -230144,6 +230673,81 @@ function registerFunctionTools(server) {
230144
230673
  }
230145
230674
  }
230146
230675
  };
230676
+ /**
230677
+ * 云函数日志统一走 CLS。查询失败时分两类给出可执行的下一步:
230678
+ * 日志服务没开通 ⇒ 引导开通;其它失败 ⇒ 引导到 queryLogs 用同一条检索语句重试。
230679
+ */
230680
+ const throwLogQueryError = (error, scope) => {
230681
+ const message = error instanceof Error ? error.message : String(error);
230682
+ if (isLogServiceNotEnabledMessage(message)) {
230683
+ (0, tool_result_js_1.throwToolPayloadError)({
230684
+ success: false,
230685
+ errorCode: exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE,
230686
+ data: { ...scope },
230687
+ message: (0, index_js_1.t)("functions.logs.serviceNotEnabled"),
230688
+ nextActions: [
230689
+ {
230690
+ tool: "manageLogs",
230691
+ action: "createLogService",
230692
+ reason: (0, index_js_1.t)("functions.reason.createLogService"),
230693
+ suggested_args: { action: "createLogService", confirm: true },
230694
+ },
230695
+ ],
230696
+ });
230697
+ }
230698
+ (0, tool_result_js_1.throwToolPayloadError)({
230699
+ success: false,
230700
+ errorCode: exports.FUNCTION_LOG_QUERY_ERROR_CODE,
230701
+ data: { ...scope, rawError: message },
230702
+ message: (0, index_js_1.t)("functions.logs.queryFailed", { message }),
230703
+ nextActions: [
230704
+ {
230705
+ tool: "queryLogs",
230706
+ action: "searchLogs",
230707
+ reason: (0, index_js_1.t)("functions.reason.searchClsFallback"),
230708
+ suggested_args: {
230709
+ action: "searchLogs",
230710
+ service: "tcb",
230711
+ queryString: buildFunctionLogClsQuery({
230712
+ functionName: scope.functionName,
230713
+ requestId: scope.requestId,
230714
+ }),
230715
+ startTime: scope.window.StartTime,
230716
+ endTime: scope.window.EndTime,
230717
+ },
230718
+ },
230719
+ ],
230720
+ });
230721
+ };
230722
+ /**
230723
+ * 空结果不等于「函数没被调用」:日志服务没开通时 CLS 同样返回空。
230724
+ * 这里只在真的查到空结果时多做一次开销很小的探活,避免把「没开通」误报成「没日志」。
230725
+ */
230726
+ const assertLogServiceEnabled = async (logService) => {
230727
+ try {
230728
+ if (await logService.checkLogServiceEnabled()) {
230729
+ return;
230730
+ }
230731
+ }
230732
+ catch {
230733
+ // 探活失败不制造新的错误源:保持「该时间段没有日志」的原始结论。
230734
+ return;
230735
+ }
230736
+ (0, tool_result_js_1.throwToolPayloadError)({
230737
+ success: false,
230738
+ errorCode: exports.FUNCTION_LOG_SERVICE_NOT_ENABLED_ERROR_CODE,
230739
+ data: { enabled: false },
230740
+ message: (0, index_js_1.t)("functions.logs.serviceNotEnabled"),
230741
+ nextActions: [
230742
+ {
230743
+ tool: "manageLogs",
230744
+ action: "createLogService",
230745
+ reason: (0, index_js_1.t)("functions.reason.createLogService"),
230746
+ suggested_args: { action: "createLogService", confirm: true },
230747
+ },
230748
+ ],
230749
+ });
230750
+ };
230147
230751
  const normalizeManageLayers = (layers) => normalizeFunctionLayers((layers ?? []).map((layer) => ({
230148
230752
  LayerName: layer.layerName ?? layer.LayerName,
230149
230753
  LayerVersion: layer.layerVersion ?? layer.LayerVersion,
@@ -230265,39 +230869,86 @@ function registerFunctionTools(server) {
230265
230869
  }
230266
230870
  validateLogRange(input.startTime, input.endTime, input.offset, input.limit);
230267
230871
  const cloudbase = await getManager();
230268
- let result;
230872
+ const window = resolveFunctionLogWindow(input.startTime, input.endTime);
230873
+ const limit = Math.min(Math.max(input.limit ?? FUNCTION_LOG_DEFAULT_LIMIT, 1), FUNCTION_LOG_MAX_LIMIT);
230874
+ const offset = Math.max(input.offset ?? 0, 0);
230875
+ // SearchClsLog 单次最多 100 条且没有 offset 入参:不带游标时多取 offset+limit 再本地
230876
+ // 切片,带游标(context)时游标本身已经记录了偏移,直接返回当页。
230877
+ const fetchLimit = Math.min(offset + limit, FUNCTION_LOG_MAX_LIMIT);
230878
+ const queryString = buildFunctionLogClsQuery({
230879
+ functionName: input.functionName,
230880
+ requestId: input.requestId,
230881
+ qualifier: input.qualifier,
230882
+ });
230883
+ let logResults = {};
230269
230884
  try {
230270
- result = await cloudbase.functions.getFunctionLogsV2({
230271
- name: input.functionName,
230272
- offset: input.offset,
230273
- limit: input.limit,
230274
- startTime: input.startTime,
230275
- endTime: input.endTime,
230276
- requestId: input.requestId,
230277
- qualifier: input.qualifier,
230885
+ const result = await cloudbase.log.searchClsLog({
230886
+ queryString,
230887
+ StartTime: window.StartTime,
230888
+ EndTime: window.EndTime,
230889
+ Limit: fetchLimit,
230890
+ Context: input.context,
230891
+ Sort: "desc",
230892
+ service: "tcb",
230278
230893
  });
230894
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230895
+ logResults = result.LogResults ?? {};
230279
230896
  }
230280
230897
  catch (error) {
230281
- const errMsg = error instanceof Error ? error.message : String(error);
230282
- if (/invalid parameter/i.test(errMsg)) {
230283
- throw new Error(`${errMsg}\n\n${(0, index_js_1.t)("functions.logsInvalidParamTips")}`);
230284
- }
230285
- throw error;
230898
+ throwLogQueryError(error, { functionName: input.functionName, window });
230286
230899
  }
230287
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230900
+ const entries = (logResults.Results ?? []).map(parseFunctionLogItem);
230901
+ const logs = input.context ? entries : entries.slice(offset, offset + limit);
230902
+ if (logs.length === 0) {
230903
+ await assertLogServiceEnabled(cloudbase.log);
230904
+ }
230905
+ const hasMore = !logResults.ListOver && Boolean(logResults.Context);
230288
230906
  return buildEnvelope({
230289
230907
  action: input.action,
230290
230908
  functionName: input.functionName,
230291
- logs: result.LogList || [],
230292
- requestId: result.RequestId,
230293
- raw: result,
230294
- }, (0, index_js_1.t)("functions.gotFunctionLogs", { fnName: input.functionName }), [
230295
- {
230296
- tool: "queryFunctions",
230297
- action: "getFunctionLogDetail",
230298
- reason: (0, index_js_1.t)("functions.reason.logDetail"),
230299
- },
230300
- ]);
230909
+ service: "tcb",
230910
+ queryString,
230911
+ startTime: window.StartTime,
230912
+ endTime: window.EndTime,
230913
+ logs,
230914
+ count: logs.length,
230915
+ context: logResults.Context ?? null,
230916
+ hasMore,
230917
+ }, logs.length > 0
230918
+ ? (0, index_js_1.t)("functions.gotFunctionLogs", { fnName: input.functionName })
230919
+ : (0, index_js_1.t)("functions.logs.emptyByFunction", {
230920
+ fnName: input.functionName,
230921
+ start: window.StartTime,
230922
+ end: window.EndTime,
230923
+ }), hasMore
230924
+ ? [
230925
+ {
230926
+ tool: "queryFunctions",
230927
+ action: "listFunctionLogs",
230928
+ reason: (0, index_js_1.t)("functions.reason.nextLogPage"),
230929
+ suggested_args: {
230930
+ action: "listFunctionLogs",
230931
+ functionName: input.functionName,
230932
+ context: logResults.Context,
230933
+ startTime: window.StartTime,
230934
+ endTime: window.EndTime,
230935
+ limit,
230936
+ },
230937
+ },
230938
+ ]
230939
+ : [
230940
+ {
230941
+ tool: "queryFunctions",
230942
+ action: "getFunctionLogDetail",
230943
+ reason: (0, index_js_1.t)("functions.reason.logDetail"),
230944
+ suggested_args: {
230945
+ action: "getFunctionLogDetail",
230946
+ requestId: logs[0]?.requestId || "<日志里的 requestId>",
230947
+ startTime: window.StartTime,
230948
+ endTime: window.EndTime,
230949
+ },
230950
+ },
230951
+ ]);
230301
230952
  }
230302
230953
  case "getFunctionLogDetail": {
230303
230954
  if (!input.requestId) {
@@ -230305,27 +230956,67 @@ function registerFunctionTools(server) {
230305
230956
  }
230306
230957
  validateLogRange(input.startTime, input.endTime);
230307
230958
  const cloudbase = await getManager();
230308
- let result;
230959
+ const window = resolveFunctionLogWindow(input.startTime, input.endTime);
230960
+ const queryString = buildFunctionLogClsQuery({
230961
+ functionName: input.functionName,
230962
+ requestId: input.requestId,
230963
+ qualifier: input.qualifier,
230964
+ });
230965
+ let logResults = {};
230309
230966
  try {
230310
- result = await cloudbase.functions.getFunctionLogDetail({
230311
- startTime: input.startTime,
230312
- endTime: input.endTime,
230313
- logRequestId: input.requestId,
230967
+ const result = await cloudbase.log.searchClsLog({
230968
+ queryString,
230969
+ StartTime: window.StartTime,
230970
+ EndTime: window.EndTime,
230971
+ Limit: FUNCTION_LOG_MAX_LIMIT,
230972
+ Sort: "asc",
230973
+ service: "tcb",
230314
230974
  });
230975
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230976
+ logResults = result.LogResults ?? {};
230315
230977
  }
230316
230978
  catch (error) {
230317
- const errMsg = error instanceof Error ? error.message : String(error);
230318
- if (/invalid parameter/i.test(errMsg)) {
230319
- throw new Error(`${errMsg}\n\n${(0, index_js_1.t)("functions.logDetailInvalidParamTips")}`);
230320
- }
230321
- throw error;
230979
+ throwLogQueryError(error, { requestId: input.requestId, window });
230980
+ }
230981
+ // 一次调用在 CLS 里是多条记录(START / 业务日志 / REPORT),按时间升序拼回完整过程。
230982
+ const entries = (logResults.Results ?? []).map(parseFunctionLogItem);
230983
+ if (entries.length === 0) {
230984
+ await assertLogServiceEnabled(cloudbase.log);
230985
+ return buildEnvelope({
230986
+ action: input.action,
230987
+ requestId: input.requestId,
230988
+ queryString,
230989
+ startTime: window.StartTime,
230990
+ endTime: window.EndTime,
230991
+ logs: [],
230992
+ count: 0,
230993
+ }, (0, index_js_1.t)("functions.logs.emptyByRequest", {
230994
+ requestId: input.requestId,
230995
+ start: window.StartTime,
230996
+ end: window.EndTime,
230997
+ }), [
230998
+ {
230999
+ tool: "queryFunctions",
231000
+ action: "listFunctionLogs",
231001
+ reason: (0, index_js_1.t)("functions.reason.widenLogRange"),
231002
+ suggested_args: {
231003
+ action: "listFunctionLogs",
231004
+ functionName: input.functionName || "<函数名>",
231005
+ startTime: window.StartTime,
231006
+ endTime: window.EndTime,
231007
+ },
231008
+ },
231009
+ ]);
230322
231010
  }
230323
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230324
231011
  return buildEnvelope({
230325
231012
  action: input.action,
230326
231013
  requestId: input.requestId,
230327
- logDetail: result,
230328
- raw: result,
231014
+ functionName: entries[0]?.functionName || input.functionName || null,
231015
+ queryString,
231016
+ startTime: window.StartTime,
231017
+ endTime: window.EndTime,
231018
+ logs: entries,
231019
+ count: entries.length,
230329
231020
  }, (0, index_js_1.t)("functions.gotLogDetail", { requestId: input.requestId }));
230330
231021
  }
230331
231022
  case "listFunctionLayers": {
@@ -231701,6 +232392,7 @@ function registerFunctionTools(server) {
231701
232392
  .optional()
231702
232393
  .describe("functions.schema.query.requestId"),
231703
232394
  qualifier: zod_1.z.string().optional().describe("functions.schema.query.qualifier"),
232395
+ context: zod_1.z.string().optional().describe("functions.schema.query.context"),
231704
232396
  runtime: zod_1.z.string().optional().describe("functions.schema.query.runtime"),
231705
232397
  searchKey: zod_1.z.string().optional().describe("functions.schema.query.searchKey"),
231706
232398
  layerName: zod_1.z
@@ -231827,6 +232519,7 @@ const QUERY_GATEWAY_ACTIONS = [
231827
232519
  "getRoute",
231828
232520
  "listCustomDomains",
231829
232521
  "getPrivilege",
232522
+ "cachePurgeTask",
231830
232523
  ];
231831
232524
  const MANAGE_GATEWAY_ACTIONS = [
231832
232525
  "createRoute",
@@ -231838,6 +232531,7 @@ const MANAGE_GATEWAY_ACTIONS = [
231838
232531
  "deleteCustomDomain",
231839
232532
  "enableService",
231840
232533
  "authSwitch",
232534
+ "purgeCache",
231841
232535
  ];
231842
232536
  /** DomainType of the environment default HTTP service (gateway) domain. */
231843
232537
  const HTTP_SERVICE_DOMAIN_TYPE = "HTTPSERVICE";
@@ -231847,6 +232541,24 @@ const UPSTREAM_RESOURCE_TYPES = [
231847
232541
  "CBR",
231848
232542
  "STATIC_STORE",
231849
232543
  "LH",
232544
+ "STORAGE",
232545
+ ];
232546
+ /** Domain-level protocol accepted by HTTPServiceDomainParam. */
232547
+ const DOMAIN_PROTOCOLS = [
232548
+ "HTTP",
232549
+ "HTTPS",
232550
+ "HTTP_AND_HTTPS",
232551
+ "HTTP_TO_HTTPS",
232552
+ "HTTPS_TO_HTTP",
232553
+ ];
232554
+ /** Cache layer that owns the purged objects (CDN vs EdgeOne). */
232555
+ const CACHE_TYPES = ["CDN", "EO"];
232556
+ const PURGE_TYPES = ["PURGE_URL", "PURGE_PREFIX", "PURGE_HOST"];
232557
+ const HEADER_ADD_ACTIONS = [
232558
+ "APPEND_IF_EXISTS_OR_ADD",
232559
+ "ADD_IF_ABSENT",
232560
+ "OVERWRITE_IF_EXISTS_OR_ADD",
232561
+ "OVERWRITE_IF_EXISTS",
231850
232562
  ];
231851
232563
  function normalizeAccessPath(path) {
231852
232564
  if (!path) {
@@ -231854,6 +232566,225 @@ function normalizeAccessPath(path) {
231854
232566
  }
231855
232567
  return path.startsWith("/") ? path : `/${path}`;
231856
232568
  }
232569
+ // ---------------------------------------------------------------------------
232570
+ // Route/Domain extension schemas (aligned with manager-node HTTPServiceRouteParam)
232571
+ // ---------------------------------------------------------------------------
232572
+ const headerToAddSchema = zod_1.z.object({
232573
+ key: zod_1.z.string().describe("gateway.schema.manage.extension.header.key"),
232574
+ value: zod_1.z.string().describe("gateway.schema.manage.extension.header.value"),
232575
+ action: zod_1.z
232576
+ .enum(HEADER_ADD_ACTIONS)
232577
+ .optional()
232578
+ .describe("gateway.schema.manage.extension.header.action"),
232579
+ });
232580
+ const headersHandlerSchema = zod_1.z.object({
232581
+ requestHeadersToAdd: zod_1.z.array(headerToAddSchema).optional(),
232582
+ requestHeadersToRemove: zod_1.z.array(zod_1.z.string()).optional(),
232583
+ responseHeadersToAdd: zod_1.z.array(headerToAddSchema).optional(),
232584
+ responseHeadersToRemove: zod_1.z.array(zod_1.z.string()).optional(),
232585
+ });
232586
+ const cacheRuleSchema = zod_1.z.object({
232587
+ description: zod_1.z.string().optional(),
232588
+ enable: zod_1.z.boolean().optional(),
232589
+ condition: zod_1.z
232590
+ .object({
232591
+ target: zod_1.z.enum(["url_path", "file_extension", "full_uri"]).optional(),
232592
+ matchType: zod_1.z.enum(["prefix", "suffix", "contains", "exact"]).optional(),
232593
+ values: zod_1.z.array(zod_1.z.string()).optional(),
232594
+ })
232595
+ .describe("gateway.schema.manage.extension.cacheCondition"),
232596
+ actions: zod_1.z
232597
+ .array(zod_1.z.object({
232598
+ type: zod_1.z.enum(["Cache", "CacheKey"]).optional(),
232599
+ cache: zod_1.z
232600
+ .object({
232601
+ followOrigin: zod_1.z.boolean().optional(),
232602
+ noCache: zod_1.z.boolean().optional(),
232603
+ cacheTime: zod_1.z.number().int().min(0).optional(),
232604
+ maxAgeTime: zod_1.z.number().int().min(0).optional(),
232605
+ })
232606
+ .optional(),
232607
+ cacheKey: zod_1.z
232608
+ .object({
232609
+ fullUrlCache: zod_1.z.enum(["on", "off"]).optional(),
232610
+ queryStringSwitch: zod_1.z.enum(["on", "off"]).optional(),
232611
+ queryStringAction: zod_1.z.enum(["includeCustom", "excludeCustom"]).optional(),
232612
+ queryStringValues: zod_1.z.array(zod_1.z.string()).optional(),
232613
+ })
232614
+ .optional(),
232615
+ }))
232616
+ .describe("gateway.schema.manage.extension.cacheActions"),
232617
+ });
232618
+ const extensionSchema = zod_1.z.object({
232619
+ headersHandler: headersHandlerSchema.optional(),
232620
+ cache: zod_1.z.object({ rules: zod_1.z.array(cacheRuleSchema).optional() }).optional(),
232621
+ });
232622
+ const pathRewriteSchema = zod_1.z.object({
232623
+ prefix: zod_1.z.string().optional(),
232624
+ staticStorePrefix: zod_1.z.string().optional(),
232625
+ });
232626
+ const qpsPolicySchema = zod_1.z.object({
232627
+ total: zod_1.z.number().int().min(0).optional(),
232628
+ perClient: zod_1.z
232629
+ .object({
232630
+ limitBy: zod_1.z.enum(["ClientIP", "UserID"]).optional(),
232631
+ limitValue: zod_1.z.number().int().min(1).optional(),
232632
+ })
232633
+ .optional(),
232634
+ });
232635
+ function toHeadersHandler(input) {
232636
+ if (!input) {
232637
+ return undefined;
232638
+ }
232639
+ const output = {};
232640
+ const toHeader = (item) => item.action
232641
+ ? { Key: item.key, Value: item.value, Action: item.action }
232642
+ : { Key: item.key, Value: item.value };
232643
+ if (input.requestHeadersToAdd?.length) {
232644
+ output.RequestHeadersToAdd = input.requestHeadersToAdd.map(toHeader);
232645
+ }
232646
+ if (input.requestHeadersToRemove?.length) {
232647
+ output.RequestHeadersToRemove = input.requestHeadersToRemove;
232648
+ }
232649
+ if (input.responseHeadersToAdd?.length) {
232650
+ output.ResponseHeadersToAdd = input.responseHeadersToAdd.map(toHeader);
232651
+ }
232652
+ if (input.responseHeadersToRemove?.length) {
232653
+ output.ResponseHeadersToRemove = input.responseHeadersToRemove;
232654
+ }
232655
+ return Object.keys(output).length ? output : undefined;
232656
+ }
232657
+ function toCacheSet(input) {
232658
+ if (!input?.rules?.length) {
232659
+ return undefined;
232660
+ }
232661
+ return {
232662
+ Rules: input.rules.map((rule) => ({
232663
+ ...(rule.description !== undefined ? { Description: rule.description } : {}),
232664
+ ...(rule.enable !== undefined ? { Enable: rule.enable } : {}),
232665
+ Condition: {
232666
+ ...(rule.condition?.target ? { Target: rule.condition.target } : {}),
232667
+ ...(rule.condition?.matchType ? { MatchType: rule.condition.matchType } : {}),
232668
+ ...(rule.condition?.values ? { Values: rule.condition.values } : {}),
232669
+ },
232670
+ Actions: (rule.actions ?? []).map((action) => ({
232671
+ ...(action.type ? { Type: action.type } : {}),
232672
+ ...(action.cache
232673
+ ? {
232674
+ Cache: {
232675
+ ...(action.cache.followOrigin !== undefined
232676
+ ? { FollowOrigin: action.cache.followOrigin }
232677
+ : {}),
232678
+ ...(action.cache.noCache !== undefined
232679
+ ? { NoCache: action.cache.noCache }
232680
+ : {}),
232681
+ ...(action.cache.cacheTime !== undefined
232682
+ ? { CacheTime: action.cache.cacheTime }
232683
+ : {}),
232684
+ ...(action.cache.maxAgeTime !== undefined
232685
+ ? { MaxAgeTime: action.cache.maxAgeTime }
232686
+ : {}),
232687
+ },
232688
+ }
232689
+ : {}),
232690
+ ...(action.cacheKey
232691
+ ? {
232692
+ CacheKey: {
232693
+ ...(action.cacheKey.fullUrlCache
232694
+ ? { FullURLCache: action.cacheKey.fullUrlCache }
232695
+ : {}),
232696
+ ...(action.cacheKey.queryStringSwitch
232697
+ ? { QueryStringSwitch: action.cacheKey.queryStringSwitch }
232698
+ : {}),
232699
+ ...(action.cacheKey.queryStringAction
232700
+ ? { QueryStringAction: action.cacheKey.queryStringAction }
232701
+ : {}),
232702
+ ...(action.cacheKey.queryStringValues
232703
+ ? { QueryStringValues: action.cacheKey.queryStringValues }
232704
+ : {}),
232705
+ },
232706
+ }
232707
+ : {}),
232708
+ })),
232709
+ })),
232710
+ };
232711
+ }
232712
+ function toExtension(input) {
232713
+ if (!input) {
232714
+ return undefined;
232715
+ }
232716
+ const output = {};
232717
+ const headersHandler = toHeadersHandler(input.headersHandler);
232718
+ if (headersHandler) {
232719
+ output.HeadersHandler = headersHandler;
232720
+ }
232721
+ const cache = toCacheSet(input.cache);
232722
+ if (cache) {
232723
+ output.Cache = cache;
232724
+ }
232725
+ return Object.keys(output).length ? output : undefined;
232726
+ }
232727
+ function toPathRewrite(input) {
232728
+ if (!input) {
232729
+ return undefined;
232730
+ }
232731
+ const output = {};
232732
+ if (typeof input.prefix === "string") {
232733
+ output.Prefix = input.prefix;
232734
+ }
232735
+ if (typeof input.staticStorePrefix === "string") {
232736
+ output.StaticStorePrefix = input.staticStorePrefix;
232737
+ }
232738
+ return Object.keys(output).length ? output : undefined;
232739
+ }
232740
+ function toQpsPolicy(input) {
232741
+ if (!input) {
232742
+ return undefined;
232743
+ }
232744
+ const output = {};
232745
+ if (typeof input.total === "number") {
232746
+ output.QPSTotal = input.total;
232747
+ }
232748
+ if (input.perClient) {
232749
+ const perClient = {};
232750
+ if (input.perClient.limitBy) {
232751
+ perClient.LimitBy = input.perClient.limitBy;
232752
+ }
232753
+ if (typeof input.perClient.limitValue === "number") {
232754
+ perClient.LimitValue = input.perClient.limitValue;
232755
+ }
232756
+ if (Object.keys(perClient).length) {
232757
+ output.QPSPerClient = perClient;
232758
+ }
232759
+ }
232760
+ return Object.keys(output).length ? output : undefined;
232761
+ }
232762
+ /**
232763
+ * Route fields that ModifyHTTPServiceRoute replaces wholesale. When an update
232764
+ * omits one of them, the existing value must be carried over — otherwise a
232765
+ * partial update (e.g. only bumping QPS) would silently wipe CORS / rewrite /
232766
+ * headers / cache rules.
232767
+ */
232768
+ const PRESERVED_ROUTE_FIELDS = [
232769
+ "EnableAuth",
232770
+ "EnableSafeDomain",
232771
+ "EnablePathTransmission",
232772
+ "PathRewrite",
232773
+ "QPSPolicy",
232774
+ "Extension",
232775
+ "Enable",
232776
+ ];
232777
+ function carryOverRouteFields(route, existing) {
232778
+ if (!existing) {
232779
+ return route;
232780
+ }
232781
+ for (const field of PRESERVED_ROUTE_FIELDS) {
232782
+ if (route[field] === undefined && existing[field] !== undefined) {
232783
+ route[field] = existing[field];
232784
+ }
232785
+ }
232786
+ return route;
232787
+ }
231857
232788
  /**
231858
232789
  * Collect FAIL items from VerifyHTTPServiceRoute, mapping DNS records to
231859
232790
  * lowercase field names for MCP structured output (aligned with CLI 3.8.1).
@@ -232296,16 +233227,13 @@ function registerGatewayTools(server) {
232296
233227
  });
232297
233228
  const path = normalizeAccessPath(input.route?.path ?? input.path ?? `/${upstreamResourceName}`);
232298
233229
  const domain = await resolveRouteDomain(input.domain);
232299
- const enableAuth = input.route?.auth !== undefined
232300
- ? input.route.auth
232301
- : input.auth !== undefined
232302
- ? input.auth
232303
- : undefined;
232304
- const enablePathTransmission = input.route?.enablePathTransmission !== undefined
232305
- ? input.route.enablePathTransmission
232306
- : input.enablePathTransmission !== undefined
232307
- ? input.enablePathTransmission
232308
- : undefined;
233230
+ const pick = (routeValue, topValue) => routeValue !== undefined ? routeValue : topValue;
233231
+ const enableAuth = pick(input.route?.auth, input.auth);
233232
+ const enableSafeDomain = pick(input.route?.enableSafeDomain, input.enableSafeDomain);
233233
+ const enablePathTransmission = pick(input.route?.enablePathTransmission, input.enablePathTransmission);
233234
+ const routePathRewrite = toPathRewrite(pick(input.route?.pathRewrite, input.pathRewrite));
233235
+ const routeQpsPolicy = toQpsPolicy(pick(input.route?.qpsPolicy, input.qpsPolicy));
233236
+ const routeExtension = toExtension(pick(input.route?.extension, input.extension));
232309
233237
  // Route enable: prefer route.enable. For updateRoute only, top-level
232310
233238
  // enable also maps to Route.Enable (enableService/authSwitch use the
232311
233239
  // same field name but different actions).
@@ -232319,18 +233247,35 @@ function registerGatewayTools(server) {
232319
233247
  Path: path,
232320
233248
  UpstreamResourceType: upstreamResourceType,
232321
233249
  UpstreamResourceName: upstreamResourceName,
232322
- EnableAuth: enableAuth,
232323
233250
  };
233251
+ if (enableAuth !== undefined) {
233252
+ route.EnableAuth = enableAuth;
233253
+ }
233254
+ if (enableSafeDomain !== undefined) {
233255
+ route.EnableSafeDomain = enableSafeDomain;
233256
+ }
232324
233257
  if (enablePathTransmission !== undefined) {
232325
233258
  route.EnablePathTransmission = enablePathTransmission;
232326
233259
  }
233260
+ if (routePathRewrite) {
233261
+ route.PathRewrite = routePathRewrite;
233262
+ }
233263
+ if (routeQpsPolicy) {
233264
+ route.QPSPolicy = routeQpsPolicy;
233265
+ }
233266
+ if (routeExtension) {
233267
+ route.Extension = routeExtension;
233268
+ }
232327
233269
  if (routeEnable !== undefined) {
232328
233270
  route.Enable = routeEnable;
232329
233271
  }
233272
+ const domainExtension = toExtension(input.domainExtension);
232330
233273
  return {
232331
233274
  EnvId: await resolveEnvId(),
232332
233275
  Domain: {
232333
233276
  Domain: domain,
233277
+ ...(input.protocol ? { Protocol: input.protocol } : {}),
233278
+ ...(domainExtension ? { Extension: domainExtension } : {}),
232334
233279
  Routes: [route],
232335
233280
  },
232336
233281
  resolved: {
@@ -232339,6 +233284,7 @@ function registerGatewayTools(server) {
232339
233284
  upstreamResourceType,
232340
233285
  upstreamResourceName,
232341
233286
  enableAuth,
233287
+ enableSafeDomain,
232342
233288
  enablePathTransmission,
232343
233289
  enable: routeEnable,
232344
233290
  },
@@ -232387,6 +233333,57 @@ function registerGatewayTools(server) {
232387
233333
  }
232388
233334
  return matches[0];
232389
233335
  };
233336
+ /**
233337
+ * Locate the live record (domain + route) so partial updates can carry over
233338
+ * fields the caller did not touch.
233339
+ */
233340
+ const findExistingRouteRecord = async (domain, path) => {
233341
+ const result = await listHttpServiceRoutes({ domain });
233342
+ const domainRecord = (result.Domains ?? []).find((item) => item.Domain === domain);
233343
+ const route = (domainRecord?.Routes ?? []).find((item) => normalizeAccessPath(String(item.Path ?? "")) ===
233344
+ path);
233345
+ return {
233346
+ domainRecord,
233347
+ route: route,
233348
+ };
233349
+ };
233350
+ /** CDN vs EO is decided by the domain's access type; default to CDN. */
233351
+ /**
233352
+ * 缓存层由域名的接入方式决定,不能猜。
233353
+ *
233354
+ * - EO / CDN → 对应缓存层
233355
+ * - DIRECT / CUSTOM / NONE → CloudBase 侧没有托管缓存,刷新不适用。
233356
+ * 实测(2026-10-21)对 DIRECT 域发 CDN 刷新,PurgeHTTPServiceCache 会「成功」登记
233357
+ * 一个任务,而 DescribeHTTPServiceCachePurgeTask 随后抛 Third Service Error ——
233358
+ * 即后端对 DIRECT 域缺校验、返回不可读错误。这里提前拦住并说明原因。
233359
+ */
233360
+ const resolveDomainCacheType = async (domain, preferred) => {
233361
+ const findDomain = (result) => (result.Domains ?? []).find((item) => item.Domain === domain);
233362
+ let record = findDomain(await listHttpServiceRoutes({ domain }));
233363
+ if (!record) {
233364
+ // 域名下暂时没有路由时,筛 Domain 可能查不到,退回全量再找一次。
233365
+ record = findDomain(await listHttpServiceRoutes());
233366
+ }
233367
+ if (!record) {
233368
+ throw new Error((0, index_js_1.t)("gateway.cache.domainNotFound", { domain }));
233369
+ }
233370
+ const accessType = String(record.AccessType ?? "");
233371
+ const boundCacheType = accessType === "EO" ? "EO" : accessType === "CDN" ? "CDN" : undefined;
233372
+ if (!boundCacheType) {
233373
+ throw new Error((0, index_js_1.t)("gateway.cache.notManaged", {
233374
+ domain,
233375
+ accessType: accessType || "UNKNOWN",
233376
+ }));
233377
+ }
233378
+ if (preferred && preferred !== boundCacheType) {
233379
+ throw new Error((0, index_js_1.t)("gateway.cache.typeMismatch", {
233380
+ domain,
233381
+ accessType: boundCacheType,
233382
+ requested: preferred,
233383
+ }));
233384
+ }
233385
+ return boundCacheType;
233386
+ };
232390
233387
  const routeMutationNextActions = (targetName) => [
232391
233388
  {
232392
233389
  tool: "queryGateway",
@@ -232499,6 +233496,34 @@ function registerGatewayTools(server) {
232499
233496
  },
232500
233497
  ]);
232501
233498
  }
233499
+ case "cachePurgeTask": {
233500
+ if (!input.domain) {
233501
+ throw new Error((0, index_js_1.t)("gateway.query.cachePurgeTaskDomainRequired"));
233502
+ }
233503
+ const cacheType = await resolveDomainCacheType(input.domain, input.cacheType);
233504
+ const cloudbase = await getManager();
233505
+ const result = await cloudbase.env.describeHttpServiceCachePurgeTask({
233506
+ EnvId: await resolveEnvId(),
233507
+ Domain: input.domain,
233508
+ CacheType: cacheType,
233509
+ ...(input.taskId ? { TaskId: input.taskId } : {}),
233510
+ ...(input.purgeType ? { PurgeType: input.purgeType } : {}),
233511
+ ...(input.startTime ? { StartTime: input.startTime } : {}),
233512
+ ...(input.endTime ? { EndTime: input.endTime } : {}),
233513
+ ...(input.offset !== undefined ? { Offset: input.offset } : {}),
233514
+ ...(input.limit !== undefined ? { Limit: input.limit } : {}),
233515
+ });
233516
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
233517
+ const tasks = Array.isArray(result?.Tasks) ? result.Tasks : [];
233518
+ return buildEnvelope({
233519
+ action: input.action,
233520
+ domain: input.domain,
233521
+ cacheType,
233522
+ tasks,
233523
+ total: result?.TotalCount ?? tasks.length,
233524
+ raw: result,
233525
+ }, (0, index_js_1.t)("gateway.query.cachePurgeTask", { count: tasks.length }));
233526
+ }
232502
233527
  default:
232503
233528
  throw new Error((0, index_js_1.t)("gateway.error.unsupportedAction", { action: input.action }));
232504
233529
  }
@@ -232607,6 +233632,7 @@ function registerGatewayTools(server) {
232607
233632
  upstreamResourceType: payload.resolved.upstreamResourceType,
232608
233633
  upstreamResourceName: payload.resolved.upstreamResourceName,
232609
233634
  auth: payload.resolved.enableAuth ?? null,
233635
+ enableSafeDomain: payload.resolved.enableSafeDomain ?? null,
232610
233636
  enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
232611
233637
  accessUrl: `https://${payload.resolved.domain}${payload.resolved.path}`,
232612
233638
  accessUrls: [`https://${payload.resolved.domain}${payload.resolved.path}`],
@@ -232635,6 +233661,14 @@ function registerGatewayTools(server) {
232635
233661
  case "updateRoute": {
232636
233662
  const cloudbase = await getManager();
232637
233663
  const payload = await normalizeRoutePayload(input);
233664
+ // Preserve untouched route/domain fields from the live record: the
233665
+ // backend replaces the whole route entry, so a partial update would
233666
+ // otherwise wipe CORS / rewrite / QPS / headers / cache settings.
233667
+ const existing = await findExistingRouteRecord(payload.resolved.domain, payload.resolved.path);
233668
+ carryOverRouteFields(payload.Domain.Routes[0], existing.route);
233669
+ if (!payload.Domain.Protocol && existing.domainRecord?.Protocol) {
233670
+ payload.Domain.Protocol = existing.domainRecord.Protocol;
233671
+ }
232638
233672
  const result = await cloudbase.env.modifyHttpServiceRoute({
232639
233673
  EnvId: payload.EnvId,
232640
233674
  Domain: payload.Domain,
@@ -232652,6 +233686,7 @@ function registerGatewayTools(server) {
232652
233686
  upstreamResourceType: payload.resolved.upstreamResourceType,
232653
233687
  upstreamResourceName: payload.resolved.upstreamResourceName,
232654
233688
  auth: payload.resolved.enableAuth ?? null,
233689
+ enableSafeDomain: payload.resolved.enableSafeDomain ?? null,
232655
233690
  enablePathTransmission: payload.resolved.enablePathTransmission ?? null,
232656
233691
  enable: payload.resolved.enable ?? null,
232657
233692
  ...(accessUrl
@@ -232733,6 +233768,11 @@ function registerGatewayTools(server) {
232733
233768
  if (enablePathTransmission !== undefined) {
232734
233769
  route.EnablePathTransmission = enablePathTransmission;
232735
233770
  }
233771
+ if (input.enableSafeDomain !== undefined) {
233772
+ route.EnableSafeDomain = input.enableSafeDomain;
233773
+ }
233774
+ // Toggling Enable must not drop CORS / rewrite / QPS / headers / cache.
233775
+ carryOverRouteFields(route, existing);
232736
233776
  const cloudbase = await getManager();
232737
233777
  const result = await cloudbase.env.modifyHttpServiceRoute({
232738
233778
  EnvId: await resolveEnvId(),
@@ -232827,6 +233867,7 @@ function registerGatewayTools(server) {
232827
233867
  CertId: input.certificateId || "",
232828
233868
  AccessType: accessType,
232829
233869
  Enable: input.enable !== undefined ? input.enable : true,
233870
+ ...(input.protocol ? { Protocol: input.protocol } : {}),
232830
233871
  ...(input.customCname ? { CustomCname: input.customCname } : {}),
232831
233872
  };
232832
233873
  const verifyFailure = await verifyHttpServiceRouteOrFail({
@@ -232848,6 +233889,7 @@ function registerGatewayTools(server) {
232848
233889
  Domain: input.domain,
232849
233890
  CertId: certificateId,
232850
233891
  AccessType: accessType,
233892
+ ...(input.protocol ? { Protocol: input.protocol } : {}),
232851
233893
  ...(input.enable !== undefined
232852
233894
  ? { Enable: input.enable }
232853
233895
  : {}),
@@ -232978,6 +234020,43 @@ function registerGatewayTools(server) {
232978
234020
  },
232979
234021
  ]);
232980
234022
  }
234023
+ case "purgeCache": {
234024
+ if (!input.domain) {
234025
+ throw new Error((0, index_js_1.t)("gateway.purgeCache.domainRequired"));
234026
+ }
234027
+ const targets = (input.targets ?? [])
234028
+ .map((item) => (typeof item === "string" ? item.trim() : ""))
234029
+ .filter(Boolean);
234030
+ if (!targets.length) {
234031
+ throw new Error((0, index_js_1.t)("gateway.purgeCache.targetsRequired"));
234032
+ }
234033
+ const purgeType = input.purgeType ?? "PURGE_URL";
234034
+ const cacheType = await resolveDomainCacheType(input.domain, input.cacheType);
234035
+ const cloudbase = await getManager();
234036
+ const result = await cloudbase.env.purgeHttpServiceCache({
234037
+ EnvId: await resolveEnvId(),
234038
+ Domain: input.domain,
234039
+ CacheType: cacheType,
234040
+ PurgeType: purgeType,
234041
+ Targets: targets,
234042
+ });
234043
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
234044
+ return buildEnvelope({
234045
+ action: input.action,
234046
+ domain: input.domain,
234047
+ cacheType,
234048
+ purgeType,
234049
+ targets,
234050
+ taskId: result?.TaskId ?? null,
234051
+ raw: result,
234052
+ }, (0, index_js_1.t)("gateway.purgeCache.message", { count: targets.length }), [
234053
+ {
234054
+ tool: "queryGateway",
234055
+ action: "cachePurgeTask",
234056
+ reason: (0, index_js_1.t)("gateway.purgeCache.verifyReason"),
234057
+ },
234058
+ ]);
234059
+ }
232981
234060
  default:
232982
234061
  throw new Error((0, index_js_1.t)("gateway.error.unsupportedAction", { action: input.action }));
232983
234062
  }
@@ -232996,6 +234075,19 @@ function registerGatewayTools(server) {
232996
234075
  routeId: zod_1.z.string().optional().describe("gateway.schema.query.routeId"),
232997
234076
  path: zod_1.z.string().optional().describe("gateway.schema.query.path"),
232998
234077
  domain: zod_1.z.string().optional().describe("gateway.schema.query.domain"),
234078
+ cacheType: zod_1.z
234079
+ .enum(CACHE_TYPES)
234080
+ .optional()
234081
+ .describe("gateway.schema.query.cacheType"),
234082
+ taskId: zod_1.z.string().optional().describe("gateway.schema.query.taskId"),
234083
+ purgeType: zod_1.z
234084
+ .enum(PURGE_TYPES)
234085
+ .optional()
234086
+ .describe("gateway.schema.query.purgeType"),
234087
+ startTime: zod_1.z.string().optional().describe("gateway.schema.query.startTime"),
234088
+ endTime: zod_1.z.string().optional().describe("gateway.schema.query.endTime"),
234089
+ offset: zod_1.z.number().int().min(0).optional().describe("gateway.schema.query.offset"),
234090
+ limit: zod_1.z.number().int().min(1).optional().describe("gateway.schema.query.limit"),
232999
234091
  },
233000
234092
  annotations: {
233001
234093
  readOnlyHint: true,
@@ -233025,6 +234117,26 @@ function registerGatewayTools(server) {
233025
234117
  .boolean()
233026
234118
  .optional()
233027
234119
  .describe("gateway.schema.manage.enablePathTransmission"),
234120
+ enableSafeDomain: zod_1.z
234121
+ .boolean()
234122
+ .optional()
234123
+ .describe("gateway.schema.manage.enableSafeDomain"),
234124
+ pathRewrite: pathRewriteSchema
234125
+ .optional()
234126
+ .describe("gateway.schema.manage.pathRewrite"),
234127
+ qpsPolicy: qpsPolicySchema
234128
+ .optional()
234129
+ .describe("gateway.schema.manage.qpsPolicy"),
234130
+ extension: extensionSchema
234131
+ .optional()
234132
+ .describe("gateway.schema.manage.extension"),
234133
+ domainExtension: extensionSchema
234134
+ .optional()
234135
+ .describe("gateway.schema.manage.domainExtension"),
234136
+ protocol: zod_1.z
234137
+ .enum(DOMAIN_PROTOCOLS)
234138
+ .optional()
234139
+ .describe("gateway.schema.manage.protocol"),
233028
234140
  route: zod_1.z
233029
234141
  .object({
233030
234142
  path: zod_1.z.string().optional(),
@@ -233041,6 +234153,19 @@ function registerGatewayTools(server) {
233041
234153
  .boolean()
233042
234154
  .optional()
233043
234155
  .describe("gateway.schema.manage.route.enablePathTransmission"),
234156
+ enableSafeDomain: zod_1.z
234157
+ .boolean()
234158
+ .optional()
234159
+ .describe("gateway.schema.manage.route.enableSafeDomain"),
234160
+ pathRewrite: pathRewriteSchema
234161
+ .optional()
234162
+ .describe("gateway.schema.manage.route.pathRewrite"),
234163
+ qpsPolicy: qpsPolicySchema
234164
+ .optional()
234165
+ .describe("gateway.schema.manage.route.qpsPolicy"),
234166
+ extension: extensionSchema
234167
+ .optional()
234168
+ .describe("gateway.schema.manage.route.extension"),
233044
234169
  enable: zod_1.z
233045
234170
  .boolean()
233046
234171
  .optional()
@@ -233062,6 +234187,18 @@ function registerGatewayTools(server) {
233062
234187
  .optional()
233063
234188
  .describe("gateway.schema.manage.customCname"),
233064
234189
  enable: zod_1.z.boolean().optional().describe("gateway.schema.manage.enable"),
234190
+ cacheType: zod_1.z
234191
+ .enum(CACHE_TYPES)
234192
+ .optional()
234193
+ .describe("gateway.schema.manage.cacheType"),
234194
+ purgeType: zod_1.z
234195
+ .enum(PURGE_TYPES)
234196
+ .optional()
234197
+ .describe("gateway.schema.manage.purgeType"),
234198
+ targets: zod_1.z
234199
+ .array(zod_1.z.string())
234200
+ .optional()
234201
+ .describe("gateway.schema.manage.targets"),
233065
234202
  },
233066
234203
  annotations: {
233067
234204
  readOnlyHint: false,
@@ -239455,7 +240592,7 @@ exports.readSkipList = readSkipList;
239455
240592
  exports.isSkipped = isSkipped;
239456
240593
  exports.readAutoApprovePolicy = readAutoApprovePolicy;
239457
240594
  exports.guardDestructiveAction = guardDestructiveAction;
239458
- const cloudbase_manager_js_1 = __webpack_require__(33109);
240595
+ const env_binding_js_1 = __webpack_require__(61498);
239459
240596
  const index_js_1 = __webpack_require__(24908);
239460
240597
  const elicitation_js_1 = __webpack_require__(65048);
239461
240598
  const destructive_actions_js_1 = __webpack_require__(72827);
@@ -239524,16 +240661,7 @@ function readAutoApprovePolicy() {
239524
240661
  return "allow";
239525
240662
  }
239526
240663
  function resolveEnvId(server) {
239527
- const fromOptions = server.cloudBaseOptions?.envId;
239528
- if (typeof fromOptions === "string" && fromOptions.length > 0) {
239529
- return fromOptions;
239530
- }
239531
- try {
239532
- return (0, cloudbase_manager_js_1.getCachedEnvId)() ?? (0, index_js_1.t)("confirmation.message.unknownEnv");
239533
- }
239534
- catch {
239535
- return (0, index_js_1.t)("confirmation.message.unknownEnv");
239536
- }
240664
+ return (0, env_binding_js_1.readDisplayedEnvId)(server.cloudBaseOptions?.envId) ?? (0, index_js_1.t)("confirmation.message.unknownEnv");
239537
240665
  }
239538
240666
  function blockedText(status, actionKey, targetInfo, params) {
239539
240667
  const keyByStatus = {
@@ -239901,6 +241029,47 @@ async function guardCredentialWrite(server, params) {
239901
241029
  }
239902
241030
 
239903
241031
 
241032
+ /***/ },
241033
+
241034
+ /***/ 31284
241035
+ (__unused_webpack_module, exports) {
241036
+
241037
+ "use strict";
241038
+
241039
+ /**
241040
+ * 账号级 DescribeEnvs 的查询口径 —— 唯一真源。
241041
+ *
241042
+ * EnvTypes 是白名单语义(不为空时以该字段为准):少写一个值不会报错,只会静默
241043
+ * 少一类环境。2026-10 的客户案例就是这样:平台侧代建的环境 EnvType=platform,
241044
+ * 控制台能列、MCP 拉不到,用户看到的是「请先创建环境」,现场没有任何错误可看。
241045
+ *
241046
+ * - baas / weda:标准云开发、微搭,MCP 工具主要面向这两类
241047
+ * - platform:平台侧代建的环境(site-* 一类),控制台列这类
241048
+ *
241049
+ * 未纳入 hosting / run(云托管)、tcbgw、paas:MCP 历史上就没列过,加进来会让
241050
+ * 用户在环境候选里看到一堆用不了的托管环境。授权页那份白名单多一个 hosting,
241051
+ * 是历史原因,两边差异是有意的。
241052
+ */
241053
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
241054
+ exports.DESCRIBE_ENVS_EXCLUDED_CHANNELS = exports.DESCRIBE_ENVS_ENV_TYPES = void 0;
241055
+ exports.buildDescribeEnvsFilter = buildDescribeEnvsFilter;
241056
+ /** 允许出现在环境候选列表里的 EnvType */
241057
+ exports.DESCRIBE_ENVS_ENV_TYPES = ["baas", "weda", "platform"];
241058
+ /**
241059
+ * IsVisible=false 表示 Channels 是「排除列表」:
241060
+ * 这 4 个渠道创建的环境不参与环境候选,其余渠道(含控制台的 qc_console)正常返回。
241061
+ */
241062
+ exports.DESCRIBE_ENVS_EXCLUDED_CHANNELS = ["dcloud", "iotenable", "tem", "scene_module"];
241063
+ /** 生成 DescribeEnvs 的 Param 过滤段;每次返回新数组,避免调用方改到共享常量 */
241064
+ function buildDescribeEnvsFilter() {
241065
+ return {
241066
+ EnvTypes: [...exports.DESCRIBE_ENVS_ENV_TYPES],
241067
+ IsVisible: false,
241068
+ Channels: [...exports.DESCRIBE_ENVS_EXCLUDED_CHANNELS],
241069
+ };
241070
+ }
241071
+
241072
+
239904
241073
  /***/ },
239905
241074
 
239906
241075
  /***/ 72827
@@ -240251,6 +241420,213 @@ function resolveDestructiveAction(toolName, args) {
240251
241420
  }
240252
241421
 
240253
241422
 
241423
+ /***/ },
241424
+
241425
+ /***/ 36953
241426
+ (__unused_webpack_module, exports, __webpack_require__) {
241427
+
241428
+ "use strict";
241429
+
241430
+ var __importDefault = (this && this.__importDefault) || function (mod) {
241431
+ return (mod && mod.__esModule) ? mod : { "default": mod };
241432
+ };
241433
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
241434
+ exports.readSystemAnchor = readSystemAnchor;
241435
+ exports.physicalMacAddresses = physicalMacAddresses;
241436
+ exports.collectHostSignal = collectHostSignal;
241437
+ exports.computeDeviceId = computeDeviceId;
241438
+ const crypto_1 = __importDefault(__webpack_require__(55511));
241439
+ const child_process_1 = __webpack_require__(79646);
241440
+ const fs_1 = __importDefault(__webpack_require__(29021));
241441
+ const os_1 = __importDefault(__webpack_require__(21820));
241442
+ /**
241443
+ * 匿名设备指纹。
241444
+ *
241445
+ * 为什么要有这个文件:旧实现是 `sha256(hostname | CPU model | 全部非内部网卡 MAC)`,
241446
+ * 锚点选错了 —— macOS 开着「私有 Wi-Fi 地址」时每个 SSID 一个随机 MAC,`awdl0` / `llw0` /
241447
+ * `bridge0` 的 MAC 随系统状态漂移,容器里 hostname 和 MAC 每次重建都变。结果是同一台 Mac
241448
+ * 一天能上报成几十个「设备」(2026-10-26 实测:Mac 指纹 82% 只在那一天出现过,
241449
+ * Windows 同口径 22%)。
241450
+ *
241451
+ * 现在的取值顺序:
241452
+ * 1. 系统级不可变标识 —— macOS `IOPlatformUUID`、Windows `MachineGuid`、Linux `/etc/machine-id`;
241453
+ * 2. 取不到时退到 `hostname + CPU model + 过滤后的物理网卡 MAC`;
241454
+ * 3. 再取不到只留 `hostname + CPU model`。
241455
+ *
241456
+ * 三条纪律:
241457
+ * - **格式不变**:仍是 SHA256 前 32 位 hex,灯塔 `A2` 字段与后端都不用改。
241458
+ * - **绝不用随机数兜底**。旧实现取不到信息时 `crypto.randomBytes(16)`,等于每次启动都是
241459
+ * 一台新设备 —— 正是要消灭的行为。宁可所有机器共用一个弱指纹,也不要一堆假设备。
241460
+ * - **只在构造时跑一次**。锚点要起子进程(`ioreg` / `reg`),1s 超时就降级,
241461
+ * 绝不放进每次上报的路径。
241462
+ *
241463
+ * 口径变化(换锚点当天就会看到,报表侧要提前打招呼):
241464
+ * - 历史 `A2` 全部失效,老设备会以新指纹重新出现 —— 报表上「装机量跳一下」不是增长。
241465
+ * - 容器内多实例若共享镜像固化的 `/etc/machine-id`,会被合并成同一台设备(以前是各算一个新设备)。
241466
+ */
241467
+ /** 取系统级锚点的子进程超时:超了直接降级,不能拖慢进程启动 */
241468
+ const ANCHOR_TIMEOUT_MS = 1000;
241469
+ /**
241470
+ * 虚拟 / 隧道网卡名前缀(小写比较)。这些接口的 MAC 会随系统状态漂移或每次重建都变,
241471
+ * 拼进指纹就等于给同一台机器发新身份。
241472
+ */
241473
+ const VIRTUAL_NIC_PREFIXES = [
241474
+ 'awdl', // Apple Wireless Direct Link
241475
+ 'llw', // Apple 低延迟 WLAN
241476
+ 'bridge', // macOS / Windows 网桥
241477
+ 'utun', // macOS / iOS VPN 隧道
241478
+ 'tun',
241479
+ 'tap',
241480
+ 'veth', // 容器 veth pair
241481
+ 'docker',
241482
+ 'br-', // docker bridge
241483
+ 'vmnet', // VMware
241484
+ 'vboxnet', // VirtualBox
241485
+ 'vether', // Hyper-V 虚拟交换机(vEthernet)
241486
+ 'zt', // ZeroTier
241487
+ 'tailscale',
241488
+ 'wg', // WireGuard
241489
+ 'lo', // loopback(正常情况下 internal 已过滤,兜一层)
241490
+ ];
241491
+ function sha256Short(input) {
241492
+ return crypto_1.default.createHash('sha256').update(input).digest('hex').substring(0, 32);
241493
+ }
241494
+ /** `machine-id` 合法值:16–64 位 hex,且不能是全 0(有些镜像里就是全 0 的空壳) */
241495
+ function isUsableMachineId(value) {
241496
+ if (!/^[0-9a-f]{16,64}$/i.test(value)) {
241497
+ return false;
241498
+ }
241499
+ return !/^0+$/.test(value);
241500
+ }
241501
+ function readFileQuietly(filePath) {
241502
+ try {
241503
+ return fs_1.default.readFileSync(filePath, 'utf8').trim();
241504
+ }
241505
+ catch {
241506
+ return undefined;
241507
+ }
241508
+ }
241509
+ /** 起一次子进程读锚点。超时 / 不存在 / 权限不足一律返回 undefined,由调用方降级。 */
241510
+ function execQuietly(file, args) {
241511
+ try {
241512
+ return (0, child_process_1.execFileSync)(file, args, {
241513
+ timeout: ANCHOR_TIMEOUT_MS,
241514
+ encoding: 'utf8',
241515
+ // 子进程的 stderr 全部吞掉:`ioreg` 在受限环境会打噪音,`reg` 在 32 位 Node
241516
+ // 下可能报找不到键,这些都不该出现在 MCP 的 stdout(stdio transport 上是协议流)
241517
+ stdio: ['ignore', 'pipe', 'ignore'],
241518
+ windowsHide: true,
241519
+ });
241520
+ }
241521
+ catch {
241522
+ return undefined;
241523
+ }
241524
+ }
241525
+ /**
241526
+ * 读系统级不可变标识。**只读一次**,失败即降级。
241527
+ * `platform` 参数只为测试注入,正常运行不传。
241528
+ */
241529
+ function readSystemAnchor(platform = process.platform) {
241530
+ if (platform === 'darwin') {
241531
+ const out = execQuietly('ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice']);
241532
+ const matched = out && /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out);
241533
+ if (matched?.[1]) {
241534
+ return { value: matched[1].trim(), source: 'platform-uuid' };
241535
+ }
241536
+ return undefined;
241537
+ }
241538
+ if (platform === 'win32') {
241539
+ const out = execQuietly('reg', [
241540
+ 'query',
241541
+ 'HKLM\\SOFTWARE\\Microsoft\\Cryptography',
241542
+ '/v',
241543
+ 'MachineGuid',
241544
+ ]);
241545
+ // 形如:` MachineGuid REG_SZ 3f2a....`(分隔符是多个空格或 tab)
241546
+ const matched = out && /MachineGuid\s+REG_SZ\s+([0-9a-fA-F-]{8,})/.exec(out);
241547
+ if (matched?.[1]) {
241548
+ return { value: matched[1].trim().toLowerCase(), source: 'machine-guid' };
241549
+ }
241550
+ return undefined;
241551
+ }
241552
+ // linux / openharmony / 其他类 Unix:先 /etc/machine-id,再 dbus 那份
241553
+ for (const candidate of ['/etc/machine-id', '/var/lib/dbus/machine-id']) {
241554
+ const raw = readFileQuietly(candidate);
241555
+ if (raw && isUsableMachineId(raw)) {
241556
+ return { value: raw.toLowerCase(), source: 'machine-id' };
241557
+ }
241558
+ }
241559
+ return undefined;
241560
+ }
241561
+ function isVirtualNic(name) {
241562
+ const lower = name.toLowerCase();
241563
+ return VIRTUAL_NIC_PREFIXES.some((prefix) => lower.startsWith(prefix));
241564
+ }
241565
+ /**
241566
+ * 非内部、非虚拟、MAC 不是全 0 / 广播地址的网卡 MAC,去重后排序。
241567
+ * 排序是为了让指纹与网卡枚举顺序无关(枚举顺序在重启后可能变)。
241568
+ */
241569
+ function physicalMacAddresses(interfaces = os_1.default.networkInterfaces()) {
241570
+ const macs = new Set();
241571
+ for (const [name, list] of Object.entries(interfaces)) {
241572
+ if (!list || isVirtualNic(name)) {
241573
+ continue;
241574
+ }
241575
+ for (const nic of list) {
241576
+ const mac = nic?.mac?.toLowerCase();
241577
+ if (!nic || nic.internal || !mac) {
241578
+ continue;
241579
+ }
241580
+ if (mac === '00:00:00:00:00:00' || mac === 'ff:ff:ff:ff:ff:ff') {
241581
+ continue;
241582
+ }
241583
+ macs.add(mac);
241584
+ }
241585
+ }
241586
+ return [...macs].sort();
241587
+ }
241588
+ /** 取不到系统锚点时的兜底信号:**不含虚拟网卡**,主机名与 CPU 型号足够稳。 */
241589
+ function collectHostSignal() {
241590
+ let hostname = 'unknown-host';
241591
+ try {
241592
+ hostname = os_1.default.hostname() || hostname;
241593
+ }
241594
+ catch {
241595
+ // 主机名读不到不影响降级:CPU + MAC 仍可用
241596
+ }
241597
+ let cpu = '';
241598
+ try {
241599
+ cpu = os_1.default
241600
+ .cpus()
241601
+ .map((item) => item.model)
241602
+ .join(',');
241603
+ }
241604
+ catch {
241605
+ // 同上
241606
+ }
241607
+ let macs = [];
241608
+ try {
241609
+ macs = physicalMacAddresses();
241610
+ }
241611
+ catch {
241612
+ // 同上
241613
+ }
241614
+ const parts = [hostname, cpu, ...macs].filter((part) => Boolean(part));
241615
+ return {
241616
+ value: parts.join('|'),
241617
+ source: macs.length > 0 ? 'host-fallback' : 'host-only',
241618
+ };
241619
+ }
241620
+ /**
241621
+ * 计算匿名设备指纹(SHA256 前 32 位 hex),并返回这次用的锚点来源 ——
241622
+ * 来源只进 debug 日志,用于上线后确认「到底有多少机器走到了兜底」。
241623
+ */
241624
+ function computeDeviceId(platform = process.platform) {
241625
+ const anchor = readSystemAnchor(platform) ?? collectHostSignal();
241626
+ return { deviceId: sha256Short(anchor.value), source: anchor.source };
241627
+ }
241628
+
241629
+
240254
241630
  /***/ },
240255
241631
 
240256
241632
  /***/ 65048
@@ -240391,6 +241767,113 @@ async function requestChoiceElicitation(server, params) {
240391
241767
  }
240392
241768
 
240393
241769
 
241770
+ /***/ },
241771
+
241772
+ /***/ 61498
241773
+ (__unused_webpack_module, exports, __webpack_require__) {
241774
+
241775
+ "use strict";
241776
+
241777
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
241778
+ exports.ENV_SOURCES = void 0;
241779
+ exports.noteEnvBinding = noteEnvBinding;
241780
+ exports.clearEnvBinding = clearEnvBinding;
241781
+ exports.hasEnvSlot = hasEnvSlot;
241782
+ exports.readReportedEnv = readReportedEnv;
241783
+ exports.readDisplayedEnvId = readDisplayedEnvId;
241784
+ exports.runWithEnvSlot = runWithEnvSlot;
241785
+ exports.runInEnvSlot = runInEnvSlot;
241786
+ exports.peekLocalEnvBinding = peekLocalEnvBinding;
241787
+ const node_async_hooks_1 = __webpack_require__(18667);
241788
+ const project_config_js_1 = __webpack_require__(39175);
241789
+ /**
241790
+ * Where this resolution got its env id.
241791
+ * The first four values are already in shipped queries. Do not rename them.
241792
+ * `default` stays `default` (an empty result, not a rename to `unresolved`).
241793
+ */
241794
+ exports.ENV_SOURCES = [
241795
+ "cloudBaseOptions",
241796
+ "process.env.CLOUDBASE_ENV_ID",
241797
+ "envManager.cachedEnvId",
241798
+ "project",
241799
+ "login_state",
241800
+ "auto_bound",
241801
+ "default",
241802
+ ];
241803
+ const envCall = new node_async_hooks_1.AsyncLocalStorage();
241804
+ function isReportableEnvId(envId) {
241805
+ return !!envId && envId !== "local" && envId !== "unknown";
241806
+ }
241807
+ /** Write into the current slot. Outside a slot this is a no-op and does not touch process globals. */
241808
+ function noteEnvBinding(binding) {
241809
+ const slot = envCall.getStore();
241810
+ if (!slot || binding.source === "default" || !isReportableEnvId(binding.envId)) {
241811
+ return;
241812
+ }
241813
+ slot.binding = { envId: binding.envId, source: binding.source };
241814
+ }
241815
+ function clearEnvBinding() {
241816
+ const slot = envCall.getStore();
241817
+ if (slot) {
241818
+ slot.binding = null;
241819
+ }
241820
+ }
241821
+ function hasEnvSlot() {
241822
+ return envCall.getStore() !== undefined;
241823
+ }
241824
+ function readReportedEnv() {
241825
+ const binding = envCall.getStore()?.binding;
241826
+ if (!binding || binding.source === "default" || !isReportableEnvId(binding.envId)) {
241827
+ return { envId: "unknown", envIdSource: "default" };
241828
+ }
241829
+ return { envId: binding.envId, envIdSource: binding.source };
241830
+ }
241831
+ /**
241832
+ * Env id shown on the start page, the usage card, and confirmation text.
241833
+ * An explicit envId on this call wins, matching the resolver ladder.
241834
+ * Otherwise the current slot (login state, project, cache, or env var already noted).
241835
+ * Does not read process.env or the manager cache: on a hosted request those belong to the server machine.
241836
+ */
241837
+ function readDisplayedEnvId(explicitEnvId) {
241838
+ if (isReportableEnvId(explicitEnvId)) {
241839
+ return explicitEnvId;
241840
+ }
241841
+ const reported = readReportedEnv();
241842
+ if (reported.envIdSource !== "default") {
241843
+ return reported.envId;
241844
+ }
241845
+ return null;
241846
+ }
241847
+ function runWithEnvSlot(slot, fn) {
241848
+ return envCall.run(slot, fn);
241849
+ }
241850
+ /** One fresh slot. Tests and any caller that must not share another request's binding. */
241851
+ function runInEnvSlot(fn) {
241852
+ return envCall.run({ binding: null }, fn);
241853
+ }
241854
+ /**
241855
+ * Read-only snapshot for process start. Does not call setEnvId and does not list envs.
241856
+ * `cachedEnvId` is passed in so this module does not import the manager (that import cycles).
241857
+ */
241858
+ function peekLocalEnvBinding(input) {
241859
+ if (isReportableEnvId(input.cachedEnvId)) {
241860
+ return { envId: input.cachedEnvId, source: "envManager.cachedEnvId" };
241861
+ }
241862
+ const fromEnv = process.env.CLOUDBASE_ENV_ID;
241863
+ if (isReportableEnvId(fromEnv)) {
241864
+ return { envId: fromEnv, source: "process.env.CLOUDBASE_ENV_ID" };
241865
+ }
241866
+ const projectEnvId = (0, project_config_js_1.readProjectEnvId)();
241867
+ if (isReportableEnvId(projectEnvId)) {
241868
+ return { envId: projectEnvId, source: "project" };
241869
+ }
241870
+ if (isReportableEnvId(input.loginEnvId)) {
241871
+ return { envId: input.loginEnvId, source: "login_state" };
241872
+ }
241873
+ return { envId: null, source: "default" };
241874
+ }
241875
+
241876
+
240394
241877
  /***/ },
240395
241878
 
240396
241879
  /***/ 81194
@@ -242069,6 +243552,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
242069
243552
  };
242070
243553
  Object.defineProperty(exports, "__esModule", ({ value: true }));
242071
243554
  exports.reportToolkitSessionInitialized = exports.reportToolkitLifecycle = exports.reportResourceRead = exports.reportElicitationResult = exports.reportToolCall = exports.MCP_UI_EXTENSION = exports.telemetryReporter = void 0;
243555
+ exports.seedStdioEnvBinding = seedStdioEnvBinding;
242072
243556
  exports.extractMcpClientInfo = extractMcpClientInfo;
242073
243557
  exports.readMcpClientInfoFromServer = readMcpClientInfoFromServer;
242074
243558
  exports.normalizeClientName = normalizeClientName;
@@ -242082,6 +243566,8 @@ const os_1 = __importDefault(__webpack_require__(21820));
242082
243566
  const auth_js_1 = __webpack_require__(561);
242083
243567
  const cloudbase_manager_js_1 = __webpack_require__(33109);
242084
243568
  const cloud_mode_js_1 = __webpack_require__(88694);
243569
+ const env_binding_js_1 = __webpack_require__(61498);
243570
+ const device_id_js_1 = __webpack_require__(36953);
242085
243571
  const logger_js_1 = __webpack_require__(40781);
242086
243572
  const site_map_js_1 = __webpack_require__(34533);
242087
243573
  /**
@@ -242137,7 +243623,7 @@ class TelemetryReporter {
242137
243623
  const arch = os_1.default.arch(); // 系统架构
242138
243624
  // Build-time injected version; guard for vitest / non-webpack runtimes.
242139
243625
  const mcpVersion = process.env.npm_package_version ||
242140
- ( true ? "2.36.0" : 0);
243626
+ ( true ? "2.36.2" : 0);
242141
243627
  return {
242142
243628
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
242143
243629
  deviceId: this.deviceId,
@@ -242150,26 +243636,25 @@ class TelemetryReporter {
242150
243636
  }
242151
243637
  /**
242152
243638
  * 获取设备唯一标识
242153
- * 基于主机名、CPU信息和MAC地址生成匿名设备指纹
243639
+ *
243640
+ * 锚点与降级顺序见 `utils/device-id.ts`:优先系统级不可变标识(IOPlatformUUID /
243641
+ * MachineGuid / machine-id),取不到才退到 hostname + CPU + 过滤后的物理网卡 MAC。
243642
+ * 容器里 hostname 每次重建都变、macOS 私有 Wi-Fi 地址让 MAC 随 SSID 漂移,
243643
+ * 两者都不能当锚点 —— 旧实现把它们全拼进去,同一台 Mac 一天能上报成几十个设备。
243644
+ *
243645
+ * 子进程(ioreg / reg)只在这里跑一次(构造时),1s 超时即降级。
242154
243646
  */
242155
243647
  getDeviceId() {
242156
243648
  try {
242157
- // 获取设备信息组合
242158
- const deviceInfo = [
242159
- os_1.default.hostname(),
242160
- os_1.default.cpus().map((cpu) => cpu.model).join(','),
242161
- Object.values(os_1.default.networkInterfaces())
242162
- .reduce((acc, val) => acc.concat(val || []), [])
242163
- .filter((nic) => nic && !nic.internal && nic.mac)
242164
- .map((nic) => nic.mac)
242165
- .join(',')
242166
- ].join('|');
242167
- // 生成SHA256哈希作为设备ID
242168
- return crypto_1.default.createHash('sha256').update(deviceInfo).digest('hex').substring(0, 32);
243649
+ const { deviceId, source } = (0, device_id_js_1.computeDeviceId)();
243650
+ (0, logger_js_1.debug)('device_id_resolved', { source, deviceId: deviceId.substring(0, 8) + '...' });
243651
+ return deviceId;
242169
243652
  }
242170
243653
  catch (err) {
242171
- // 如果获取设备信息失败,生成随机ID
242172
- return crypto_1.default.randomBytes(16).toString('hex');
243654
+ // 走到这里说明连 hostname / CPU 都读不到:用固定串兜底,**绝不能**用随机数
243655
+ // (随机数等于每次启动都是一台新设备,正是这次要消灭的行为)。
243656
+ (0, logger_js_1.debug)('获取设备标识失败,回落到固定串', err instanceof Error ? err : new Error(String(err)));
243657
+ return crypto_1.default.createHash('sha256').update('cloudbase-mcp|no-device-signal').digest('hex').substring(0, 32);
242173
243658
  }
242174
243659
  }
242175
243660
  /**
@@ -242285,39 +243770,40 @@ class TelemetryReporter {
242285
243770
  // 创建全局实例
242286
243771
  exports.telemetryReporter = new TelemetryReporter();
242287
243772
  /**
242288
- * 解析环境ID
242289
- * 优先级:传入配置 > envManager缓存 > 环境变量 > unknown
243773
+ * Reports read the current AsyncLocalStorage slot.
243774
+ * They do not re-read envManager or CLOUDBASE_ENV_ID.
243775
+ * An explicit envId on this call is only used when the slot is still empty
243776
+ * (hosted initialized, before a resolver runs, when the request already carried one).
243777
+ *
243778
+ * The source list lives in `env-binding.ts` (`ENV_SOURCES`) — one definition only.
242290
243779
  */
242291
243780
  function resolveEnvId(cloudBaseOptions) {
242292
- let envId = 'unknown';
242293
- let envIdSource = 'unknown';
242294
- try {
242295
- if (cloudBaseOptions?.envId) {
242296
- envId = cloudBaseOptions.envId;
242297
- envIdSource = 'cloudBaseOptions';
242298
- }
242299
- else {
242300
- const cachedEnvId = (0, cloudbase_manager_js_1.getCachedEnvId)();
242301
- if (cachedEnvId) {
242302
- envId = cachedEnvId;
242303
- envIdSource = 'envManager.cachedEnvId';
242304
- }
242305
- else if (process.env.CLOUDBASE_ENV_ID) {
242306
- envId = process.env.CLOUDBASE_ENV_ID;
242307
- envIdSource = 'process.env.CLOUDBASE_ENV_ID';
242308
- }
242309
- else {
242310
- envId = 'unknown';
242311
- envIdSource = 'default';
242312
- }
242313
- }
243781
+ const reported = (0, env_binding_js_1.readReportedEnv)();
243782
+ if (reported.envIdSource !== "default") {
243783
+ return reported;
242314
243784
  }
242315
- catch (err) {
242316
- (0, logger_js_1.debug)('获取环境ID失败,遥测数据将使用 unknown', err instanceof Error ? err : new Error(String(err)));
242317
- envId = 'unknown';
242318
- envIdSource = 'error';
243785
+ if (cloudBaseOptions?.envId) {
243786
+ return { envId: cloudBaseOptions.envId, envIdSource: "cloudBaseOptions" };
243787
+ }
243788
+ return { envId: "unknown", envIdSource: "default" };
243789
+ }
243790
+ /**
243791
+ * Stdio start: reuse the login warm (one peekLoginState per process) and one project-file read.
243792
+ * Does not call setEnvId and does not list environments. Cloud mode does not read local files.
243793
+ */
243794
+ async function seedStdioEnvBinding(slot) {
243795
+ if ((0, cloud_mode_js_1.isCloudMode)()) {
243796
+ return;
243797
+ }
243798
+ await warmLocalLoginState();
243799
+ const loginEnvId = typeof localLoginState?.envId === "string" ? localLoginState.envId : null;
243800
+ const binding = (0, env_binding_js_1.peekLocalEnvBinding)({
243801
+ cachedEnvId: (0, cloudbase_manager_js_1.getCachedEnvId)(),
243802
+ loginEnvId,
243803
+ });
243804
+ if (binding.envId && binding.source !== "default") {
243805
+ slot.binding = binding;
242319
243806
  }
242320
- return { envId, envIdSource };
242321
243807
  }
242322
243808
  /**
242323
243809
  * Normalize SDK Implementation / clientInfo into telemetry-safe fields.
@@ -242624,6 +244110,9 @@ const reportToolCall = async (params) => {
242624
244110
  duration: params.duration !== undefined ? String(params.duration) : undefined,
242625
244111
  error: params.error ? params.error.substring(0, 200) : undefined, // 限制错误信息长度
242626
244112
  envId: envId || 'unknown',
244113
+ // 取值来源(cloudBaseOptions / envManager.cachedEnvId / process.env / default):
244114
+ // 这一列就是「envId 为什么是 unknown」的判据,别再靠猜
244115
+ envIdSource,
242627
244116
  region: siteRegion.region,
242628
244117
  site: siteRegion.site,
242629
244118
  login_uin: loginUin,
@@ -242637,6 +244126,13 @@ const reportToolCall = async (params) => {
242637
244126
  if (params.action !== undefined) {
242638
244127
  eventData.action = params.action;
242639
244128
  }
244129
+ // 环境准备结果:拿不到就不占列(与能力字段同一纪律,不写 unknown 假值)
244130
+ if (params.envSetupStatus !== undefined) {
244131
+ eventData.envSetupStatus = params.envSetupStatus;
244132
+ }
244133
+ if (params.envSetupFailReason !== undefined) {
244134
+ eventData.envSetupFailReason = params.envSetupFailReason;
244135
+ }
242640
244136
  // 添加入参信息(如果提供)
242641
244137
  if (params.inputParams !== undefined) {
242642
244138
  try {
@@ -242758,7 +244254,15 @@ const reportToolkitLifecycle = async (params) => {
242758
244254
  // 安全获取环境ID,优先使用传入的配置
242759
244255
  const { envId, envIdSource } = resolveEnvId(params.cloudBaseOptions);
242760
244256
  const siteRegion = resolveTelemetrySiteRegion(params.cloudBaseOptions);
242761
- const loginUin = await resolveLoginUin(params.cloudBaseOptions, { allowResolve: false });
244257
+ // `start` 在进程最早期发出:此时既没有宿主注入的 uin,也还没人 warm 过本地登录态。
244258
+ // 卡死 `allowResolve: false` 就只能读到空缓存 → 恒 'unknown'(2026-10-26 实测:这一列
244259
+ // 有真实 uin 的行是 0)。start 允许解析:`warmLocalLoginState()` 进程内只跑一次
244260
+ // (读本地 auth.json;临时密钥过期时可能续期一次,与工具调用本来就要付的开销同源),
244261
+ // 不会挂到每次上报上。
244262
+ // `exit` 仍保持不允许 —— 退出路径不能等,拿不到就是 unknown,`start` 那次已经把值带出去了。
244263
+ const loginUin = await resolveLoginUin(params.cloudBaseOptions, {
244264
+ allowResolve: params.event !== 'exit',
244265
+ });
242762
244266
  (0, logger_js_1.debug)('[telemetry] 生命周期事件 envId 获取结果', {
242763
244267
  event: params.event,
242764
244268
  envId,
@@ -242773,6 +244277,9 @@ const reportToolkitLifecycle = async (params) => {
242773
244277
  exitCode: params.exitCode !== undefined ? String(params.exitCode) : undefined,
242774
244278
  error: params.error ? params.error.substring(0, 200) : undefined, // 限制错误信息长度
242775
244279
  envId: envId || 'unknown',
244280
+ // 取值来源(cloudBaseOptions / envManager.cachedEnvId / process.env / default):
244281
+ // 这一列就是「envId 为什么是 unknown」的判据,别再靠猜
244282
+ envIdSource,
242776
244283
  region: siteRegion.region,
242777
244284
  site: siteRegion.site,
242778
244285
  login_uin: loginUin,
@@ -242829,12 +244336,15 @@ exports.reportToolkitLifecycle = reportToolkitLifecycle;
242829
244336
  */
242830
244337
  const reportToolkitSessionInitialized = async (params) => {
242831
244338
  const { nodeVersion, osType, osRelease, arch, mcpVersion } = exports.telemetryReporter.getUserAgent();
242832
- const { envId } = resolveEnvId(params.cloudBaseOptions);
244339
+ const { envId, envIdSource } = resolveEnvId(params.cloudBaseOptions);
242833
244340
  const siteRegion = resolveTelemetrySiteRegion(params.cloudBaseOptions);
242834
- const loginUin = await resolveLoginUin(params.cloudBaseOptions, { allowResolve: false });
244341
+ // 与 `start` 同理:`initialized` 紧跟在 `start` 之后,也是不允许等的分支就会白报一条 unknown。
244342
+ // 这里放开解析;若 `start` 已经 warm 过,这一句直接命中进程内缓存,零额外成本。
244343
+ const loginUin = await resolveLoginUin(params.cloudBaseOptions);
242835
244344
  const eventData = {
242836
244345
  event: 'initialized',
242837
244346
  envId: envId || 'unknown',
244347
+ envIdSource,
242838
244348
  region: siteRegion.region,
242839
244349
  site: siteRegion.site,
242840
244350
  login_uin: loginUin,
@@ -243008,7 +244518,6 @@ function throwToolPayloadError(payload) {
243008
244518
  Object.defineProperty(exports, "__esModule", ({ value: true }));
243009
244519
  exports.applyCategoryAnnotationMeta = applyCategoryAnnotationMeta;
243010
244520
  exports.wrapServerWithTelemetry = wrapServerWithTelemetry;
243011
- const cloudbase_manager_js_1 = __webpack_require__(33109);
243012
244521
  const cloud_mode_js_1 = __webpack_require__(88694);
243013
244522
  const logger_js_1 = __webpack_require__(40781);
243014
244523
  const telemetry_js_1 = __webpack_require__(5198);
@@ -243071,8 +244580,56 @@ function contributionHint(server) {
243071
244580
  ].join("\n");
243072
244581
  }
243073
244582
  /**
243074
- * 创建包装后的处理函数,添加数据上报功能
244583
+ * 从 `auth` 的返回里取出「环境准备」的结果,供遥测上报。
244584
+ *
244585
+ * 为什么要从返回体里抠:`env_setup_status` / `env_setup_failure.reason` 只存在于
244586
+ * `auth` 的 payload 里,不是 `reportToolCall` 的入参。而工具 wrapper 是唯一同时
244587
+ * 看得见 args 和 result 的地方。
244588
+ *
244589
+ * 现状(2026-10-27):这两项只回给模型、没进埋点,于是「无环境的用户卡在购买页、
244590
+ * 实名、还是 CAM 授权」在数据里长得一模一样,加文案也无法验收。
243075
244591
  */
244592
+ function readEnvSetupOutcome(result) {
244593
+ const rec = result;
244594
+ if (!rec || typeof rec !== 'object')
244595
+ return undefined;
244596
+ // 优先 structuredContent(MCP 规范位),退到 content[0].text 里的 JSON
244597
+ const candidates = [];
244598
+ if (rec.structuredContent)
244599
+ candidates.push(rec.structuredContent);
244600
+ const content = Array.isArray(rec.content) ? rec.content : [];
244601
+ for (const item of content) {
244602
+ const text = item?.text;
244603
+ if (typeof text === 'string' && text.trim().startsWith('{')) {
244604
+ try {
244605
+ candidates.push(JSON.parse(text));
244606
+ }
244607
+ catch {
244608
+ /* 不是 JSON 就跳过,不要因为解析失败影响正常返回 */
244609
+ }
244610
+ }
244611
+ }
244612
+ for (const payload of candidates) {
244613
+ const p = payload;
244614
+ const status = typeof p?.env_setup_status === 'string' ? p.env_setup_status : undefined;
244615
+ if (!status)
244616
+ continue;
244617
+ const failure = p?.env_setup_failure;
244618
+ const reason = typeof failure?.reason === 'string' ? failure.reason : undefined;
244619
+ // 把两个布尔压进 reason 的后缀:一列就能看清「卡在哪」,
244620
+ // 单看 reason 只知道「建环境失败」,不知道是实名还是 CAM 授权拦住的
244621
+ const gates = [];
244622
+ if (failure?.need_real_name_auth === true)
244623
+ gates.push('realname');
244624
+ if (failure?.need_cam_auth === true)
244625
+ gates.push('cam');
244626
+ return {
244627
+ status,
244628
+ reason: reason ? (gates.length ? `${reason}+${gates.join('+')}` : reason) : undefined,
244629
+ };
244630
+ }
244631
+ return undefined;
244632
+ }
243076
244633
  /**
243077
244634
  * 工具自己返回的失败:结构化 `{ success: false }`(`isBusinessFailureToolResult` 已覆盖
243078
244635
  * 顶层 / structuredContent / JSON 文本三种挂法),**或**已经显式带 `isError: true` 的回执。
@@ -243092,7 +244649,7 @@ function isReturnedFailureResult(result) {
243092
244649
  * 所以两条路径对同一个底层错误得到的文本逐字相同(🆔 RequestId + 📦 版本页脚都在这里)。
243093
244650
  */
243094
244651
  function buildFailureClientText(errorMessage, requestId, server) {
243095
- const mcpVersion = true ? "2.36.0" : 0;
244652
+ const mcpVersion = true ? "2.36.2" : 0;
243096
244653
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
243097
244654
  return `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n${contributionHint(server)}`;
243098
244655
  }
@@ -243152,12 +244709,18 @@ function withEnhancedFailureText(result, errorMessage, requestId, server) {
243152
244709
  }
243153
244710
  return result;
243154
244711
  }
244712
+ /**
244713
+ * 创建包装后的处理函数,添加数据上报功能
244714
+ */
243155
244715
  function createWrappedHandler(name, handler, server) {
243156
244716
  return async (args, extra) => {
243157
244717
  const startTime = Date.now();
243158
244718
  let success = false;
243159
244719
  let errorMessage;
243160
244720
  let requestId;
244721
+ // 提到 try 外:finally 里要读 auth 的环境准备结果(env_setup_status / failure.reason)。
244722
+ // 护栏拦截的早退路径不会赋值,finally 里按 undefined 处理。
244723
+ let result;
243161
244724
  try {
243162
244725
  (0, logger_js_1.debug)(`开始执行工具: ${name}`, { args: sanitizeArgs(args) });
243163
244726
  // In test environment, skip logger to avoid potential blocking
@@ -243189,7 +244752,7 @@ function createWrappedHandler(name, handler, server) {
243189
244752
  return guardDecision.result;
243190
244753
  }
243191
244754
  // 执行原始处理函数(extra 一并透传,工具可据此拿到在途请求 id)
243192
- const result = await handler(args, extra);
244755
+ result = await handler(args, extra);
243193
244756
  // 工具**自己返回**的失败(`{ success: false }` 结构化载荷,或已经带 `isError: true` 的
243194
244757
  // 失败回执)与"抛错"是同一件事:遥测要按失败记,文案要吃同一套收尾(增强 + RequestId +
243195
244758
  // 版本页脚)。护栏拦截的返回走不到这里——它在上面就 return 了,且它的载荷既没有
@@ -243270,24 +244833,19 @@ function createWrappedHandler(name, handler, server) {
243270
244833
  const isTestEnvironment = false || process.env.VITEST === "true";
243271
244834
  if (!isTestEnvironment) {
243272
244835
  const duration = Date.now() - startTime;
243273
- // 如果 server.cloudBaseOptions 为空或没有 envId,尝试从缓存获取并更新
243274
- let cloudBaseOptions = server.cloudBaseOptions;
243275
- if (!cloudBaseOptions?.envId) {
243276
- const cachedEnvId = (0, cloudbase_manager_js_1.getCachedEnvId)();
243277
- if (cachedEnvId) {
243278
- cloudBaseOptions = { ...cloudBaseOptions, envId: cachedEnvId };
243279
- }
243280
- }
244836
+ const envSetup = name === 'auth' ? readEnvSetupOutcome(result) : undefined;
243281
244837
  // 异步上报,不阻塞工具返回
243282
244838
  (0, telemetry_js_1.reportToolCall)({
243283
244839
  toolName: name,
244840
+ envSetupStatus: envSetup?.status,
244841
+ envSetupFailReason: envSetup?.reason,
243284
244842
  success,
243285
244843
  requestId,
243286
244844
  duration,
243287
244845
  error: errorMessage,
243288
244846
  inputParams: sanitizeArgs(args), // 添加入参上报
243289
244847
  action: args?.action, // 工具级 action(如 auth 的 status/set_env)
243290
- cloudBaseOptions: cloudBaseOptions, // 传递 CloudBase 配置(可能已更新)
244848
+ cloudBaseOptions: server.cloudBaseOptions,
243291
244849
  ide: server.ide || process.env.INTEGRATION_IDE, // 传递集成IDE信息
243292
244850
  client: server.client || process.env.CLOUDBASE_MCP_CLIENT, // 传递 MCP client 来源标识
243293
244851
  mcpClientInfo: (0, telemetry_js_1.readMcpClientInfoFromServer)(server),
@@ -263601,6 +265159,14 @@ module.exports = require("net");
263601
265159
 
263602
265160
  /***/ },
263603
265161
 
265162
+ /***/ 18667
265163
+ (module) {
265164
+
265165
+ "use strict";
265166
+ module.exports = require("node:async_hooks");
265167
+
265168
+ /***/ },
265169
+
263604
265170
  /***/ 75305
263605
265171
  (module) {
263606
265172