@kevisual/cli 0.1.12 → 0.1.14

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.
@@ -42679,6 +42679,13 @@ var envKevisualDir = process.env.ASSISTANT_CONFIG_DIR;
42679
42679
  if (envKevisualDir) {
42680
42680
  kevisualDir = envKevisualDir;
42681
42681
  logger.debug("使用环境变量 ASSISTANT_CONFIG_DIR 作为 kevisual 目录:", kevisualDir);
42682
+ } else {
42683
+ if (!kevisualDir) {
42684
+ const isCNB = process.env.CNB_GROUP_SLUG;
42685
+ if (isCNB) {
42686
+ kevisualDir = path.join("/workspace/kevisual");
42687
+ }
42688
+ }
42682
42689
  }
42683
42690
  var HomeConfigDir = path.join(kevisualDir, "assistant-app");
42684
42691
  function parseArgs(args) {
@@ -51957,7 +51964,7 @@ class RemoteApp {
51957
51964
  this.emitter.emit("message", data);
51958
51965
  }
51959
51966
  onError(error49) {
51960
- console.error("远程应用错误:", this.id, error49);
51967
+ console.error(`[remote-app] 远程应用错误: ${this.id}`, error49);
51961
51968
  this.isError = true;
51962
51969
  this.emitter.emit("error", error49);
51963
51970
  }
@@ -52027,7 +52034,7 @@ class RemoteApp {
52027
52034
  }
52028
52035
  }
52029
52036
 
52030
- // ../node_modules/.pnpm/@kevisual+query@0.0.49/node_modules/@kevisual/query/dist/query-browser.js
52037
+ // ../node_modules/.pnpm/@kevisual+query@0.0.52/node_modules/@kevisual/query/dist/query-browser.js
52031
52038
  var isTextForContentType = (contentType) => {
52032
52039
  if (!contentType)
52033
52040
  return false;
@@ -66322,7 +66329,7 @@ function filter(data, query) {
66322
66329
  return executor.execute(ast, data);
66323
66330
  }
66324
66331
 
66325
- // ../node_modules/.pnpm/@kevisual+api@0.0.59_@types+react@19.2.10_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@kevisual/api/query/query-proxy/router-api-proxy.ts
66332
+ // ../node_modules/.pnpm/@kevisual+api@0.0.60_@types+react@19.2.10_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@kevisual/api/query/query-proxy/router-api-proxy.ts
66326
66333
  var initApi = async (opts) => {
66327
66334
  const router = opts?.router;
66328
66335
  const item = opts?.item;
@@ -80963,7 +80970,7 @@ class AssistantApp extends Manager2 {
80963
80970
  app: this.mainApp,
80964
80971
  autoReconnect: true,
80965
80972
  reconnectDelay: 5000,
80966
- maxReconnectAttempts: Infinity,
80973
+ maxReconnectAttempts: 50,
80967
80974
  enableBackoff: true
80968
80975
  });
80969
80976
  remoteApp.isConnect();
@@ -80983,7 +80990,7 @@ class AssistantApp extends Manager2 {
80983
80990
  logger.info("[remote-app] 远程连接已关闭,自动重连机制正在处理...");
80984
80991
  });
80985
80992
  remoteApp.on("maxReconnectAttemptsReached", () => {
80986
- logger.error("远程应用重连达到最大次数,停止重连");
80993
+ logger.error("[remote-app] 远程应用重连达到最大次数,停止重连");
80987
80994
  });
80988
80995
  this.remoteApp = remoteApp;
80989
80996
  } else {
@@ -81149,7 +81156,7 @@ var checkLocalUser = async (opts) => {
81149
81156
  }
81150
81157
  }
81151
81158
  };
81152
- // ../node_modules/.pnpm/@kevisual+query@0.0.49/node_modules/@kevisual/query/dist/query.js
81159
+ // ../node_modules/.pnpm/@kevisual+query@0.0.52/node_modules/@kevisual/query/dist/query.js
81153
81160
  var isTextForContentType2 = (contentType) => {
81154
81161
  if (!contentType)
81155
81162
  return false;
@@ -81437,7 +81444,7 @@ class Query2 {
81437
81444
  }
81438
81445
  }
81439
81446
 
81440
- // ../node_modules/.pnpm/@kevisual+api@0.0.59_@types+react@19.2.10_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@kevisual/api/dist/query-login-node.js
81447
+ // ../node_modules/.pnpm/@kevisual+api@0.0.60_@types+react@19.2.10_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/@kevisual/api/dist/query-login-node.js
81441
81448
  import { homedir as homedir2 } from "node:os";
81442
81449
  import { join, dirname } from "node:path";
81443
81450
  import fs13 from "node:fs";
@@ -81753,6 +81760,15 @@ class BaseQuery {
81753
81760
  return this.query.get(data, options);
81754
81761
  }
81755
81762
  }
81763
+ var defaultCacheData = {
81764
+ loginUsers: [],
81765
+ user: undefined,
81766
+ id: undefined,
81767
+ accessToken: undefined,
81768
+ refreshToken: undefined,
81769
+ accessTokenExpiresIn: undefined,
81770
+ createdAt: undefined
81771
+ };
81756
81772
 
81757
81773
  class LoginCacheStore {
81758
81774
  cache;
@@ -81763,13 +81779,7 @@ class LoginCacheStore {
81763
81779
  throw new Error("cache is required");
81764
81780
  }
81765
81781
  this.cache = opts.cache;
81766
- this.cacheData = {
81767
- loginUsers: [],
81768
- user: undefined,
81769
- id: undefined,
81770
- accessToken: undefined,
81771
- refreshToken: undefined
81772
- };
81782
+ this.cacheData = { ...defaultCacheData };
81773
81783
  this.name = opts.name;
81774
81784
  }
81775
81785
  async setValue(value) {
@@ -81779,27 +81789,13 @@ class LoginCacheStore {
81779
81789
  }
81780
81790
  async delValue() {
81781
81791
  await this.cache.del();
81782
- this.cacheData = {
81783
- loginUsers: [],
81784
- user: undefined,
81785
- id: undefined,
81786
- accessToken: undefined,
81787
- refreshToken: undefined
81788
- };
81792
+ this.cacheData = { ...defaultCacheData };
81789
81793
  }
81790
81794
  getValue() {
81791
81795
  return this.cache.get(this.name);
81792
81796
  }
81793
81797
  async init() {
81794
- const defaultData = {
81795
- loginUsers: [],
81796
- user: undefined,
81797
- id: undefined,
81798
- accessToken: undefined,
81799
- refreshToken: undefined,
81800
- accessTokenExpiresIn: undefined,
81801
- createdAt: undefined
81802
- };
81798
+ const defaultData = { ...this.cacheData };
81803
81799
  if (this.cache.init) {
81804
81800
  try {
81805
81801
  const cacheData = await this.cache.init();
@@ -81812,18 +81808,18 @@ class LoginCacheStore {
81812
81808
  }
81813
81809
  return this.cacheData;
81814
81810
  }
81815
- async setLoginUser(user) {
81816
- const has = this.cacheData.loginUsers.find((u) => u.id === user.id);
81811
+ async setLoginUser(loginUser) {
81812
+ const has = this.cacheData.loginUsers.find((u) => u.id === loginUser.id);
81817
81813
  if (has) {
81818
- this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
81819
- }
81820
- this.cacheData.loginUsers.push(user);
81821
- this.cacheData.user = user.user;
81822
- this.cacheData.id = user.id;
81823
- this.cacheData.accessToken = user.accessToken;
81824
- this.cacheData.refreshToken = user.refreshToken;
81825
- this.cacheData.accessTokenExpiresIn = user.accessTokenExpiresIn;
81826
- this.cacheData.createdAt = user.createdAt;
81814
+ this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== loginUser.id);
81815
+ }
81816
+ this.cacheData.loginUsers.push(loginUser);
81817
+ this.cacheData.user = loginUser.user;
81818
+ this.cacheData.id = loginUser.id;
81819
+ this.cacheData.accessToken = loginUser.accessToken;
81820
+ this.cacheData.refreshToken = loginUser.refreshToken;
81821
+ this.cacheData.accessTokenExpiresIn = loginUser.accessTokenExpiresIn;
81822
+ this.cacheData.createdAt = loginUser.createdAt;
81827
81823
  await this.setValue(this.cacheData);
81828
81824
  }
81829
81825
  getCurrentUser() {
@@ -81859,22 +81855,22 @@ class LoginCacheStore {
81859
81855
  if (has) {
81860
81856
  this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
81861
81857
  }
81862
- this.cacheData.user = undefined;
81863
- this.cacheData.id = undefined;
81864
- this.cacheData.accessToken = undefined;
81865
- this.cacheData.refreshToken = undefined;
81866
- this.cacheData.accessTokenExpiresIn = undefined;
81867
- this.cacheData.createdAt = undefined;
81858
+ const hasOther = this.cacheData.loginUsers.length > 0;
81859
+ const current = this.cacheData.loginUsers[this.cacheData.loginUsers.length - 1];
81860
+ if (hasOther && current) {
81861
+ this.cacheData.user = current.user;
81862
+ this.cacheData.id = current.id;
81863
+ this.cacheData.accessToken = current.accessToken;
81864
+ this.cacheData.refreshToken = current.refreshToken;
81865
+ this.cacheData.accessTokenExpiresIn = current.accessTokenExpiresIn;
81866
+ this.cacheData.createdAt = current.createdAt;
81867
+ } else {
81868
+ this.cacheData = { ...defaultCacheData };
81869
+ }
81868
81870
  await this.setValue(this.cacheData);
81869
81871
  }
81870
81872
  async clearAll() {
81871
- this.cacheData.loginUsers = [];
81872
- this.cacheData.user = undefined;
81873
- this.cacheData.id = undefined;
81874
- this.cacheData.accessToken = undefined;
81875
- this.cacheData.refreshToken = undefined;
81876
- this.cacheData.accessTokenExpiresIn = undefined;
81877
- this.cacheData.createdAt = undefined;
81873
+ this.cacheData = { ...defaultCacheData };
81878
81874
  await this.setValue(this.cacheData);
81879
81875
  }
81880
81876
  }
@@ -82462,7 +82458,7 @@ class QueryLogin extends BaseQuery {
82462
82458
  async queryRefreshToken(opts) {
82463
82459
  const refreshToken = opts?.refreshToken;
82464
82460
  let accessToken = opts?.accessToken;
82465
- const _refreshToken = refreshToken || await this.cacheStore.getRefreshToken();
82461
+ const _refreshToken = refreshToken ?? await this.cacheStore.getRefreshToken();
82466
82462
  let data = {};
82467
82463
  if (accessToken) {
82468
82464
  data.accessToken = accessToken;
@@ -82573,6 +82569,7 @@ class QueryLogin extends BaseQuery {
82573
82569
  }
82574
82570
  const isExpired = await this.cacheStore.getIsExpired();
82575
82571
  if (isExpired) {
82572
+ console.log("token过期,正在刷新token", this.cacheStore.cacheData);
82576
82573
  const res = await this.refreshLoginUser();
82577
82574
  if (res.code === 200) {
82578
82575
  return res.data?.accessToken || null;
@@ -100310,7 +100307,7 @@ app.route({
100310
100307
  }).addTo(app, {
100311
100308
  overwrite: false
100312
100309
  });
100313
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/core/serverSentEvents.gen.js
100310
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/core/serverSentEvents.gen.js
100314
100311
  var createSseClient = ({ onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url: url4, ...options }) => {
100315
100312
  let lastEventId;
100316
100313
  const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
@@ -100419,7 +100416,7 @@ var createSseClient = ({ onSseError, onSseEvent, responseTransformer, responseVa
100419
100416
  return { stream: stream2 };
100420
100417
  };
100421
100418
 
100422
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/core/auth.gen.js
100419
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/core/auth.gen.js
100423
100420
  var getAuthToken = async (auth, callback) => {
100424
100421
  const token = typeof callback === "function" ? await callback(auth) : callback;
100425
100422
  if (!token) {
@@ -100434,12 +100431,12 @@ var getAuthToken = async (auth, callback) => {
100434
100431
  return token;
100435
100432
  };
100436
100433
 
100437
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/core/bodySerializer.gen.js
100434
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/core/bodySerializer.gen.js
100438
100435
  var jsonBodySerializer = {
100439
100436
  bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
100440
100437
  };
100441
100438
 
100442
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/core/pathSerializer.gen.js
100439
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/core/pathSerializer.gen.js
100443
100440
  var separatorArrayExplode = (style) => {
100444
100441
  switch (style) {
100445
100442
  case "label":
@@ -100542,7 +100539,7 @@ var serializeObjectParam = ({ allowReserved, explode, name, style, value, valueO
100542
100539
  return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
100543
100540
  };
100544
100541
 
100545
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/core/utils.gen.js
100542
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/core/utils.gen.js
100546
100543
  var PATH_PARAM_RE = /\{[^{}]+\}/g;
100547
100544
  var defaultPathSerializer = ({ path: path14, url: _url5 }) => {
100548
100545
  let url4 = _url5;
@@ -100610,7 +100607,7 @@ var getUrl = ({ baseUrl, path: path14, query: query2, querySerializer, url: _url
100610
100607
  return url4;
100611
100608
  };
100612
100609
 
100613
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/client/utils.gen.js
100610
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/client/utils.gen.js
100614
100611
  var createQuerySerializer = ({ allowReserved, array: array6, object: object5 } = {}) => {
100615
100612
  const querySerializer = (queryParams) => {
100616
100613
  const search = [];
@@ -100818,7 +100815,7 @@ var createConfig = (override = {}) => ({
100818
100815
  ...override
100819
100816
  });
100820
100817
 
100821
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/client/client.gen.js
100818
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/client/client.gen.js
100822
100819
  var createClient = (config7 = {}) => {
100823
100820
  let _config = mergeConfigs(createConfig(), config7);
100824
100821
  const getConfig2 = () => ({ ..._config });
@@ -100965,7 +100962,7 @@ var createClient = (config7 = {}) => {
100965
100962
  trace: makeMethod("TRACE")
100966
100963
  };
100967
100964
  };
100968
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/core/params.gen.js
100965
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/core/params.gen.js
100969
100966
  var extraPrefixesMap = {
100970
100967
  $body_: "body",
100971
100968
  $headers_: "headers",
@@ -100973,12 +100970,12 @@ var extraPrefixesMap = {
100973
100970
  $query_: "query"
100974
100971
  };
100975
100972
  var extraPrefixes = Object.entries(extraPrefixesMap);
100976
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/client.gen.js
100973
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/client.gen.js
100977
100974
  var client = createClient(createConfig({
100978
100975
  baseUrl: "http://localhost:4096"
100979
100976
  }));
100980
100977
 
100981
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/gen/sdk.gen.js
100978
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/gen/sdk.gen.js
100982
100979
  class _HeyApiClient {
100983
100980
  _client = client;
100984
100981
  constructor(args2) {
@@ -101648,7 +101645,7 @@ class OpencodeClient extends _HeyApiClient {
101648
101645
  event = new Event({ client: this._client });
101649
101646
  }
101650
101647
 
101651
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/client.js
101648
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/client.js
101652
101649
  function createOpencodeClient(config7) {
101653
101650
  if (!config7?.fetch) {
101654
101651
  const customFetch = (req) => {
@@ -101669,7 +101666,7 @@ function createOpencodeClient(config7) {
101669
101666
  const client2 = createClient(config7);
101670
101667
  return new OpencodeClient({ client: client2 });
101671
101668
  }
101672
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/server.js
101669
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/server.js
101673
101670
  import { spawn as spawn3 } from "node:child_process";
101674
101671
  async function createOpencodeServer(options) {
101675
101672
  options = Object.assign({
@@ -101738,7 +101735,7 @@ Server output: ${output}`;
101738
101735
  }
101739
101736
  };
101740
101737
  }
101741
- // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.10/node_modules/@opencode-ai/sdk/dist/index.js
101738
+ // ../node_modules/.pnpm/@opencode-ai+sdk@1.2.14/node_modules/@opencode-ai/sdk/dist/index.js
101742
101739
  async function createOpencode(options) {
101743
101740
  const server2 = await createOpencodeServer({
101744
101741
  ...options
@@ -102197,11 +102194,6 @@ var getLiveMdContent = (opts) => {
102197
102194
  - OpenWebUI: ${openWebUrl}
102198
102195
  - Kevisual: ${kevisualUrl}
102199
102196
 
102200
- ### 直接访问
102201
- - Kevisual: ${kevisualUrl}
102202
- - OpenCode: ${url4?.replace("{{port}}", "4096")}
102203
- - VSCode Web: ${vscodeWebUrl}
102204
-
102205
102197
  ### 密码访问
102206
102198
  - OpenClaw: ${openclawUrlSecret}
102207
102199
  - OpenCode: ${opencodeUrlSecret}
@@ -102213,6 +102205,9 @@ var getLiveMdContent = (opts) => {
102213
102205
 
102214
102206
  使用插件访问vscode web获取wss进行保活,避免长时间不操作导致的自动断开连接。
102215
102207
 
102208
+ 保活说明
102209
+ 方法1: 使用插件访问vscode web获取wss进行保活,避免长时间不操作导致的自动断开连接。
102210
+
102216
102211
  1. 安装插件[CNB LIVE](https://chromewebstore.google.com/detail/cnb-live/iajpiophkcdghonpijkcgpjafbcjhkko?pli=1)
102217
102212
  2. 打开vscode web获取,点击插件,获取json数据,替换keep.json中的数据,保持在线状态。
102218
102213
  3. keep.json中的数据结构说明:
@@ -102221,6 +102216,7 @@ var getLiveMdContent = (opts) => {
102221
102216
  - url: vscode web的访问地址,可以直接访问vscode web
102222
102217
  4. 运行cli命令,ev cnb live -c /workspace/live/keep.json.(直接对话opencode或者openclaw调用cnb-live技能即可)
102223
102218
 
102219
+ 方法2:环境变量设置CNB_COOKIE,直接opencode或者openclaw的ui界面对话说,cnb-keep-live保活,他会自动调用保活,同时不需要点cnb-lie插件获取配置。
102224
102220
  `;
102225
102221
  const labels = [
102226
102222
  {
@@ -102250,7 +102246,7 @@ var getLiveMdContent = (opts) => {
102250
102246
  {
102251
102247
  key: "openclawUrl",
102252
102248
  title: "OpenClaw 地址",
102253
- value: openclawUrl,
102249
+ value: openclawUrl + "/openclaw",
102254
102250
  description: "OpenClaw 的访问地址,可以通过该地址访问 OpenClaw 服务"
102255
102251
  },
102256
102252
  {
@@ -102351,7 +102347,7 @@ var createOSInfo = (more = false) => {
102351
102347
  }, {
102352
102348
  key: "cpuCores",
102353
102349
  title: "CPU 核心数",
102354
- value: `${cpus.length}`,
102350
+ value: cpus.length,
102355
102351
  description: "CPU 核心数"
102356
102352
  }, {
102357
102353
  key: "memoryUsed",
@@ -102393,8 +102389,8 @@ var createOSInfo = (more = false) => {
102393
102389
  }, {
102394
102390
  key: "buildUptime",
102395
102391
  title: "构建已运行时间",
102396
- value: buildUptimeStr,
102397
- description: "构建已运行时间"
102392
+ value: buildUptime,
102393
+ description: `构建已运行时间: ${buildUptimeStr}`
102398
102394
  });
102399
102395
  if (maxRunTime > 0) {
102400
102396
  const now = import_dayjs.default();
@@ -102413,8 +102409,8 @@ var createOSInfo = (more = false) => {
102413
102409
  labels.unshift({
102414
102410
  key: "remainingTime",
102415
102411
  title: "剩余时间",
102416
- value: formatUptime(Math.floor((maxRunTime - buildUptime) / 1000)) + " " + timeTo4Str,
102417
- description: "构建剩余时间"
102412
+ value: maxRunTime - buildUptime,
102413
+ description: "构建剩余时间" + formatUptime(Math.floor((maxRunTime - buildUptime) / 1000)) + " " + timeTo4Str
102418
102414
  });
102419
102415
  }
102420
102416
  }
@@ -102484,9 +102480,9 @@ app.route({
102484
102480
  }
102485
102481
  }).define(async (ctx) => {
102486
102482
  const more = ctx.query?.more ?? false;
102487
- const list4 = getLiveMdContent({ more });
102488
102483
  if (notCNBCheck(ctx))
102489
102484
  return;
102485
+ const list4 = getLiveMdContent({ more });
102490
102486
  ctx.body = {
102491
102487
  title: "开发环境模式配置",
102492
102488
  list: list4