@aiscene/aiserver 1.9.8 → 2.0.0

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.
@@ -3,7 +3,7 @@ import { fork } from 'child_process';
3
3
  import { URL } from 'url';
4
4
  import os from 'os';
5
5
  import { randomUUID } from 'crypto';
6
- import { uploadReportToServer, uploadCapturePackageFile } from '../api/callback.js';
6
+ import { saveCaptureDataToDatabase, uploadReportToServer, uploadCapturePackageFile } from '../api/callback.js';
7
7
  import fs from 'fs';
8
8
  import path from 'path';
9
9
  import { createLogger } from '../core/logger.js';
@@ -492,6 +492,46 @@ export class DebugWebSocketServer {
492
492
  code: resolvedCode || actionParams.code,
493
493
  };
494
494
  }
495
+ pickNonEmptyString(...values) {
496
+ for (const value of values) {
497
+ if (typeof value === 'string' && value.trim())
498
+ return value.trim();
499
+ if (typeof value === 'number' && Number.isFinite(value))
500
+ return String(value);
501
+ }
502
+ return undefined;
503
+ }
504
+ buildCaptureDataPersistenceMeta(fallbackBatchId, ...sources) {
505
+ let batchId;
506
+ let creator;
507
+ for (const source of sources) {
508
+ if (!source || typeof source !== 'object')
509
+ continue;
510
+ const data = source;
511
+ batchId = batchId || this.pickNonEmptyString(data.batchId, data.batch_id, data.executionBatchId);
512
+ creator = creator || this.pickNonEmptyString(data.creator, data.erp, data.ownerErp, data.ownerERP, data.userErp, data.username, data.userName, data.proxyAccount);
513
+ }
514
+ return {
515
+ batchId: batchId || fallbackBatchId,
516
+ creator: creator || '',
517
+ };
518
+ }
519
+ rememberCaptureDataPersistenceMeta(sessionId, meta) {
520
+ const session = sessionManager.get(sessionId);
521
+ if (!session)
522
+ return;
523
+ session.captureDataPersistence = {
524
+ ...session.captureDataPersistence,
525
+ ...meta,
526
+ };
527
+ }
528
+ getCaptureDataPersistenceMeta(sessionId) {
529
+ const session = sessionManager.get(sessionId);
530
+ return {
531
+ batchId: session?.captureDataPersistence?.batchId || sessionId,
532
+ creator: session?.captureDataPersistence?.creator || '',
533
+ };
534
+ }
495
535
  async handleStartDebug(ws, request) {
496
536
  // Web 分支:支持 sessionId 复用 + 主进程内嵌执行 + 投屏
497
537
  if (request.platform === 'web') {
@@ -501,6 +541,7 @@ export class DebugWebSocketServer {
501
541
  // 真机分支:保持原有 fork worker 逻辑
502
542
  const sessionId = `debug-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
503
543
  const session = sessionManager.create(sessionId, ws, request.deviceId);
544
+ this.rememberCaptureDataPersistenceMeta(sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
504
545
  this.sendMessage(ws, { type: 'session_created', sessionId }, request.deviceId);
505
546
  try {
506
547
  request.naturalLanguage = await this.resolveScriptReferences(request.naturalLanguage) || request.naturalLanguage;
@@ -557,6 +598,7 @@ export class DebugWebSocketServer {
557
598
  // 复用:更新 client 引用为最新的 ws(旧 ws 可能已断)
558
599
  session.client = ws;
559
600
  }
601
+ this.rememberCaptureDataPersistenceMeta(sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
560
602
  // 复用时校验浏览器形态参数是否一致:mobileMode/deviceName 在 launch 阶段决定,无法热切
561
603
  if (isReuse) {
562
604
  const incomingMobile = !!request.mobileMode;
@@ -925,7 +967,7 @@ export class DebugWebSocketServer {
925
967
  logger.info('[Debug] Captured ' + capturedRequests.length + ' requests during debug session');
926
968
  // 将抓包数据保存到文件并上传到 OSS
927
969
  try {
928
- const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId);
970
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
929
971
  if (uploadResult) {
930
972
  captureDataUrl = uploadResult;
931
973
  logger.info('[Debug] Capture data uploaded: ' + captureDataUrl);
@@ -1069,7 +1111,7 @@ export class DebugWebSocketServer {
1069
1111
  let workerCaptureDataUrl;
1070
1112
  if (workerCapturedRequests.length > 0) {
1071
1113
  try {
1072
- const uploadResult = await this.saveAndUploadCaptureData(workerCapturedRequests, sessionId);
1114
+ const uploadResult = await this.saveAndUploadCaptureData(workerCapturedRequests, sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
1073
1115
  if (uploadResult) {
1074
1116
  workerCaptureDataUrl = uploadResult;
1075
1117
  logger.info('[Debug-Worker] Capture data uploaded: ' + workerCaptureDataUrl);
@@ -1127,6 +1169,7 @@ export class DebugWebSocketServer {
1127
1169
  async handleExecuteAction(ws, request) {
1128
1170
  const sessionId = `action-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1129
1171
  const session = sessionManager.create(sessionId, ws, request.deviceId);
1172
+ this.rememberCaptureDataPersistenceMeta(sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
1130
1173
  const abortController = new AbortController();
1131
1174
  session.abortController = abortController;
1132
1175
  // 调试日志:打印收到的模型配置
@@ -1226,7 +1269,7 @@ export class DebugWebSocketServer {
1226
1269
  if (capturedRequests.length > 0) {
1227
1270
  logger.info('[Action] Captured ' + capturedRequests.length + ' requests during session');
1228
1271
  try {
1229
- const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId);
1272
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
1230
1273
  if (uploadResult) {
1231
1274
  captureDataUrl = uploadResult;
1232
1275
  logger.info('[Action] Capture data uploaded: ' + captureDataUrl);
@@ -1285,7 +1328,7 @@ export class DebugWebSocketServer {
1285
1328
  if (capturedRequests.length > 0) {
1286
1329
  logger.info('[Action] Captured ' + capturedRequests.length + ' requests (error path)');
1287
1330
  try {
1288
- const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId);
1331
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
1289
1332
  if (uploadResult)
1290
1333
  captureDataUrl = uploadResult;
1291
1334
  }
@@ -1332,6 +1375,7 @@ export class DebugWebSocketServer {
1332
1375
  async handleExecuteAiAct(ws, request) {
1333
1376
  const sessionId = `aiact-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1334
1377
  const session = sessionManager.create(sessionId, ws, request.deviceId);
1378
+ this.rememberCaptureDataPersistenceMeta(sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
1335
1379
  const abortController = new AbortController();
1336
1380
  session.abortController = abortController;
1337
1381
  // 将前端传来的 modelConfig 应用到环境变量,并获取补全后的 modelConfig(含 Planning/Insight)
@@ -1498,7 +1542,7 @@ export class DebugWebSocketServer {
1498
1542
  if (capturedRequests.length > 0) {
1499
1543
  logger.info('[AiAct] Captured ' + capturedRequests.length + ' requests during session');
1500
1544
  try {
1501
- const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId);
1545
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
1502
1546
  if (uploadResult) {
1503
1547
  captureDataUrl = uploadResult;
1504
1548
  logger.info('[AiAct] Capture data uploaded: ' + captureDataUrl);
@@ -1545,7 +1589,7 @@ export class DebugWebSocketServer {
1545
1589
  if (capturedRequests.length > 0) {
1546
1590
  logger.info('[AiAct] Captured ' + capturedRequests.length + ' requests (error path)');
1547
1591
  try {
1548
- const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId);
1592
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
1549
1593
  if (uploadResult)
1550
1594
  captureDataUrl = uploadResult;
1551
1595
  }
@@ -1766,6 +1810,7 @@ export class DebugWebSocketServer {
1766
1810
  session = sessionManager.create(sessionId, ws);
1767
1811
  session.platform = 'web';
1768
1812
  }
1813
+ this.rememberCaptureDataPersistenceMeta(sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
1769
1814
  try {
1770
1815
  this.sendMessage(ws, { type: 'web_action_started', sessionId, actionType: request.actionType });
1771
1816
  const resolvedNaturalLanguage = await this.resolveScriptReferences(request.naturalLanguage);
@@ -2111,7 +2156,7 @@ export class DebugWebSocketServer {
2111
2156
  if (capturedRequests.length > 0) {
2112
2157
  logger.info('[WebAction] Captured ' + capturedRequests.length + ' requests during session');
2113
2158
  try {
2114
- const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId);
2159
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
2115
2160
  if (uploadResult) {
2116
2161
  captureDataUrl = uploadResult;
2117
2162
  logger.info('[WebAction] Capture data uploaded: ' + captureDataUrl);
@@ -2178,7 +2223,7 @@ export class DebugWebSocketServer {
2178
2223
  if (capturedRequests.length > 0) {
2179
2224
  logger.info('[WebAction] Captured ' + capturedRequests.length + ' requests (error path)');
2180
2225
  try {
2181
- const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId);
2226
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, sessionId, this.buildCaptureDataPersistenceMeta(sessionId, request));
2182
2227
  if (uploadResult)
2183
2228
  captureDataUrl = uploadResult;
2184
2229
  }
@@ -2524,6 +2569,7 @@ export class DebugWebSocketServer {
2524
2569
  webActionRequest.sessionId = sharedSessionId;
2525
2570
  }
2526
2571
  const sessionId = sharedSessionId;
2572
+ this.rememberCaptureDataPersistenceMeta(sessionId, this.buildCaptureDataPersistenceMeta(request.batchId, tc, request));
2527
2573
  const reportGroupName = tc.title || tc.id || `batch-${request.batchId}-case-${caseIndex + 1}`;
2528
2574
  const caseMemorySource = this.pickBatchAiMemorySource(request, tc);
2529
2575
  let session = sessionManager.get(sessionId);
@@ -2532,6 +2578,7 @@ export class DebugWebSocketServer {
2532
2578
  if (!session)
2533
2579
  session = sessionManager.create(sessionId, ws);
2534
2580
  session.platform = 'web';
2581
+ this.rememberCaptureDataPersistenceMeta(sessionId, this.buildCaptureDataPersistenceMeta(request.batchId, tc, request));
2535
2582
  // 并发上限保护
2536
2583
  if (this.webSessionsActivity.size >= this.webSessionMaxConcurrency) {
2537
2584
  throw new Error(`Web 调试会话数已达上限 (${this.webSessionMaxConcurrency}),请稍后重试`);
@@ -2646,7 +2693,7 @@ export class DebugWebSocketServer {
2646
2693
  try {
2647
2694
  const capturedRequests = await whistleManager.stopCapture(sessionId);
2648
2695
  if (capturedRequests.length > 0) {
2649
- const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, `${request.batchId}-${tc.id}`);
2696
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, `${request.batchId}-${tc.id}`, this.buildCaptureDataPersistenceMeta(request.batchId, tc, request));
2650
2697
  if (uploadResult)
2651
2698
  caseCaptureDataUrl = uploadResult;
2652
2699
  }
@@ -2737,6 +2784,7 @@ export class DebugWebSocketServer {
2737
2784
  const caseMemorySource = this.pickBatchAiMemorySource(request, tc);
2738
2785
  const caseSessionId = `batch-${batchId}-case-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2739
2786
  const session = sessionManager.create(caseSessionId, ws, effectiveDeviceId);
2787
+ this.rememberCaptureDataPersistenceMeta(caseSessionId, this.buildCaptureDataPersistenceMeta(batchId, tc, request));
2740
2788
  this.activeBatchSessionIds.add(caseSessionId);
2741
2789
  const caseAbortController = new AbortController();
2742
2790
  session.abortController = caseAbortController;
@@ -2841,7 +2889,7 @@ export class DebugWebSocketServer {
2841
2889
  try {
2842
2890
  const capturedRequests = await whistleManager.stopCapture(caseSessionId);
2843
2891
  if (capturedRequests.length > 0) {
2844
- const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, `${batchId}-${tc.id}`);
2892
+ const uploadResult = await this.saveAndUploadCaptureData(capturedRequests, `${batchId}-${tc.id}`, this.buildCaptureDataPersistenceMeta(batchId, tc, request));
2845
2893
  if (uploadResult)
2846
2894
  caseCaptureDataUrl = uploadResult;
2847
2895
  }
@@ -3077,7 +3125,7 @@ export class DebugWebSocketServer {
3077
3125
  let stopCaptureDataUrl;
3078
3126
  if (stopCapturedRequests.length > 0) {
3079
3127
  try {
3080
- const uploadResult = await this.saveAndUploadCaptureData(stopCapturedRequests, request.sessionId);
3128
+ const uploadResult = await this.saveAndUploadCaptureData(stopCapturedRequests, request.sessionId, this.getCaptureDataPersistenceMeta(request.sessionId));
3081
3129
  if (uploadResult) {
3082
3130
  stopCaptureDataUrl = uploadResult;
3083
3131
  logger.info('[StopDebug] Capture data uploaded: ' + stopCaptureDataUrl);
@@ -3363,10 +3411,15 @@ export class DebugWebSocketServer {
3363
3411
  * 将抓包数据保存到本地文件并上传到 OSS
3364
3412
  * 返回 OSS URL,失败则返回 undefined
3365
3413
  */
3366
- async saveAndUploadCaptureData(capturedRequests, sessionId) {
3414
+ async saveAndUploadCaptureData(capturedRequests, sessionId, metadata) {
3367
3415
  if (!capturedRequests || capturedRequests.length === 0)
3368
3416
  return undefined;
3369
3417
  try {
3418
+ const effectiveMetadata = metadata || this.getCaptureDataPersistenceMeta(sessionId);
3419
+ const saveOk = await saveCaptureDataToDatabase(capturedRequests, effectiveMetadata.batchId || sessionId, effectiveMetadata.creator || '');
3420
+ if (!saveOk) {
3421
+ logger.warn(`[CaptureUpload] Save capture data to database failed, continue OSS upload (sessionId=${sessionId})`);
3422
+ }
3370
3423
  // 保存到本地临时文件
3371
3424
  const tmpDir = path.join(process.cwd(), 'tmp', 'captures');
3372
3425
  if (!fs.existsSync(tmpDir)) {