@aiscene/aiserver 1.7.1 → 1.7.3

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.
Files changed (87) hide show
  1. package/dist/core/adb.d.ts +10 -0
  2. package/dist/core/adb.d.ts.map +1 -0
  3. package/dist/core/adb.js +25 -0
  4. package/dist/core/adb.js.map +1 -0
  5. package/dist/core/android-identifiers.d.ts +3 -0
  6. package/dist/core/android-identifiers.d.ts.map +1 -0
  7. package/dist/core/android-identifiers.js +21 -0
  8. package/dist/core/android-identifiers.js.map +1 -0
  9. package/dist/core/device-proxy.d.ts +6 -0
  10. package/dist/core/device-proxy.d.ts.map +1 -0
  11. package/dist/core/device-proxy.js +37 -0
  12. package/dist/core/device-proxy.js.map +1 -0
  13. package/dist/core/native-swipe.d.ts.map +1 -1
  14. package/dist/core/native-swipe.js +3 -6
  15. package/dist/core/native-swipe.js.map +1 -1
  16. package/dist/core/types.d.ts +13 -0
  17. package/dist/core/types.d.ts.map +1 -1
  18. package/dist/debug/report-group-name.d.ts +14 -0
  19. package/dist/debug/report-group-name.d.ts.map +1 -0
  20. package/dist/debug/report-group-name.js +30 -0
  21. package/dist/debug/report-group-name.js.map +1 -0
  22. package/dist/debug/types.d.ts +55 -31
  23. package/dist/debug/types.d.ts.map +1 -1
  24. package/dist/debug/websocket-server.d.ts +1 -0
  25. package/dist/debug/websocket-server.d.ts.map +1 -1
  26. package/dist/debug/websocket-server.js +142 -9
  27. package/dist/debug/websocket-server.js.map +1 -1
  28. package/dist/device/detector.d.ts.map +1 -1
  29. package/dist/device/detector.js +2 -1
  30. package/dist/device/detector.js.map +1 -1
  31. package/dist/device/heartbeat.d.ts.map +1 -1
  32. package/dist/device/heartbeat.js +2 -4
  33. package/dist/device/heartbeat.js.map +1 -1
  34. package/dist/executor/action-executor.d.ts.map +1 -1
  35. package/dist/executor/action-executor.js +22 -22
  36. package/dist/executor/action-executor.js.map +1 -1
  37. package/dist/executor/android-executor.d.ts.map +1 -1
  38. package/dist/executor/android-executor.js +22 -19
  39. package/dist/executor/android-executor.js.map +1 -1
  40. package/dist/executor/cli-executor.d.ts.map +1 -1
  41. package/dist/executor/cli-executor.js +6 -0
  42. package/dist/executor/cli-executor.js.map +1 -1
  43. package/dist/login/android-login-context.d.ts +29 -0
  44. package/dist/login/android-login-context.d.ts.map +1 -0
  45. package/dist/login/android-login-context.js +165 -0
  46. package/dist/login/android-login-context.js.map +1 -0
  47. package/dist/recorder/android-login-replay.d.ts +11 -0
  48. package/dist/recorder/android-login-replay.d.ts.map +1 -0
  49. package/dist/recorder/android-login-replay.js +70 -0
  50. package/dist/recorder/android-login-replay.js.map +1 -0
  51. package/dist/recorder/android-login-script.d.ts +50 -0
  52. package/dist/recorder/android-login-script.d.ts.map +1 -0
  53. package/dist/recorder/android-login-script.js +2 -0
  54. package/dist/recorder/android-login-script.js.map +1 -0
  55. package/dist/recorder/android-recording-manager.d.ts +23 -0
  56. package/dist/recorder/android-recording-manager.d.ts.map +1 -0
  57. package/dist/recorder/android-recording-manager.js +180 -0
  58. package/dist/recorder/android-recording-manager.js.map +1 -0
  59. package/dist/recorder/android-ui-hierarchy.d.ts +45 -0
  60. package/dist/recorder/android-ui-hierarchy.d.ts.map +1 -0
  61. package/dist/recorder/android-ui-hierarchy.js +183 -0
  62. package/dist/recorder/android-ui-hierarchy.js.map +1 -0
  63. package/dist/recorder/web-login-replay.d.ts +9 -0
  64. package/dist/recorder/web-login-replay.d.ts.map +1 -0
  65. package/dist/recorder/web-login-replay.js +39 -0
  66. package/dist/recorder/web-login-replay.js.map +1 -0
  67. package/dist/recorder/web-login-script.d.ts +36 -0
  68. package/dist/recorder/web-login-script.d.ts.map +1 -0
  69. package/dist/recorder/web-login-script.js +2 -0
  70. package/dist/recorder/web-login-script.js.map +1 -0
  71. package/dist/recorder/web-recording-manager.d.ts +41 -0
  72. package/dist/recorder/web-recording-manager.d.ts.map +1 -0
  73. package/dist/recorder/web-recording-manager.js +226 -0
  74. package/dist/recorder/web-recording-manager.js.map +1 -0
  75. package/dist/scrcpy/server.d.ts.map +1 -1
  76. package/dist/scrcpy/server.js +213 -11
  77. package/dist/scrcpy/server.js.map +1 -1
  78. package/dist/task/poller.d.ts.map +1 -1
  79. package/dist/task/poller.js +8 -1
  80. package/dist/task/poller.js.map +1 -1
  81. package/dist/task/scheduler.d.ts.map +1 -1
  82. package/dist/task/scheduler.js +71 -0
  83. package/dist/task/scheduler.js.map +1 -1
  84. package/dist/task/types.d.ts.map +1 -1
  85. package/dist/task/types.js +35 -1
  86. package/dist/task/types.js.map +1 -1
  87. package/package.json +1 -1
@@ -2,6 +2,7 @@ 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 { randomUUID } from 'crypto';
5
6
  import { uploadReportToServer, uploadCapturePackageFile } from '../api/callback.js';
6
7
  import fs from 'fs';
7
8
  import path from 'path';
@@ -9,6 +10,7 @@ import { createLogger } from '../core/logger.js';
9
10
  import { eventBus } from '../core/event-bus.js';
10
11
  import { sessionManager } from './session-manager.js';
11
12
  import { dumpManager } from './dump-manager.js';
13
+ import { createDebugReportGroupName } from './report-group-name.js';
12
14
  import { screencastManager } from './screencast.js';
13
15
  import { webScreencastManager } from './web-screencast.js';
14
16
  import { webBrowserPool } from './web-browser-pool.js';
@@ -16,6 +18,8 @@ import { ExecutorFactory } from '../executor/executor-factory.js';
16
18
  import { instrumentCode } from '../executor/code-instrument.js';
17
19
  import { playwrightSwipe } from '../core/native-swipe.js';
18
20
  import { whistleManager } from '../proxy/whistle-manager.js';
21
+ import { executeAndroidLogin, executeWebLogin, } from '../login/android-login-context.js';
22
+ import { webRecordingManager } from '../recorder/web-recording-manager.js';
19
23
  const logger = createLogger('DebugWebSocket');
20
24
  /** 获取本机可用的非内部 IPv4 地址,供客户端连接投屏 WebSocket */
21
25
  function getLocalIP() {
@@ -248,6 +252,10 @@ export class DebugWebSocketServer {
248
252
  });
249
253
  ws.on('close', () => {
250
254
  logger.info('[Control] Connection closed');
255
+ const recordingClientId = ws.recordingClientId;
256
+ if (recordingClientId) {
257
+ void webRecordingManager.cancel(recordingClientId);
258
+ }
251
259
  for (const sessionId of sessionManager.findByClient(ws)) {
252
260
  const s = sessionManager.get(sessionId);
253
261
  // Web 调试会话:客户端断开不销毁浏览器/投屏,仅解绑 client,由 TTL 在 30 分钟无活动时自动回收
@@ -302,10 +310,86 @@ export class DebugWebSocketServer {
302
310
  case 'unsubscribe_task_logs':
303
311
  this.handleUnsubscribeTaskLogs(ws, message);
304
312
  break;
313
+ case 'web_recording_start':
314
+ case 'web_recording_snapshot':
315
+ case 'web_recording_select':
316
+ case 'web_recording_input':
317
+ case 'web_recording_stop':
318
+ case 'web_recording_cancel':
319
+ case 'web_recording_replay':
320
+ await this.handleWebRecording(ws, message);
321
+ break;
305
322
  default:
306
323
  this.sendError(ws, `Unknown message type: ${message.type}`);
307
324
  }
308
325
  }
326
+ async handleWebRecording(ws, request) {
327
+ const clientId = (ws.recordingClientId ||= randomUUID());
328
+ try {
329
+ if (request.type === 'web_recording_start') {
330
+ if (!request.targetUrl)
331
+ throw new Error('targetUrl is required');
332
+ const script = await webRecordingManager.start(clientId, {
333
+ name: request.name,
334
+ targetUrl: request.targetUrl,
335
+ });
336
+ this.sendMessage(ws, { type: 'recording-started', script });
337
+ this.sendMessage(ws, {
338
+ type: 'recording-snapshot',
339
+ ...(await webRecordingManager.snapshot(clientId)),
340
+ });
341
+ return;
342
+ }
343
+ if (request.type === 'web_recording_snapshot') {
344
+ this.sendMessage(ws, {
345
+ type: 'recording-snapshot',
346
+ ...(await webRecordingManager.snapshot(clientId)),
347
+ });
348
+ return;
349
+ }
350
+ if (request.type === 'web_recording_select') {
351
+ const result = await webRecordingManager.select(clientId, Number(request.x), Number(request.y));
352
+ if (result.step) {
353
+ this.sendMessage(ws, { type: 'recording-step-added', step: result.step });
354
+ }
355
+ this.sendMessage(ws, {
356
+ type: 'recording-action-done',
357
+ action: 'tap',
358
+ passwordField: result.passwordField,
359
+ });
360
+ return;
361
+ }
362
+ if (request.type === 'web_recording_input') {
363
+ const step = await webRecordingManager.input(clientId, String(request.text || ''), String(request.recordingValue || request.text || ''), request.secret === true);
364
+ this.sendMessage(ws, { type: 'recording-step-added', step });
365
+ this.sendMessage(ws, { type: 'recording-input-done', secret: request.secret === true });
366
+ return;
367
+ }
368
+ if (request.type === 'web_recording_stop') {
369
+ const script = await webRecordingManager.stop(clientId);
370
+ this.sendMessage(ws, { type: 'recording-stopped', script });
371
+ return;
372
+ }
373
+ if (request.type === 'web_recording_cancel') {
374
+ await webRecordingManager.cancel(clientId);
375
+ this.sendMessage(ws, { type: 'recording-cancelled' });
376
+ return;
377
+ }
378
+ if (request.type === 'web_recording_replay') {
379
+ await webRecordingManager.replay({
380
+ script: request.script,
381
+ variables: request.variables,
382
+ });
383
+ this.sendMessage(ws, { type: 'recording-replay-completed' });
384
+ }
385
+ }
386
+ catch (error) {
387
+ this.sendMessage(ws, {
388
+ type: 'recording-error',
389
+ message: error.message,
390
+ });
391
+ }
392
+ }
309
393
  // ==================== Task Log Subscription ====================
310
394
  handleSubscribeTaskLogs(ws, request) {
311
395
  this.subscribeTaskLog(ws, request.taskId);
@@ -344,6 +428,12 @@ export class DebugWebSocketServer {
344
428
  try {
345
429
  sessionManager.updateStatus(sessionId, 'running');
346
430
  this.sendMessage(ws, { type: 'debug_started', sessionId }, request.deviceId);
431
+ await executeAndroidLogin({
432
+ source: request,
433
+ deviceId: request.deviceId,
434
+ platform: request.platform,
435
+ logPrefix: `[Debug ${sessionId}]`,
436
+ });
347
437
  // 判断 naturalLanguage 是代码还是自然语言
348
438
  const isCode = this.isCodeScript(request.naturalLanguage || '');
349
439
  logger.info(`[Debug] naturalLanguage mode: ${isCode ? 'runCode' : 'aiAction'}, preview: ${(request.naturalLanguage || '').substring(0, 50)}...`);
@@ -377,6 +467,7 @@ export class DebugWebSocketServer {
377
467
  async handleStartDebugWeb(ws, request) {
378
468
  // 1) 决定 sessionId(客户端传了就用以复用)
379
469
  const sessionId = request.sessionId || `debug-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
470
+ const reportGroupName = createDebugReportGroupName(request);
380
471
  // 2) 判断是否复用:session 已存在且浏览器/agent 完整
381
472
  let session = sessionManager.get(sessionId);
382
473
  const isReuse = !!(session && session.browser && session.webAgent && session.page);
@@ -503,6 +594,7 @@ export class DebugWebSocketServer {
503
594
  // modelConfig 走构造参数(与 handleExecuteWebAction 一致,避免多会话相互覆盖 env)
504
595
  session.webAgent = new midscene.PlaywrightAgent(page, {
505
596
  outputFormat: 'html-and-external-assets',
597
+ groupName: reportGroupName,
506
598
  modelConfig: this.applyModelConfig(request.modelConfig),
507
599
  });
508
600
  // 注册并发池 + 启动 TTL 计时
@@ -520,7 +612,12 @@ export class DebugWebSocketServer {
520
612
  wsUrl: `ws://${localIp}:${this.port}/screencast?sessionId=${encodeURIComponent(sessionId)}`,
521
613
  });
522
614
  }
523
- // 登录(仅首次启动时执行)
615
+ await executeWebLogin({
616
+ source: request,
617
+ page,
618
+ logPrefix: `[WebDebug ${sessionId}]`,
619
+ });
620
+ // 兼容旧版表单登录配置(仅首次启动时执行)
524
621
  if (request.loginConfig && request.loginConfig.loginUrl
525
622
  && request.loginConfig.loginUsername && request.loginConfig.loginPassword) {
526
623
  const loginConfig = { ...request.loginConfig, mobileMode: request.mobileMode || false };
@@ -529,7 +626,13 @@ export class DebugWebSocketServer {
529
626
  }
530
627
  else {
531
628
  logger.info(`[WebDebug] Reusing existing session: ${sessionId}`);
532
- // 复用时若投屏被前端关掉过、需要重新开启,可在此扩展(暂保持原状)
629
+ // 浏览器和页面继续复用,但每次请求使用新的 Agent,避免报告内容和名称串到上一次调试。
630
+ const midscene = await import('@aiscene/web/playwright');
631
+ session.webAgent = new midscene.PlaywrightAgent(session.page, {
632
+ outputFormat: 'html-and-external-assets',
633
+ groupName: reportGroupName,
634
+ modelConfig: this.applyModelConfig(request.modelConfig),
635
+ });
533
636
  }
534
637
  // 4) 复用/首启都要:刷新 TTL
535
638
  this.touchWebSession(sessionId);
@@ -658,7 +761,7 @@ export class DebugWebSocketServer {
658
761
  session.executor = executor;
659
762
  const config = {
660
763
  type: (request.platform === 'ios' ? 'ios' : 'android'),
661
- testName: 'debug-runCode',
764
+ testName: createDebugReportGroupName(request),
662
765
  actionType: 'runCode',
663
766
  actionParams: { code: request.naturalLanguage },
664
767
  udid: request.deviceId,
@@ -667,10 +770,11 @@ export class DebugWebSocketServer {
667
770
  url: request.url,
668
771
  modelConfig: fullModelConfig,
669
772
  executionId: sessionId,
773
+ skipAppRestart: request.skipAppRestart,
670
774
  mobileMode: request.mobileMode,
671
775
  deviceName: request.deviceName,
672
776
  // 环境代理配置
673
- proxyPort: request.proxyPort || 8899,
777
+ proxyPort: request.proxyPort,
674
778
  // 公司代理平台账号
675
779
  proxyAccount: request.proxyAccount,
676
780
  // 添加登录配置
@@ -783,7 +887,7 @@ export class DebugWebSocketServer {
783
887
  const fullModelConfig = this.applyModelConfig(request.modelConfig);
784
888
  const execConfig = {
785
889
  type: (request.platform === 'ios' ? 'ios' : 'android'),
786
- testName: 'debugtest',
890
+ testName: createDebugReportGroupName(request),
787
891
  url: request.url,
788
892
  udid: request.deviceId,
789
893
  packageName: request.packageName,
@@ -795,8 +899,9 @@ export class DebugWebSocketServer {
795
899
  taskId: sessionId,
796
900
  nodeId: this.config.task.nodeId,
797
901
  modelConfig: fullModelConfig,
902
+ skipAppRestart: request.skipAppRestart,
798
903
  // 环境代理配置
799
- proxyPort: request.proxyPort || 8899,
904
+ proxyPort: request.proxyPort,
800
905
  // 公司代理平台账号
801
906
  proxyAccount: request.proxyAccount,
802
907
  // 添加登录配置
@@ -959,6 +1064,12 @@ export class DebugWebSocketServer {
959
1064
  try {
960
1065
  sessionManager.updateStatus(sessionId, 'running');
961
1066
  this.sendMessage(ws, { type: 'action_started', sessionId, actionType: request.actionType }, request.deviceId);
1067
+ await executeAndroidLogin({
1068
+ source: request,
1069
+ deviceId: request.deviceId,
1070
+ platform: request.platform,
1071
+ logPrefix: `[Action ${sessionId}]`,
1072
+ });
962
1073
  const executor = ExecutorFactory.create('action', {
963
1074
  sessionId,
964
1075
  onLog: (msg) => {
@@ -976,7 +1087,7 @@ export class DebugWebSocketServer {
976
1087
  session.executor = executor;
977
1088
  const config = {
978
1089
  type: request.platform || 'android',
979
- testName: `action-${request.actionType}`,
1090
+ testName: createDebugReportGroupName(request),
980
1091
  actionType: request.actionType,
981
1092
  locatePrompt: request.locatePrompt,
982
1093
  actionParams: request.actionParams,
@@ -1223,6 +1334,12 @@ export class DebugWebSocketServer {
1223
1334
  try {
1224
1335
  sessionManager.updateStatus(sessionId, 'running');
1225
1336
  this.sendMessage(ws, { type: 'ai_act_started', sessionId }, request.deviceId);
1337
+ await executeAndroidLogin({
1338
+ source: request,
1339
+ deviceId: request.deviceId,
1340
+ platform: request.platform,
1341
+ logPrefix: `[AiAct ${sessionId}]`,
1342
+ });
1226
1343
  const executor = ExecutorFactory.create('action', {
1227
1344
  sessionId,
1228
1345
  onLog: (msg) => {
@@ -1237,7 +1354,7 @@ export class DebugWebSocketServer {
1237
1354
  session.executor = executor;
1238
1355
  const config = {
1239
1356
  type: request.platform || 'android',
1240
- testName: 'ai-act',
1357
+ testName: createDebugReportGroupName(request),
1241
1358
  actionType: 'aiAct',
1242
1359
  naturalLanguage: request.naturalLanguage,
1243
1360
  udid: request.deviceId,
@@ -1434,6 +1551,7 @@ export class DebugWebSocketServer {
1434
1551
  }
1435
1552
  async handleExecuteWebAction(ws, request) {
1436
1553
  const sessionId = request.sessionId || `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1554
+ const reportGroupName = createDebugReportGroupName(request);
1437
1555
  let session = sessionManager.get(sessionId);
1438
1556
  const isNewSession = !session;
1439
1557
  if (isNewSession) {
@@ -1546,6 +1664,7 @@ export class DebugWebSocketServer {
1546
1664
  // 因为 fork 子进程必须通过 env 透传配置,不影响这里。
1547
1665
  session.webAgent = new midscene.PlaywrightAgent(page, {
1548
1666
  outputFormat: 'html-and-external-assets',
1667
+ groupName: reportGroupName,
1549
1668
  modelConfig: this.applyModelConfig(request.modelConfig),
1550
1669
  });
1551
1670
  // 注册到 web session 并发池,开始计入并发数 + TTL
@@ -1564,12 +1683,26 @@ export class DebugWebSocketServer {
1564
1683
  });
1565
1684
  }
1566
1685
  }
1686
+ else {
1687
+ // 允许复用已有浏览器页面,但本次操作必须生成独立报告。
1688
+ const midscene = await import('@aiscene/web/playwright');
1689
+ session.webAgent = new midscene.PlaywrightAgent(session.page, {
1690
+ outputFormat: 'html-and-external-assets',
1691
+ groupName: reportGroupName,
1692
+ modelConfig: this.applyModelConfig(request.modelConfig),
1693
+ });
1694
+ }
1567
1695
  // 取出 agent / page
1568
1696
  const agent = session.webAgent;
1569
1697
  const page = session.page;
1570
1698
  // 刷新 web session 活跃时间,避免 TTL 误杀
1571
1699
  this.touchWebSession(sessionId);
1572
- // 处理登录
1700
+ await executeWebLogin({
1701
+ source: request,
1702
+ page,
1703
+ logPrefix: `[WebAction ${sessionId}]`,
1704
+ });
1705
+ // 兼容旧版表单登录配置
1573
1706
  if (request.loginConfig && request.loginConfig.loginUrl && request.loginConfig.loginUsername && request.loginConfig.loginPassword) {
1574
1707
  const loginConfig = {
1575
1708
  ...request.loginConfig,