@aiscene/aiserver 1.9.6 → 1.9.8
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/core/device-proxy.d.ts +17 -0
- package/dist/core/device-proxy.d.ts.map +1 -1
- package/dist/core/device-proxy.js +95 -5
- package/dist/core/device-proxy.js.map +1 -1
- package/dist/debug/types.d.ts +56 -4
- package/dist/debug/types.d.ts.map +1 -1
- package/dist/debug/websocket-server.d.ts +8 -0
- package/dist/debug/websocket-server.d.ts.map +1 -1
- package/dist/debug/websocket-server.js +180 -17
- package/dist/debug/websocket-server.js.map +1 -1
- package/dist/executor/action-executor.d.ts +5 -5
- package/dist/executor/action-executor.d.ts.map +1 -1
- package/dist/executor/action-executor.js +32 -25
- package/dist/executor/action-executor.js.map +1 -1
- package/dist/executor/android-executor.d.ts +3 -14
- package/dist/executor/android-executor.d.ts.map +1 -1
- package/dist/executor/android-executor.js +21 -96
- package/dist/executor/android-executor.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -10
- package/dist/index.js.map +1 -1
- package/dist/scrcpy/server.d.ts +13 -0
- package/dist/scrcpy/server.d.ts.map +1 -1
- package/dist/scrcpy/server.js +333 -148
- package/dist/scrcpy/server.js.map +1 -1
- package/dist/task/guada-online-batch-poller.d.ts +21 -0
- package/dist/task/guada-online-batch-poller.d.ts.map +1 -0
- package/dist/task/guada-online-batch-poller.js +132 -0
- package/dist/task/guada-online-batch-poller.js.map +1 -0
- package/dist/task/online-loop-poller.d.ts +20 -0
- package/dist/task/online-loop-poller.d.ts.map +1 -0
- package/dist/task/online-loop-poller.js +152 -0
- package/dist/task/online-loop-poller.js.map +1 -0
- package/dist/task/scheduler.d.ts +1 -0
- package/dist/task/scheduler.d.ts.map +1 -1
- package/dist/task/scheduler.js +47 -0
- package/dist/task/scheduler.js.map +1 -1
- package/dist/task/test-case-reference-resolver.d.ts +16 -0
- package/dist/task/test-case-reference-resolver.d.ts.map +1 -0
- package/dist/task/test-case-reference-resolver.js +283 -0
- package/dist/task/test-case-reference-resolver.js.map +1 -0
- package/dist/web/debug-page.d.ts.map +1 -1
- package/dist/web/debug-page.js +49 -58
- package/dist/web/debug-page.js.map +1 -1
- package/dist/web/server.d.ts +5 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +35 -0
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ import { applyAiMemory } from '../core/ai-memory.js';
|
|
|
21
21
|
import { whistleManager } from '../proxy/whistle-manager.js';
|
|
22
22
|
import { executeAndroidLogin, executeWebLogin, } from '../login/android-login-context.js';
|
|
23
23
|
import { webRecordingManager } from '../recorder/web-recording-manager.js';
|
|
24
|
+
import { resolveTestCaseReferences } from '../task/test-case-reference-resolver.js';
|
|
24
25
|
const logger = createLogger('DebugWebSocket');
|
|
25
26
|
/** 获取本机可用的非内部 IPv4 地址,供客户端连接投屏 WebSocket */
|
|
26
27
|
function getLocalIP() {
|
|
@@ -43,6 +44,30 @@ export class DebugWebSocketServer {
|
|
|
43
44
|
port;
|
|
44
45
|
isStarted = false;
|
|
45
46
|
activeBatchSessionIds = new Set();
|
|
47
|
+
createHeadlessBatchClient(batchId) {
|
|
48
|
+
return {
|
|
49
|
+
OPEN: 1,
|
|
50
|
+
readyState: 1,
|
|
51
|
+
bufferedAmount: 0,
|
|
52
|
+
send: (_payload, callback) => {
|
|
53
|
+
if (typeof callback === 'function')
|
|
54
|
+
callback();
|
|
55
|
+
},
|
|
56
|
+
close: () => undefined,
|
|
57
|
+
terminate: () => undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
runBatchActionInBackground(request) {
|
|
61
|
+
if (!this.isStarted) {
|
|
62
|
+
throw new Error('Debug WebSocket server is not started');
|
|
63
|
+
}
|
|
64
|
+
const batchId = request?.batchId || 'unknown';
|
|
65
|
+
const ws = this.createHeadlessBatchClient(batchId);
|
|
66
|
+
return this.handleBatchAction(ws, request).catch((error) => {
|
|
67
|
+
logger.error(`[BatchAction] Background batch failed: batchId=${batchId}, error=${error.message}`);
|
|
68
|
+
throw error;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
46
71
|
// 任务日志订阅: taskId -> Set of WebSocket clients
|
|
47
72
|
taskLogSubscribers = new Map();
|
|
48
73
|
// 记录客户端订阅的所有任务(用于断开时清理)
|
|
@@ -449,6 +474,24 @@ export class DebugWebSocketServer {
|
|
|
449
474
|
// 包含 await/this.agent/this.sleep 等 JS 代码特征则视为代码
|
|
450
475
|
return /\bawait\b/.test(text) || /\bthis\.(agent|sleep|page)\b/.test(text);
|
|
451
476
|
}
|
|
477
|
+
async resolveScriptReferences(script) {
|
|
478
|
+
if (!script)
|
|
479
|
+
return script;
|
|
480
|
+
return resolveTestCaseReferences(script, {
|
|
481
|
+
apiUrl: this.config.task.apiUrl,
|
|
482
|
+
logger,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
async resolveActionParamsReferences(actionParams) {
|
|
486
|
+
if (!actionParams || typeof actionParams.code !== 'string') {
|
|
487
|
+
return actionParams;
|
|
488
|
+
}
|
|
489
|
+
const resolvedCode = await this.resolveScriptReferences(actionParams.code);
|
|
490
|
+
return {
|
|
491
|
+
...actionParams,
|
|
492
|
+
code: resolvedCode || actionParams.code,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
452
495
|
async handleStartDebug(ws, request) {
|
|
453
496
|
// Web 分支:支持 sessionId 复用 + 主进程内嵌执行 + 投屏
|
|
454
497
|
if (request.platform === 'web') {
|
|
@@ -460,6 +503,7 @@ export class DebugWebSocketServer {
|
|
|
460
503
|
const session = sessionManager.create(sessionId, ws, request.deviceId);
|
|
461
504
|
this.sendMessage(ws, { type: 'session_created', sessionId }, request.deviceId);
|
|
462
505
|
try {
|
|
506
|
+
request.naturalLanguage = await this.resolveScriptReferences(request.naturalLanguage) || request.naturalLanguage;
|
|
463
507
|
sessionManager.updateStatus(sessionId, 'running');
|
|
464
508
|
this.sendMessage(ws, { type: 'debug_started', sessionId }, request.deviceId);
|
|
465
509
|
await executeAndroidLogin({
|
|
@@ -536,6 +580,7 @@ export class DebugWebSocketServer {
|
|
|
536
580
|
}
|
|
537
581
|
this.sendMessage(ws, { type: 'session_created', sessionId });
|
|
538
582
|
try {
|
|
583
|
+
request.naturalLanguage = await this.resolveScriptReferences(request.naturalLanguage) || request.naturalLanguage;
|
|
539
584
|
sessionManager.updateStatus(sessionId, 'running');
|
|
540
585
|
this.sendMessage(ws, { type: 'debug_started', sessionId, reused: isReuse });
|
|
541
586
|
// 3) 首次启动:申请浏览器 + 创建 agent + 自动投屏
|
|
@@ -1098,6 +1143,7 @@ export class DebugWebSocketServer {
|
|
|
1098
1143
|
try {
|
|
1099
1144
|
sessionManager.updateStatus(sessionId, 'running');
|
|
1100
1145
|
this.sendMessage(ws, { type: 'action_started', sessionId, actionType: request.actionType }, request.deviceId);
|
|
1146
|
+
const resolvedActionParams = await this.resolveActionParamsReferences(request.actionParams);
|
|
1101
1147
|
await executeAndroidLogin({
|
|
1102
1148
|
source: request,
|
|
1103
1149
|
deviceId: request.deviceId,
|
|
@@ -1124,10 +1170,11 @@ export class DebugWebSocketServer {
|
|
|
1124
1170
|
testName: createDebugReportGroupName(request),
|
|
1125
1171
|
actionType: request.actionType,
|
|
1126
1172
|
locatePrompt: request.locatePrompt,
|
|
1127
|
-
actionParams:
|
|
1173
|
+
actionParams: resolvedActionParams,
|
|
1128
1174
|
udid: request.deviceId,
|
|
1129
1175
|
packageName: request.packageName,
|
|
1130
|
-
testUrl: request
|
|
1176
|
+
testUrl: this.pickWebTargetUrl(request),
|
|
1177
|
+
url: this.pickWebTargetUrl(request),
|
|
1131
1178
|
modelConfig: fullModelConfig,
|
|
1132
1179
|
aiMemory: request.aiMemory,
|
|
1133
1180
|
memory: request.memory,
|
|
@@ -1291,7 +1338,7 @@ export class DebugWebSocketServer {
|
|
|
1291
1338
|
const fullModelConfig = this.applyModelConfig(request.modelConfig);
|
|
1292
1339
|
// 始终启动请求抓包
|
|
1293
1340
|
try {
|
|
1294
|
-
await whistleManager.startCapture(sessionId);
|
|
1341
|
+
await whistleManager.startCapture(sessionId, undefined, 3000, request.proxyAccount);
|
|
1295
1342
|
logger.info('[AiAct] Whistle request capture started: sessionId=' + sessionId);
|
|
1296
1343
|
}
|
|
1297
1344
|
catch (captureErr) {
|
|
@@ -1371,6 +1418,7 @@ export class DebugWebSocketServer {
|
|
|
1371
1418
|
try {
|
|
1372
1419
|
sessionManager.updateStatus(sessionId, 'running');
|
|
1373
1420
|
this.sendMessage(ws, { type: 'ai_act_started', sessionId }, request.deviceId);
|
|
1421
|
+
const resolvedNaturalLanguage = await this.resolveScriptReferences(request.naturalLanguage);
|
|
1374
1422
|
await executeAndroidLogin({
|
|
1375
1423
|
source: request,
|
|
1376
1424
|
deviceId: request.deviceId,
|
|
@@ -1393,14 +1441,18 @@ export class DebugWebSocketServer {
|
|
|
1393
1441
|
type: request.platform || 'android',
|
|
1394
1442
|
testName: createDebugReportGroupName(request),
|
|
1395
1443
|
actionType: 'aiAct',
|
|
1396
|
-
naturalLanguage:
|
|
1444
|
+
naturalLanguage: resolvedNaturalLanguage,
|
|
1397
1445
|
udid: request.deviceId,
|
|
1398
1446
|
packageName: request.packageName,
|
|
1447
|
+
testUrl: this.pickWebTargetUrl(request),
|
|
1448
|
+
url: this.pickWebTargetUrl(request),
|
|
1399
1449
|
modelConfig: fullModelConfig,
|
|
1400
1450
|
aiMemory: request.aiMemory,
|
|
1401
1451
|
memory: request.memory,
|
|
1402
1452
|
memoryScope: request.memoryScope,
|
|
1403
1453
|
skipAppRestart: request.skipAppRestart,
|
|
1454
|
+
proxyPort: request.proxyPort,
|
|
1455
|
+
proxyAccount: request.proxyAccount,
|
|
1404
1456
|
actionParams: {
|
|
1405
1457
|
deepLocate: request.deepLocate,
|
|
1406
1458
|
deepThink: request.deepThink,
|
|
@@ -1547,9 +1599,92 @@ export class DebugWebSocketServer {
|
|
|
1547
1599
|
return String(value).split('#')[0].replace(/\/$/, '');
|
|
1548
1600
|
}
|
|
1549
1601
|
}
|
|
1602
|
+
nonEmptyText(value) {
|
|
1603
|
+
if (typeof value !== 'string')
|
|
1604
|
+
return '';
|
|
1605
|
+
const trimmed = value.trim();
|
|
1606
|
+
if (!trimmed)
|
|
1607
|
+
return '';
|
|
1608
|
+
if (trimmed.length >= 2 &&
|
|
1609
|
+
((trimmed[0] === '"' && trimmed[trimmed.length - 1] === '"') ||
|
|
1610
|
+
(trimmed[0] === "'" && trimmed[trimmed.length - 1] === "'"))) {
|
|
1611
|
+
return trimmed.slice(1, -1).trim();
|
|
1612
|
+
}
|
|
1613
|
+
return trimmed;
|
|
1614
|
+
}
|
|
1615
|
+
pickWebTargetUrlWithSource(source, label = 'source', seen = new WeakSet()) {
|
|
1616
|
+
if (!source || typeof source !== 'object')
|
|
1617
|
+
return { value: '', path: '' };
|
|
1618
|
+
if (seen.has(source))
|
|
1619
|
+
return { value: '', path: '' };
|
|
1620
|
+
seen.add(source);
|
|
1621
|
+
const record = source;
|
|
1622
|
+
const urlKeys = [
|
|
1623
|
+
'testUrl',
|
|
1624
|
+
'test_url',
|
|
1625
|
+
'url',
|
|
1626
|
+
'targetUrl',
|
|
1627
|
+
'target_url',
|
|
1628
|
+
'pageUrl',
|
|
1629
|
+
'page_url',
|
|
1630
|
+
'openUrl',
|
|
1631
|
+
'open_url',
|
|
1632
|
+
'jumpUrl',
|
|
1633
|
+
'jump_url',
|
|
1634
|
+
'deeplink',
|
|
1635
|
+
'deepLink',
|
|
1636
|
+
'deep_link',
|
|
1637
|
+
'schemeUrl',
|
|
1638
|
+
'scheme_url',
|
|
1639
|
+
'schemaUrl',
|
|
1640
|
+
'schema_url',
|
|
1641
|
+
'h5Url',
|
|
1642
|
+
'h5_url',
|
|
1643
|
+
'webUrl',
|
|
1644
|
+
'web_url',
|
|
1645
|
+
'landingUrl',
|
|
1646
|
+
'landing_url',
|
|
1647
|
+
];
|
|
1648
|
+
for (const key of urlKeys) {
|
|
1649
|
+
const value = this.nonEmptyText(record[key]);
|
|
1650
|
+
if (value)
|
|
1651
|
+
return { value, path: `${label}.${key}` };
|
|
1652
|
+
}
|
|
1653
|
+
const nestedKeys = [
|
|
1654
|
+
'actionParams',
|
|
1655
|
+
'params',
|
|
1656
|
+
'config',
|
|
1657
|
+
'data',
|
|
1658
|
+
'payload',
|
|
1659
|
+
'extra',
|
|
1660
|
+
'case',
|
|
1661
|
+
'testCase',
|
|
1662
|
+
'request',
|
|
1663
|
+
'context',
|
|
1664
|
+
];
|
|
1665
|
+
for (const key of nestedKeys) {
|
|
1666
|
+
const nested = record[key];
|
|
1667
|
+
if (nested && typeof nested === 'object') {
|
|
1668
|
+
const picked = this.pickWebTargetUrlWithSource(nested, `${label}.${key}`, seen);
|
|
1669
|
+
if (picked.value)
|
|
1670
|
+
return picked;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
return { value: '', path: '' };
|
|
1674
|
+
}
|
|
1550
1675
|
pickWebTargetUrl(source) {
|
|
1551
|
-
|
|
1552
|
-
|
|
1676
|
+
return this.pickWebTargetUrlWithSource(source).value;
|
|
1677
|
+
}
|
|
1678
|
+
summarizeUrlInputs(source) {
|
|
1679
|
+
if (!source || typeof source !== 'object')
|
|
1680
|
+
return 'none';
|
|
1681
|
+
const record = source;
|
|
1682
|
+
const topLevelKeys = Object.keys(record).filter((key) => /url|link|scheme|schema|deep/i.test(key));
|
|
1683
|
+
const actionParams = record.actionParams;
|
|
1684
|
+
const actionParamKeys = actionParams && typeof actionParams === 'object'
|
|
1685
|
+
? Object.keys(actionParams).filter((key) => /url|link|scheme|schema|deep/i.test(key))
|
|
1686
|
+
: [];
|
|
1687
|
+
return `top=[${topLevelKeys.join(',') || '-'}], actionParams=[${actionParamKeys.join(',') || '-'}]`;
|
|
1553
1688
|
}
|
|
1554
1689
|
pickBatchAiMemorySource(request, tc) {
|
|
1555
1690
|
return {
|
|
@@ -1633,6 +1768,8 @@ export class DebugWebSocketServer {
|
|
|
1633
1768
|
}
|
|
1634
1769
|
try {
|
|
1635
1770
|
this.sendMessage(ws, { type: 'web_action_started', sessionId, actionType: request.actionType });
|
|
1771
|
+
const resolvedNaturalLanguage = await this.resolveScriptReferences(request.naturalLanguage);
|
|
1772
|
+
const resolvedActionParams = await this.resolveActionParamsReferences(request.actionParams);
|
|
1636
1773
|
// 始终启动请求抓包
|
|
1637
1774
|
try {
|
|
1638
1775
|
await whistleManager.startCapture(sessionId);
|
|
@@ -1860,7 +1997,7 @@ export class DebugWebSocketServer {
|
|
|
1860
1997
|
break;
|
|
1861
1998
|
}
|
|
1862
1999
|
case 'aiAction': {
|
|
1863
|
-
const text =
|
|
2000
|
+
const text = resolvedNaturalLanguage || request.locatePrompt || '';
|
|
1864
2001
|
if (!text)
|
|
1865
2002
|
throw new Error('aiAction requires naturalLanguage or locatePrompt');
|
|
1866
2003
|
result = await agent.aiAct(text);
|
|
@@ -1899,7 +2036,7 @@ export class DebugWebSocketServer {
|
|
|
1899
2036
|
}
|
|
1900
2037
|
case 'runCode': {
|
|
1901
2038
|
// 支持两种方式传递代码:request.code 或 request.actionParams.code
|
|
1902
|
-
const code =
|
|
2039
|
+
const code = resolvedActionParams?.code || request.code || '';
|
|
1903
2040
|
if (!code)
|
|
1904
2041
|
throw new Error('runCode requires actionParams.code');
|
|
1905
2042
|
// 行号探针:与真机 runCode 一致,通过 instrumentCode 在每条语句前插入 __lineCallback(line),
|
|
@@ -2286,7 +2423,12 @@ export class DebugWebSocketServer {
|
|
|
2286
2423
|
callbackOk: callbackFailures.length === 0,
|
|
2287
2424
|
callbackFailures: callbackFailures.slice(-10),
|
|
2288
2425
|
});
|
|
2289
|
-
|
|
2426
|
+
try {
|
|
2427
|
+
await this.continueNextLoopBatches(ws, request, finalizeCallback.data);
|
|
2428
|
+
}
|
|
2429
|
+
catch (error) {
|
|
2430
|
+
logger.error(`[BatchAction] Continue next loop batch failed after finalize: batchId=${batchId}, error=${error.message}`);
|
|
2431
|
+
}
|
|
2290
2432
|
}
|
|
2291
2433
|
}
|
|
2292
2434
|
getNextBatchRequests(callbackData) {
|
|
@@ -2342,14 +2484,16 @@ export class DebugWebSocketServer {
|
|
|
2342
2484
|
if (!targetUrl) {
|
|
2343
2485
|
throw new Error('Web 用例缺少测试地址,请先在用例中补充测试地址后再执行');
|
|
2344
2486
|
}
|
|
2487
|
+
const resolvedNaturalLanguage = await this.resolveScriptReferences(tc.naturalLanguage);
|
|
2488
|
+
const resolvedActionParams = await this.resolveActionParamsReferences(tc.actionParams);
|
|
2345
2489
|
// 构造单条 WebActionExecuteRequest
|
|
2346
2490
|
const webActionRequest = {
|
|
2347
2491
|
type: 'execute_web_action',
|
|
2348
2492
|
actionType: tc.actionType || 'aiAction',
|
|
2349
2493
|
url: targetUrl,
|
|
2350
2494
|
testUrl: targetUrl,
|
|
2351
|
-
naturalLanguage:
|
|
2352
|
-
actionParams:
|
|
2495
|
+
naturalLanguage: resolvedNaturalLanguage,
|
|
2496
|
+
actionParams: resolvedActionParams,
|
|
2353
2497
|
sessionId: sharedSessionId || undefined,
|
|
2354
2498
|
mobileMode: request.mobileMode,
|
|
2355
2499
|
deviceName: request.deviceName,
|
|
@@ -2525,7 +2669,7 @@ export class DebugWebSocketServer {
|
|
|
2525
2669
|
// 执行动作
|
|
2526
2670
|
const actionType = tc.actionType || 'aiAction';
|
|
2527
2671
|
if (actionType === 'aiAction' || actionType === 'aiAct') {
|
|
2528
|
-
const text =
|
|
2672
|
+
const text = resolvedNaturalLanguage || '';
|
|
2529
2673
|
if (!text)
|
|
2530
2674
|
throw new Error('aiAction requires naturalLanguage');
|
|
2531
2675
|
await agent.aiAct(text);
|
|
@@ -2536,7 +2680,7 @@ export class DebugWebSocketServer {
|
|
|
2536
2680
|
await this.safeWebGoto(page, targetUrl, `${caseLabel} goto`);
|
|
2537
2681
|
}
|
|
2538
2682
|
else if (actionType === 'runCode') {
|
|
2539
|
-
const code =
|
|
2683
|
+
const code = resolvedActionParams?.code || '';
|
|
2540
2684
|
if (!code)
|
|
2541
2685
|
throw new Error('runCode requires actionParams.code');
|
|
2542
2686
|
const AsyncFunctionCtor = async function () { }.constructor;
|
|
@@ -2584,6 +2728,11 @@ export class DebugWebSocketServer {
|
|
|
2584
2728
|
const effectiveDeviceId = tc.deviceId || request.deviceId;
|
|
2585
2729
|
const effectivePackageName = tc.packageName || request.packageName;
|
|
2586
2730
|
const effectiveBundleId = tc.bundleId || request.bundleId;
|
|
2731
|
+
let effectiveTestUrlInfo = this.pickWebTargetUrlWithSource(tc, 'case');
|
|
2732
|
+
if (!effectiveTestUrlInfo.value) {
|
|
2733
|
+
effectiveTestUrlInfo = this.pickWebTargetUrlWithSource(request, 'request');
|
|
2734
|
+
}
|
|
2735
|
+
let effectiveTestUrl = effectiveTestUrlInfo.value;
|
|
2587
2736
|
const effectiveLoginContext = tc.loginContext || batchLoginContext;
|
|
2588
2737
|
const caseMemorySource = this.pickBatchAiMemorySource(request, tc);
|
|
2589
2738
|
const caseSessionId = `batch-${batchId}-case-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
@@ -2631,18 +2780,32 @@ export class DebugWebSocketServer {
|
|
|
2631
2780
|
catch (captureErr) {
|
|
2632
2781
|
logger.warn(`[BatchAction] Failed to start capture: ${captureErr.message}`);
|
|
2633
2782
|
}
|
|
2783
|
+
const resolvedNaturalLanguage = await this.resolveScriptReferences(tc.naturalLanguage);
|
|
2784
|
+
const resolvedActionParams = await this.resolveActionParamsReferences(tc.actionParams);
|
|
2785
|
+
if (!effectiveTestUrl && resolvedActionParams && resolvedActionParams !== tc.actionParams) {
|
|
2786
|
+
effectiveTestUrlInfo = this.pickWebTargetUrlWithSource({ actionParams: resolvedActionParams }, 'case');
|
|
2787
|
+
effectiveTestUrl = effectiveTestUrlInfo.value;
|
|
2788
|
+
}
|
|
2789
|
+
if (effectiveTestUrl) {
|
|
2790
|
+
logger.info(`${caseLabel}: resolved test URL from ${effectiveTestUrlInfo.path}`);
|
|
2791
|
+
}
|
|
2792
|
+
else {
|
|
2793
|
+
logger.warn(`${caseLabel}: no test URL resolved; checked case ${this.summarizeUrlInputs(tc)}; request ${this.summarizeUrlInputs(request)}`);
|
|
2794
|
+
}
|
|
2634
2795
|
// 构造 ExecuteConfig
|
|
2635
2796
|
const actionType = tc.actionType || 'aiAct';
|
|
2636
2797
|
const config = {
|
|
2637
2798
|
type: platform,
|
|
2638
2799
|
testName: tc.title || tc.id || `batch-${batchId}-case-${caseIndex + 1}`,
|
|
2639
2800
|
actionType,
|
|
2640
|
-
naturalLanguage:
|
|
2641
|
-
locatePrompt:
|
|
2642
|
-
actionParams:
|
|
2801
|
+
naturalLanguage: resolvedNaturalLanguage,
|
|
2802
|
+
locatePrompt: resolvedActionParams?.locatePrompt,
|
|
2803
|
+
actionParams: resolvedActionParams,
|
|
2643
2804
|
udid: effectiveDeviceId,
|
|
2644
2805
|
packageName: effectivePackageName,
|
|
2645
2806
|
bundleId: effectiveBundleId,
|
|
2807
|
+
testUrl: effectiveTestUrl || undefined,
|
|
2808
|
+
url: effectiveTestUrl || undefined,
|
|
2646
2809
|
modelConfig: fullModelConfig,
|
|
2647
2810
|
aiMemory: caseMemorySource.aiMemory,
|
|
2648
2811
|
memory: caseMemorySource.memory,
|
|
@@ -3083,7 +3246,7 @@ export class DebugWebSocketServer {
|
|
|
3083
3246
|
const token = request.callback?.token?.trim();
|
|
3084
3247
|
if (token)
|
|
3085
3248
|
headers.Authorization = token.startsWith('Bearer ') ? token : `Bearer ${token}`;
|
|
3086
|
-
const timeoutMs = Math.max(1000, Math.min(Number(request.callback?.timeoutMs || 10000) || 10000,
|
|
3249
|
+
const timeoutMs = Math.max(1000, Math.min(Number(request.callback?.timeoutMs || 10000) || 10000, 600000));
|
|
3087
3250
|
const maxAttempts = 2;
|
|
3088
3251
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
3089
3252
|
const controller = new AbortController();
|