@aiscene/aiserver 1.6.5 → 1.6.6

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.
@@ -2,7 +2,9 @@ import { WebSocketServer } from 'ws';
2
2
  import { fork } from 'child_process';
3
3
  import { URL } from 'url';
4
4
  import os from 'os';
5
- import { uploadReportToServer } from '../api/callback.js';
5
+ import { uploadReportToServer, uploadCapturePackageFile } from '../api/callback.js';
6
+ import fs from 'fs';
7
+ import path from 'path';
6
8
  import { createLogger } from '../core/logger.js';
7
9
  import { eventBus } from '../core/event-bus.js';
8
10
  import { sessionManager } from './session-manager.js';
@@ -13,6 +15,7 @@ import { webBrowserPool } from './web-browser-pool.js';
13
15
  import { ExecutorFactory } from '../executor/executor-factory.js';
14
16
  import { instrumentCode } from '../executor/code-instrument.js';
15
17
  import { playwrightSwipe } from '../core/native-swipe.js';
18
+ import { whistleManager } from '../proxy/whistle-manager.js';
16
19
  const logger = createLogger('DebugWebSocket');
17
20
  /** 获取本机可用的非内部 IPv4 地址,供客户端连接投屏 WebSocket */
18
21
  function getLocalIP() {
@@ -661,6 +664,12 @@ export class DebugWebSocketServer {
661
664
  executionId: sessionId,
662
665
  mobileMode: request.mobileMode,
663
666
  deviceName: request.deviceName,
667
+ // 环境代理配置
668
+ environmentId: request.environmentId,
669
+ proxyPort: request.proxyPort || (request.hostMappings && request.hostMappings.length > 0 ? 8899 : undefined),
670
+ hostMappings: request.hostMappings,
671
+ // 公司代理平台账号
672
+ proxyAccount: request.proxyAccount,
664
673
  // 添加登录配置
665
674
  ...(request.loginConfig && {
666
675
  loginUrl: request.loginConfig.loginUrl,
@@ -673,6 +682,39 @@ export class DebugWebSocketServer {
673
682
  agreementSelector: request.loginConfig.agreementSelector,
674
683
  }),
675
684
  };
685
+ // Setup proxy rules: 如果有 proxyAccount,使用公司代理平台;否则走本地 Whistle
686
+ const whistleApiBase = config.proxyAccount ? `http://proxy-pc.jd.com/account/${config.proxyAccount}` : undefined;
687
+ if (config.hostMappings && config.hostMappings.length > 0) {
688
+ try {
689
+ const envId = config.environmentId || sessionId;
690
+ const ruleGroupName = whistleManager.generateRuleGroupName(envId);
691
+ const success = await whistleManager.createRuleGroup(ruleGroupName, config.hostMappings, whistleApiBase);
692
+ if (success) {
693
+ const sess = sessionManager.get(sessionId);
694
+ if (sess) {
695
+ sess.whistleRuleGroupName = ruleGroupName;
696
+ sess.whistleApiBase = whistleApiBase;
697
+ }
698
+ if (!config.proxyAccount && !config.proxyPort)
699
+ config.proxyPort = 8899;
700
+ logger.info('[Debug] Whistle proxy setup: ruleGroup=' + ruleGroupName + ', proxyPort=' + config.proxyPort + ', proxyAccount=' + (config.proxyAccount || 'N/A'));
701
+ // 开始请求抓包(传入 proxyAccount,自动选择正确的 Whistle 实例)
702
+ try {
703
+ await whistleManager.startCapture(sessionId, undefined, 3000, config.proxyAccount);
704
+ logger.info('[Debug] Whistle request capture started: sessionId=' + sessionId);
705
+ }
706
+ catch (captureErr) {
707
+ logger.warn('[Debug] Failed to start request capture: ' + (captureErr instanceof Error ? captureErr.message : String(captureErr)));
708
+ }
709
+ }
710
+ else {
711
+ logger.warn('[Debug] Whistle proxy setup failed, continuing without proxy');
712
+ }
713
+ }
714
+ catch (err) {
715
+ logger.warn('[Debug] Whistle proxy setup error: ' + (err instanceof Error ? err.message : String(err)));
716
+ }
717
+ }
676
718
  // 使用 AbortSignal 监听停止请求
677
719
  const executePromise = executor.execute(config);
678
720
  const abortPromise = new Promise((resolve) => {
@@ -698,6 +740,42 @@ export class DebugWebSocketServer {
698
740
  // 清理 executor 引用
699
741
  if (session)
700
742
  session.executor = undefined;
743
+ // 停止请求抓包,采集调试期间的所有请求
744
+ let capturedRequests = [];
745
+ let captureDataUrl;
746
+ try {
747
+ capturedRequests = await whistleManager.stopCapture(sessionId);
748
+ if (capturedRequests.length > 0) {
749
+ logger.info('[Debug] Captured ' + capturedRequests.length + ' requests during debug session');
750
+ // 将抓包数据保存到文件并上传到 OSS
751
+ try {
752
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId);
753
+ if (uploadResult) {
754
+ captureDataUrl = uploadResult;
755
+ logger.info('[Debug] Capture data uploaded: ' + captureDataUrl);
756
+ }
757
+ }
758
+ catch (uploadErr) {
759
+ logger.warn('[Debug] Failed to upload capture data: ' + (uploadErr instanceof Error ? uploadErr.message : String(uploadErr)));
760
+ }
761
+ }
762
+ }
763
+ catch (captureErr) {
764
+ logger.warn('[Debug] Failed to stop request capture: ' + (captureErr instanceof Error ? captureErr.message : String(captureErr)));
765
+ }
766
+ // Teardown Whistle proxy rules if they were set
767
+ const currentSess = sessionManager.get(sessionId);
768
+ const whistleRuleName = currentSess ? currentSess.whistleRuleGroupName : undefined;
769
+ if (whistleRuleName) {
770
+ whistleManager.removeRuleGroup(whistleRuleName, whistleApiBase).catch((err) => {
771
+ logger.warn('[Debug] Failed to teardown Whistle proxy: ' + (err instanceof Error ? err.message : String(err)));
772
+ });
773
+ if (currentSess) {
774
+ currentSess.whistleRuleGroupName = undefined;
775
+ currentSess.whistleApiBase = undefined;
776
+ }
777
+ logger.info('[Debug] Whistle proxy teardown: ruleGroup=' + whistleRuleName);
778
+ }
701
779
  sessionManager.updateStatus(sessionId, result.success ? 'completed' : 'failed');
702
780
  // 调试:打印 result 内容
703
781
  logger.info(`[executeRunCodeInProcess] result: success=${result.success}, reportFile=${result.reportFile || ''}, errorMessage=${result.errorMessage}`);
@@ -714,13 +792,17 @@ export class DebugWebSocketServer {
714
792
  reportUrl,
715
793
  error: result.errorMessage,
716
794
  platform: request.platform || 'android',
795
+ captureDataUrl,
717
796
  }, request.deviceId);
718
797
  // 同时发一份 debug_completed,让前端调试面板能正确更新状态
798
+ // capturedRequests 只发摘要给前端展示,完整数据通过 captureDataUrl (OSS URL) 获取
719
799
  this.sendMessage(ws, {
720
800
  type: 'debug_completed',
721
801
  sessionId,
722
802
  success: result.success,
723
803
  reportUrl,
804
+ capturedRequests: capturedRequests.length > 0 ? this.simplifyCapturedRequests(capturedRequests) : undefined,
805
+ captureDataUrl,
724
806
  }, request.deviceId);
725
807
  }
726
808
  /** 自然语言模式:Fork worker 进程执行 aiAction */
@@ -741,6 +823,12 @@ export class DebugWebSocketServer {
741
823
  taskId: sessionId,
742
824
  nodeId: this.config.task.nodeId,
743
825
  modelConfig: fullModelConfig,
826
+ // 环境代理配置
827
+ environmentId: request.environmentId,
828
+ proxyPort: request.proxyPort || (request.hostMappings && request.hostMappings.length > 0 ? 8899 : undefined),
829
+ hostMappings: request.hostMappings,
830
+ // 公司代理平台账号
831
+ proxyAccount: request.proxyAccount,
744
832
  // 添加登录配置
745
833
  ...(request.loginConfig && {
746
834
  loginUrl: request.loginConfig.loginUrl,
@@ -753,6 +841,39 @@ export class DebugWebSocketServer {
753
841
  agreementSelector: request.loginConfig.agreementSelector,
754
842
  }),
755
843
  };
844
+ // Setup proxy rules: 如果有 proxyAccount,使用公司代理平台;否则走本地 Whistle
845
+ const workerWhistleApiBase = execConfig.proxyAccount ? `http://proxy-pc.jd.com/account/${execConfig.proxyAccount}` : undefined;
846
+ if (execConfig.hostMappings && execConfig.hostMappings.length > 0) {
847
+ try {
848
+ const envId = execConfig.environmentId || sessionId;
849
+ const ruleGroupName = whistleManager.generateRuleGroupName(envId);
850
+ const success = await whistleManager.createRuleGroup(ruleGroupName, execConfig.hostMappings, workerWhistleApiBase);
851
+ if (success) {
852
+ const sess = sessionManager.get(sessionId);
853
+ if (sess) {
854
+ sess.whistleRuleGroupName = ruleGroupName;
855
+ sess.whistleApiBase = workerWhistleApiBase;
856
+ }
857
+ if (!execConfig.proxyAccount && !execConfig.proxyPort)
858
+ execConfig.proxyPort = 8899;
859
+ logger.info('[Debug-Worker] Whistle proxy setup: ruleGroup=' + ruleGroupName + ', proxyPort=' + execConfig.proxyPort + ', proxyAccount=' + (execConfig.proxyAccount || 'N/A'));
860
+ // 开始请求抓包(传入 proxyAccount,自动选择正确的 Whistle 实例)
861
+ try {
862
+ await whistleManager.startCapture(sessionId, undefined, 3000, execConfig.proxyAccount);
863
+ logger.info('[Debug-Worker] Whistle request capture started: sessionId=' + sessionId);
864
+ }
865
+ catch (captureErr) {
866
+ logger.warn('[Debug-Worker] Failed to start request capture: ' + (captureErr instanceof Error ? captureErr.message : String(captureErr)));
867
+ }
868
+ }
869
+ else {
870
+ logger.warn('[Debug-Worker] Whistle proxy setup failed, continuing without proxy');
871
+ }
872
+ }
873
+ catch (err) {
874
+ logger.warn('[Debug-Worker] Whistle proxy setup error: ' + (err instanceof Error ? err.message : String(err)));
875
+ }
876
+ }
756
877
  // Fork worker process for long-running debug
757
878
  const workerPath = new URL('../executor/worker-entry.js', import.meta.url).pathname;
758
879
  // 优先使用 process.env 中已被 applyModelConfig() 设置的值(前端传入的模型配置),
@@ -799,10 +920,51 @@ export class DebugWebSocketServer {
799
920
  return;
800
921
  resultHandled = true;
801
922
  const success = workerResult.success !== false;
923
+ // 停止请求抓包,采集调试期间的所有请求
924
+ let workerCapturedRequests = [];
925
+ try {
926
+ workerCapturedRequests = await whistleManager.stopCapture(sessionId);
927
+ if (workerCapturedRequests.length > 0) {
928
+ logger.info('[Debug-Worker] Captured ' + workerCapturedRequests.length + ' requests during debug session');
929
+ }
930
+ }
931
+ catch (captureErr) {
932
+ logger.warn('[Debug-Worker] Failed to stop request capture: ' + (captureErr instanceof Error ? captureErr.message : String(captureErr)));
933
+ }
934
+ // Teardown Whistle proxy rules if they were set
935
+ const curSess = sessionManager.get(sessionId);
936
+ const wRuleName = curSess ? curSess.whistleRuleGroupName : undefined;
937
+ if (wRuleName) {
938
+ try {
939
+ await whistleManager.removeRuleGroup(wRuleName, workerWhistleApiBase);
940
+ if (curSess) {
941
+ curSess.whistleRuleGroupName = undefined;
942
+ curSess.whistleApiBase = undefined;
943
+ }
944
+ logger.info('[Debug-Worker] Whistle proxy teardown: ruleGroup=' + wRuleName);
945
+ }
946
+ catch (err) {
947
+ logger.warn('[Debug-Worker] Failed to teardown Whistle proxy: ' + (err instanceof Error ? err.message : String(err)));
948
+ }
949
+ }
802
950
  sessionManager.updateStatus(sessionId, success ? 'completed' : 'failed');
803
951
  const reportFile = workerResult.reportFile || '';
804
952
  const reportUrl = await this.safeUploadReport(reportFile, sessionId);
805
953
  logger.info(`[Debug] report uploaded: reportUrl=${reportUrl || 'null'}, reportFile=${reportFile}`);
954
+ // 将 worker 抓包数据保存到文件并上传到 OSS
955
+ let workerCaptureDataUrl;
956
+ if (workerCapturedRequests.length > 0) {
957
+ try {
958
+ const uploadResult = await this.saveAndUploadCaptureData(workerCapturedRequests, sessionId);
959
+ if (uploadResult) {
960
+ workerCaptureDataUrl = uploadResult;
961
+ logger.info('[Debug-Worker] Capture data uploaded: ' + workerCaptureDataUrl);
962
+ }
963
+ }
964
+ catch (uploadErr) {
965
+ logger.warn('[Debug-Worker] Failed to upload capture data: ' + (uploadErr instanceof Error ? uploadErr.message : String(uploadErr)));
966
+ }
967
+ }
806
968
  this.sendMessage(ws, {
807
969
  type: 'debug_completed',
808
970
  sessionId,
@@ -810,6 +972,8 @@ export class DebugWebSocketServer {
810
972
  dump: '',
811
973
  reportUrl,
812
974
  error: workerResult.errorMessage,
975
+ capturedRequests: workerCapturedRequests.length > 0 ? this.simplifyCapturedRequests(workerCapturedRequests) : undefined,
976
+ captureDataUrl: workerCaptureDataUrl,
813
977
  }, request.deviceId);
814
978
  };
815
979
  childProcess.on('message', (message) => {
@@ -893,6 +1057,12 @@ export class DebugWebSocketServer {
893
1057
  modelConfig: fullModelConfig,
894
1058
  executionId: sessionId,
895
1059
  skipAppRestart: request.skipAppRestart,
1060
+ // 环境代理配置
1061
+ environmentId: request.environmentId,
1062
+ proxyPort: request.proxyPort,
1063
+ hostMappings: request.hostMappings,
1064
+ // 公司代理平台账号
1065
+ proxyAccount: request.proxyAccount,
896
1066
  };
897
1067
  // 使用 AbortSignal 监听停止请求
898
1068
  const executePromise = executor.execute(config);
@@ -1685,6 +1855,30 @@ export class DebugWebSocketServer {
1685
1855
  return;
1686
1856
  }
1687
1857
  await this.closeWebSession(request.sessionId);
1858
+ // 停止请求抓包
1859
+ try {
1860
+ await whistleManager.stopCapture(request.sessionId);
1861
+ }
1862
+ catch (captureErr) {
1863
+ logger.warn('[CloseWebSession] Failed to stop request capture: ' + (captureErr instanceof Error ? captureErr.message : String(captureErr)));
1864
+ }
1865
+ // Teardown Whistle proxy rules if they were set
1866
+ const closeSess = sessionManager.get(request.sessionId);
1867
+ const closeWhistleRule = closeSess ? closeSess.whistleRuleGroupName : undefined;
1868
+ if (closeWhistleRule) {
1869
+ try {
1870
+ const closeWhistleApiBase = closeSess ? closeSess.whistleApiBase : undefined;
1871
+ await whistleManager.removeRuleGroup(closeWhistleRule, closeWhistleApiBase);
1872
+ if (closeSess) {
1873
+ closeSess.whistleRuleGroupName = undefined;
1874
+ closeSess.whistleApiBase = undefined;
1875
+ }
1876
+ logger.info('[CloseWebSession] Whistle proxy teardown: ruleGroup=' + closeWhistleRule);
1877
+ }
1878
+ catch (err) {
1879
+ logger.warn('[CloseWebSession] Failed to teardown Whistle proxy: ' + (err instanceof Error ? err.message : String(err)));
1880
+ }
1881
+ }
1688
1882
  sessionManager.updateStatus(request.sessionId, 'stopped');
1689
1883
  this.sendMessage(ws, { type: 'web_session_closed', sessionId: request.sessionId });
1690
1884
  }
@@ -1749,15 +1943,59 @@ export class DebugWebSocketServer {
1749
1943
  }
1750
1944
  // 关闭 web 浏览器(如果存在)
1751
1945
  await this.closeWebSession(request.sessionId);
1946
+ // 停止请求抓包,采集调试期间的所有请求
1947
+ let stopCapturedRequests = [];
1948
+ try {
1949
+ stopCapturedRequests = await whistleManager.stopCapture(request.sessionId);
1950
+ if (stopCapturedRequests.length > 0) {
1951
+ logger.info('[StopDebug] Captured ' + stopCapturedRequests.length + ' requests during debug session');
1952
+ }
1953
+ }
1954
+ catch (captureErr) {
1955
+ logger.warn('[StopDebug] Failed to stop request capture: ' + (captureErr instanceof Error ? captureErr.message : String(captureErr)));
1956
+ }
1957
+ // Teardown Whistle proxy rules if they were set
1958
+ const stopSess = sessionManager.get(request.sessionId);
1959
+ const stopWhistleRule = stopSess ? stopSess.whistleRuleGroupName : undefined;
1960
+ if (stopWhistleRule) {
1961
+ try {
1962
+ const stopWhistleApiBase = stopSess ? stopSess.whistleApiBase : undefined;
1963
+ await whistleManager.removeRuleGroup(stopWhistleRule, stopWhistleApiBase);
1964
+ if (stopSess) {
1965
+ stopSess.whistleRuleGroupName = undefined;
1966
+ stopSess.whistleApiBase = undefined;
1967
+ }
1968
+ logger.info('[StopDebug] Whistle proxy teardown: ruleGroup=' + stopWhistleRule);
1969
+ }
1970
+ catch (err) {
1971
+ logger.warn('[StopDebug] Failed to teardown Whistle proxy: ' + (err instanceof Error ? err.message : String(err)));
1972
+ }
1973
+ }
1752
1974
  sessionManager.updateStatus(request.sessionId, 'stopped');
1753
1975
  // 上传报告文件到服务端,拿到 reportUrl
1754
1976
  const reportUrl = await this.safeUploadReport(reportFile, request.sessionId);
1755
1977
  logger.info(`[StopDebug] report uploaded: reportUrl=${reportUrl || 'null'}, reportFile=${reportFile}`);
1978
+ // 将停止时的抓包数据保存到文件并上传到 OSS
1979
+ let stopCaptureDataUrl;
1980
+ if (stopCapturedRequests.length > 0) {
1981
+ try {
1982
+ const uploadResult = await this.saveAndUploadCaptureData(stopCapturedRequests, request.sessionId);
1983
+ if (uploadResult) {
1984
+ stopCaptureDataUrl = uploadResult;
1985
+ logger.info('[StopDebug] Capture data uploaded: ' + stopCaptureDataUrl);
1986
+ }
1987
+ }
1988
+ catch (uploadErr) {
1989
+ logger.warn('[StopDebug] Failed to upload capture data: ' + (uploadErr instanceof Error ? uploadErr.message : String(uploadErr)));
1990
+ }
1991
+ }
1756
1992
  // 发送带执行数据的停止消息
1757
1993
  this.sendMessage(ws, {
1758
1994
  type: 'debug_stopped',
1759
1995
  sessionId: request.sessionId,
1760
1996
  reportUrl,
1997
+ capturedRequests: stopCapturedRequests.length > 0 ? this.simplifyCapturedRequests(stopCapturedRequests) : undefined,
1998
+ captureDataUrl: stopCaptureDataUrl,
1761
1999
  });
1762
2000
  // 同时发送 action_result,让前端能获取到停止时的执行数据
1763
2001
  this.sendMessage(ws, {
@@ -1774,6 +2012,8 @@ export class DebugWebSocketServer {
1774
2012
  sessionId: request.sessionId,
1775
2013
  success: false,
1776
2014
  reportUrl,
2015
+ capturedRequests: stopCapturedRequests.length > 0 ? this.simplifyCapturedRequests(stopCapturedRequests) : undefined,
2016
+ captureDataUrl: stopCaptureDataUrl,
1777
2017
  });
1778
2018
  }
1779
2019
  // ==================== Get Logs ====================
@@ -1937,6 +2177,97 @@ export class DebugWebSocketServer {
1937
2177
  sendError(ws, error) {
1938
2178
  this.sendMessage(ws, { type: 'error', error });
1939
2179
  }
2180
+ /**
2181
+ * 精简 CapturedRequest 列表,去除大 body 只保留摘要信息,
2182
+ * 避免 WebSocket 消息过大(单条 body 上限 4KB,超出截断)
2183
+ */
2184
+ /**
2185
+ * 将抓包数据保存到本地文件并上传到 OSS
2186
+ * 返回 OSS URL,失败则返回 undefined
2187
+ */
2188
+ async saveAndUploadCaptureData(capturedRequests, sessionId) {
2189
+ if (!capturedRequests || capturedRequests.length === 0)
2190
+ return undefined;
2191
+ try {
2192
+ // 保存到本地临时文件
2193
+ const tmpDir = path.join(process.cwd(), 'tmp', 'captures');
2194
+ if (!fs.existsSync(tmpDir)) {
2195
+ fs.mkdirSync(tmpDir, { recursive: true });
2196
+ }
2197
+ const captureFile = path.join(tmpDir, `capture_${sessionId}_${Date.now()}.json`);
2198
+ fs.writeFileSync(captureFile, JSON.stringify(capturedRequests, null, 2));
2199
+ logger.info(`[CaptureUpload] Saved capture data to: ${captureFile} (${capturedRequests.length} requests)`);
2200
+ // 上传到 OSS
2201
+ const uploadUrl = this.config?.callback?.packageFileUploadUrl;
2202
+ if (!uploadUrl) {
2203
+ logger.warn('[CaptureUpload] packageFileUploadUrl not configured, skip upload');
2204
+ return undefined;
2205
+ }
2206
+ const captureDataUrl = await uploadCapturePackageFile(captureFile, sessionId, uploadUrl);
2207
+ if (captureDataUrl) {
2208
+ // 上传成功后删除本地临时文件
2209
+ try {
2210
+ fs.unlinkSync(captureFile);
2211
+ }
2212
+ catch { }
2213
+ return captureDataUrl;
2214
+ }
2215
+ return undefined;
2216
+ }
2217
+ catch (err) {
2218
+ logger.error('[CaptureUpload] Failed to save/upload capture data: ' + (err instanceof Error ? err.message : String(err)));
2219
+ return undefined;
2220
+ }
2221
+ }
2222
+ simplifyCapturedRequests(requests) {
2223
+ const MAX_BODY_LENGTH = 4096;
2224
+ return requests.map(req => {
2225
+ const simplified = {
2226
+ id: req.id,
2227
+ url: req.url,
2228
+ method: req.method,
2229
+ statusCode: req.statusCode,
2230
+ clientIp: req.clientIp,
2231
+ startTime: req.startTime,
2232
+ endTime: req.endTime,
2233
+ duration: req.duration,
2234
+ dnsTime: req.dnsTime,
2235
+ requestTime: req.requestTime,
2236
+ responseTime: req.responseTime,
2237
+ };
2238
+ // 请求头只保留 content-type 和 content-length
2239
+ if (req.reqHeaders) {
2240
+ const filteredReqHeaders = {};
2241
+ for (const [k, v] of Object.entries(req.reqHeaders)) {
2242
+ if (k.toLowerCase() === 'content-type' || k.toLowerCase() === 'content-length') {
2243
+ filteredReqHeaders[k] = v;
2244
+ }
2245
+ }
2246
+ simplified.reqHeaders = filteredReqHeaders;
2247
+ }
2248
+ if (req.resHeaders) {
2249
+ const filteredResHeaders = {};
2250
+ for (const [k, v] of Object.entries(req.resHeaders)) {
2251
+ if (k.toLowerCase() === 'content-type' || k.toLowerCase() === 'content-length') {
2252
+ filteredResHeaders[k] = v;
2253
+ }
2254
+ }
2255
+ simplified.resHeaders = filteredResHeaders;
2256
+ }
2257
+ // body 截断
2258
+ if (req.reqBody) {
2259
+ simplified.reqBody = req.reqBody.length > MAX_BODY_LENGTH
2260
+ ? req.reqBody.substring(0, MAX_BODY_LENGTH) + '...[truncated]'
2261
+ : req.reqBody;
2262
+ }
2263
+ if (req.resBody) {
2264
+ simplified.resBody = req.resBody.length > MAX_BODY_LENGTH
2265
+ ? req.resBody.substring(0, MAX_BODY_LENGTH) + '...[truncated]'
2266
+ : req.resBody;
2267
+ }
2268
+ return simplified;
2269
+ });
2270
+ }
1940
2271
  async cleanupSession(sessionId) {
1941
2272
  // 关闭 web 浏览器(如果存在)
1942
2273
  await this.closeWebSession(sessionId);