@aiscene/aiserver 1.9.6 → 1.9.7
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/debug/websocket-server.d.ts +5 -0
- package/dist/debug/websocket-server.d.ts.map +1 -1
- package/dist/debug/websocket-server.js +70 -13
- package/dist/debug/websocket-server.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/scrcpy/server.d.ts +8 -0
- package/dist/scrcpy/server.d.ts.map +1 -1
- package/dist/scrcpy/server.js +253 -134
- package/dist/scrcpy/server.js.map +1 -1
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DebugSession } from '../core/types.js';
|
|
2
|
+
import type { BatchActionRequest } from './types.js';
|
|
2
3
|
import type { AIServerConfig } from '../config/schema.js';
|
|
3
4
|
export declare class DebugWebSocketServer {
|
|
4
5
|
private wss;
|
|
@@ -6,6 +7,8 @@ export declare class DebugWebSocketServer {
|
|
|
6
7
|
private port;
|
|
7
8
|
private isStarted;
|
|
8
9
|
private activeBatchSessionIds;
|
|
10
|
+
private createHeadlessBatchClient;
|
|
11
|
+
runBatchActionInBackground(request: BatchActionRequest): void;
|
|
9
12
|
private taskLogSubscribers;
|
|
10
13
|
private clientTaskSubscriptions;
|
|
11
14
|
/** 当前已启动浏览器的 web session 集合(用于并发计数与 TTL 扫描) */
|
|
@@ -53,6 +56,8 @@ export declare class DebugWebSocketServer {
|
|
|
53
56
|
private handleUnsubscribeTaskLogs;
|
|
54
57
|
/** 判断 naturalLanguage 是否为可执行代码(而非自然语言描述) */
|
|
55
58
|
private isCodeScript;
|
|
59
|
+
private resolveScriptReferences;
|
|
60
|
+
private resolveActionParamsReferences;
|
|
56
61
|
private handleStartDebug;
|
|
57
62
|
/**
|
|
58
63
|
* Web 调试主进程内嵌执行
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../src/debug/websocket-server.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../src/debug/websocket-server.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,KAAK,EAQV,kBAAkB,EAUnB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA2B1D,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,qBAAqB,CAA0B;IAEvD,OAAO,CAAC,yBAAyB;IAajC,0BAA0B,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAY7D,OAAO,CAAC,kBAAkB,CAA0C;IAEpE,OAAO,CAAC,uBAAuB,CAA0C;IAGzE,+CAA+C;IAC/C,OAAO,CAAC,mBAAmB,CAAkC;IAC7D,6DAA6D;IAC7D,OAAO,CAAC,wBAAwB,CAAgE;IAChG,6CAA6C;IAC7C,OAAO,CAAC,uBAAuB,CAA6E;IAC5G,gBAAgB;IAChB,OAAO,CAAC,iBAAiB,CAA+B;gBAE5C,IAAI,GAAE,MAAa;IAI/B,8CAA8C;IAC9C,OAAO,CAAC,eAAe;IAMvB,2CAA2C;IAC3C,OAAO,CAAC,kBAAkB;IAI1B,8BAA8B;IAC9B,OAAO,CAAC,oBAAoB;IAI5B,qCAAqC;IACrC,OAAO,CAAC,iBAAiB;IAenB,KAAK,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA4ElD;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAmB5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAexB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAcxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAUlC,OAAO,CAAC,uBAAuB;YAuDjB,aAAa;YAuDb,kBAAkB;IAwGhC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,yBAAyB;IAWjC,4CAA4C;IAC5C,OAAO,CAAC,YAAY;YAON,uBAAuB;YAQvB,6BAA6B;YAY7B,gBAAgB;IAkD9B;;;;;OAKG;YACW,mBAAmB;IAkQjC;;OAEG;YACW,gBAAgB;IAyC9B,6CAA6C;YAC/B,uBAAuB;IAkJrC,uCAAuC;YACzB,wBAAwB;YAwKxB,mBAAmB;YAyNnB,kBAAkB;IAkQhC,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,uBAAuB;IAY/B,OAAO,CAAC,gBAAgB;YAQV,oBAAoB;YAKpB,WAAW;YAmDX,sBAAsB;IA2fpC,OAAO,CAAC,qBAAqB,CAAsC;YAErD,iBAAiB;IA8M/B,OAAO,CAAC,oBAAoB;YAiBd,uBAAuB;IAqCrC,uBAAuB;YACT,mBAAmB;IAoPjC,qCAAqC;YACvB,mBAAmB;YAkJnB,eAAe;YAiBf,qBAAqB;YA4BrB,oBAAoB;YAuBpB,eAAe;YAqDf,qBAAqB;YAqBrB,eAAe;YAgIf,aAAa;IAU3B;;OAEG;YACW,gBAAgB;IA2B9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;YAgFV,iBAAiB;IAiE/B,OAAO,CAAC,WAAW;IA2DnB,OAAO,CAAC,SAAS;IAIjB;;;OAGG;IAEH;;;OAGG;YACW,wBAAwB;YAqCxB,cAAc;IA4B5B,WAAW,IAAI,YAAY,EAAE;IAIvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAa7B"}
|
|
@@ -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,29 @@ 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
|
+
void this.handleBatchAction(ws, request).catch((error) => {
|
|
67
|
+
logger.error(`[BatchAction] Background batch failed: batchId=${batchId}, error=${error.message}`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
46
70
|
// 任务日志订阅: taskId -> Set of WebSocket clients
|
|
47
71
|
taskLogSubscribers = new Map();
|
|
48
72
|
// 记录客户端订阅的所有任务(用于断开时清理)
|
|
@@ -449,6 +473,24 @@ export class DebugWebSocketServer {
|
|
|
449
473
|
// 包含 await/this.agent/this.sleep 等 JS 代码特征则视为代码
|
|
450
474
|
return /\bawait\b/.test(text) || /\bthis\.(agent|sleep|page)\b/.test(text);
|
|
451
475
|
}
|
|
476
|
+
async resolveScriptReferences(script) {
|
|
477
|
+
if (!script)
|
|
478
|
+
return script;
|
|
479
|
+
return resolveTestCaseReferences(script, {
|
|
480
|
+
apiUrl: this.config.task.apiUrl,
|
|
481
|
+
logger,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
async resolveActionParamsReferences(actionParams) {
|
|
485
|
+
if (!actionParams || typeof actionParams.code !== 'string') {
|
|
486
|
+
return actionParams;
|
|
487
|
+
}
|
|
488
|
+
const resolvedCode = await this.resolveScriptReferences(actionParams.code);
|
|
489
|
+
return {
|
|
490
|
+
...actionParams,
|
|
491
|
+
code: resolvedCode || actionParams.code,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
452
494
|
async handleStartDebug(ws, request) {
|
|
453
495
|
// Web 分支:支持 sessionId 复用 + 主进程内嵌执行 + 投屏
|
|
454
496
|
if (request.platform === 'web') {
|
|
@@ -460,6 +502,7 @@ export class DebugWebSocketServer {
|
|
|
460
502
|
const session = sessionManager.create(sessionId, ws, request.deviceId);
|
|
461
503
|
this.sendMessage(ws, { type: 'session_created', sessionId }, request.deviceId);
|
|
462
504
|
try {
|
|
505
|
+
request.naturalLanguage = await this.resolveScriptReferences(request.naturalLanguage) || request.naturalLanguage;
|
|
463
506
|
sessionManager.updateStatus(sessionId, 'running');
|
|
464
507
|
this.sendMessage(ws, { type: 'debug_started', sessionId }, request.deviceId);
|
|
465
508
|
await executeAndroidLogin({
|
|
@@ -536,6 +579,7 @@ export class DebugWebSocketServer {
|
|
|
536
579
|
}
|
|
537
580
|
this.sendMessage(ws, { type: 'session_created', sessionId });
|
|
538
581
|
try {
|
|
582
|
+
request.naturalLanguage = await this.resolveScriptReferences(request.naturalLanguage) || request.naturalLanguage;
|
|
539
583
|
sessionManager.updateStatus(sessionId, 'running');
|
|
540
584
|
this.sendMessage(ws, { type: 'debug_started', sessionId, reused: isReuse });
|
|
541
585
|
// 3) 首次启动:申请浏览器 + 创建 agent + 自动投屏
|
|
@@ -1098,6 +1142,7 @@ export class DebugWebSocketServer {
|
|
|
1098
1142
|
try {
|
|
1099
1143
|
sessionManager.updateStatus(sessionId, 'running');
|
|
1100
1144
|
this.sendMessage(ws, { type: 'action_started', sessionId, actionType: request.actionType }, request.deviceId);
|
|
1145
|
+
const resolvedActionParams = await this.resolveActionParamsReferences(request.actionParams);
|
|
1101
1146
|
await executeAndroidLogin({
|
|
1102
1147
|
source: request,
|
|
1103
1148
|
deviceId: request.deviceId,
|
|
@@ -1124,7 +1169,7 @@ export class DebugWebSocketServer {
|
|
|
1124
1169
|
testName: createDebugReportGroupName(request),
|
|
1125
1170
|
actionType: request.actionType,
|
|
1126
1171
|
locatePrompt: request.locatePrompt,
|
|
1127
|
-
actionParams:
|
|
1172
|
+
actionParams: resolvedActionParams,
|
|
1128
1173
|
udid: request.deviceId,
|
|
1129
1174
|
packageName: request.packageName,
|
|
1130
1175
|
testUrl: request.testUrl,
|
|
@@ -1371,6 +1416,7 @@ export class DebugWebSocketServer {
|
|
|
1371
1416
|
try {
|
|
1372
1417
|
sessionManager.updateStatus(sessionId, 'running');
|
|
1373
1418
|
this.sendMessage(ws, { type: 'ai_act_started', sessionId }, request.deviceId);
|
|
1419
|
+
const resolvedNaturalLanguage = await this.resolveScriptReferences(request.naturalLanguage);
|
|
1374
1420
|
await executeAndroidLogin({
|
|
1375
1421
|
source: request,
|
|
1376
1422
|
deviceId: request.deviceId,
|
|
@@ -1393,7 +1439,7 @@ export class DebugWebSocketServer {
|
|
|
1393
1439
|
type: request.platform || 'android',
|
|
1394
1440
|
testName: createDebugReportGroupName(request),
|
|
1395
1441
|
actionType: 'aiAct',
|
|
1396
|
-
naturalLanguage:
|
|
1442
|
+
naturalLanguage: resolvedNaturalLanguage,
|
|
1397
1443
|
udid: request.deviceId,
|
|
1398
1444
|
packageName: request.packageName,
|
|
1399
1445
|
modelConfig: fullModelConfig,
|
|
@@ -1633,6 +1679,8 @@ export class DebugWebSocketServer {
|
|
|
1633
1679
|
}
|
|
1634
1680
|
try {
|
|
1635
1681
|
this.sendMessage(ws, { type: 'web_action_started', sessionId, actionType: request.actionType });
|
|
1682
|
+
const resolvedNaturalLanguage = await this.resolveScriptReferences(request.naturalLanguage);
|
|
1683
|
+
const resolvedActionParams = await this.resolveActionParamsReferences(request.actionParams);
|
|
1636
1684
|
// 始终启动请求抓包
|
|
1637
1685
|
try {
|
|
1638
1686
|
await whistleManager.startCapture(sessionId);
|
|
@@ -1860,7 +1908,7 @@ export class DebugWebSocketServer {
|
|
|
1860
1908
|
break;
|
|
1861
1909
|
}
|
|
1862
1910
|
case 'aiAction': {
|
|
1863
|
-
const text =
|
|
1911
|
+
const text = resolvedNaturalLanguage || request.locatePrompt || '';
|
|
1864
1912
|
if (!text)
|
|
1865
1913
|
throw new Error('aiAction requires naturalLanguage or locatePrompt');
|
|
1866
1914
|
result = await agent.aiAct(text);
|
|
@@ -1899,7 +1947,7 @@ export class DebugWebSocketServer {
|
|
|
1899
1947
|
}
|
|
1900
1948
|
case 'runCode': {
|
|
1901
1949
|
// 支持两种方式传递代码:request.code 或 request.actionParams.code
|
|
1902
|
-
const code =
|
|
1950
|
+
const code = resolvedActionParams?.code || request.code || '';
|
|
1903
1951
|
if (!code)
|
|
1904
1952
|
throw new Error('runCode requires actionParams.code');
|
|
1905
1953
|
// 行号探针:与真机 runCode 一致,通过 instrumentCode 在每条语句前插入 __lineCallback(line),
|
|
@@ -2286,7 +2334,12 @@ export class DebugWebSocketServer {
|
|
|
2286
2334
|
callbackOk: callbackFailures.length === 0,
|
|
2287
2335
|
callbackFailures: callbackFailures.slice(-10),
|
|
2288
2336
|
});
|
|
2289
|
-
|
|
2337
|
+
try {
|
|
2338
|
+
await this.continueNextLoopBatches(ws, request, finalizeCallback.data);
|
|
2339
|
+
}
|
|
2340
|
+
catch (error) {
|
|
2341
|
+
logger.error(`[BatchAction] Continue next loop batch failed after finalize: batchId=${batchId}, error=${error.message}`);
|
|
2342
|
+
}
|
|
2290
2343
|
}
|
|
2291
2344
|
}
|
|
2292
2345
|
getNextBatchRequests(callbackData) {
|
|
@@ -2342,14 +2395,16 @@ export class DebugWebSocketServer {
|
|
|
2342
2395
|
if (!targetUrl) {
|
|
2343
2396
|
throw new Error('Web 用例缺少测试地址,请先在用例中补充测试地址后再执行');
|
|
2344
2397
|
}
|
|
2398
|
+
const resolvedNaturalLanguage = await this.resolveScriptReferences(tc.naturalLanguage);
|
|
2399
|
+
const resolvedActionParams = await this.resolveActionParamsReferences(tc.actionParams);
|
|
2345
2400
|
// 构造单条 WebActionExecuteRequest
|
|
2346
2401
|
const webActionRequest = {
|
|
2347
2402
|
type: 'execute_web_action',
|
|
2348
2403
|
actionType: tc.actionType || 'aiAction',
|
|
2349
2404
|
url: targetUrl,
|
|
2350
2405
|
testUrl: targetUrl,
|
|
2351
|
-
naturalLanguage:
|
|
2352
|
-
actionParams:
|
|
2406
|
+
naturalLanguage: resolvedNaturalLanguage,
|
|
2407
|
+
actionParams: resolvedActionParams,
|
|
2353
2408
|
sessionId: sharedSessionId || undefined,
|
|
2354
2409
|
mobileMode: request.mobileMode,
|
|
2355
2410
|
deviceName: request.deviceName,
|
|
@@ -2525,7 +2580,7 @@ export class DebugWebSocketServer {
|
|
|
2525
2580
|
// 执行动作
|
|
2526
2581
|
const actionType = tc.actionType || 'aiAction';
|
|
2527
2582
|
if (actionType === 'aiAction' || actionType === 'aiAct') {
|
|
2528
|
-
const text =
|
|
2583
|
+
const text = resolvedNaturalLanguage || '';
|
|
2529
2584
|
if (!text)
|
|
2530
2585
|
throw new Error('aiAction requires naturalLanguage');
|
|
2531
2586
|
await agent.aiAct(text);
|
|
@@ -2536,7 +2591,7 @@ export class DebugWebSocketServer {
|
|
|
2536
2591
|
await this.safeWebGoto(page, targetUrl, `${caseLabel} goto`);
|
|
2537
2592
|
}
|
|
2538
2593
|
else if (actionType === 'runCode') {
|
|
2539
|
-
const code =
|
|
2594
|
+
const code = resolvedActionParams?.code || '';
|
|
2540
2595
|
if (!code)
|
|
2541
2596
|
throw new Error('runCode requires actionParams.code');
|
|
2542
2597
|
const AsyncFunctionCtor = async function () { }.constructor;
|
|
@@ -2631,15 +2686,17 @@ export class DebugWebSocketServer {
|
|
|
2631
2686
|
catch (captureErr) {
|
|
2632
2687
|
logger.warn(`[BatchAction] Failed to start capture: ${captureErr.message}`);
|
|
2633
2688
|
}
|
|
2689
|
+
const resolvedNaturalLanguage = await this.resolveScriptReferences(tc.naturalLanguage);
|
|
2690
|
+
const resolvedActionParams = await this.resolveActionParamsReferences(tc.actionParams);
|
|
2634
2691
|
// 构造 ExecuteConfig
|
|
2635
2692
|
const actionType = tc.actionType || 'aiAct';
|
|
2636
2693
|
const config = {
|
|
2637
2694
|
type: platform,
|
|
2638
2695
|
testName: tc.title || tc.id || `batch-${batchId}-case-${caseIndex + 1}`,
|
|
2639
2696
|
actionType,
|
|
2640
|
-
naturalLanguage:
|
|
2641
|
-
locatePrompt:
|
|
2642
|
-
actionParams:
|
|
2697
|
+
naturalLanguage: resolvedNaturalLanguage,
|
|
2698
|
+
locatePrompt: resolvedActionParams?.locatePrompt,
|
|
2699
|
+
actionParams: resolvedActionParams,
|
|
2643
2700
|
udid: effectiveDeviceId,
|
|
2644
2701
|
packageName: effectivePackageName,
|
|
2645
2702
|
bundleId: effectiveBundleId,
|
|
@@ -3083,7 +3140,7 @@ export class DebugWebSocketServer {
|
|
|
3083
3140
|
const token = request.callback?.token?.trim();
|
|
3084
3141
|
if (token)
|
|
3085
3142
|
headers.Authorization = token.startsWith('Bearer ') ? token : `Bearer ${token}`;
|
|
3086
|
-
const timeoutMs = Math.max(1000, Math.min(Number(request.callback?.timeoutMs || 10000) || 10000,
|
|
3143
|
+
const timeoutMs = Math.max(1000, Math.min(Number(request.callback?.timeoutMs || 10000) || 10000, 600000));
|
|
3087
3144
|
const maxAttempts = 2;
|
|
3088
3145
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
3089
3146
|
const controller = new AbortController();
|