@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.
- package/dist/api/callback.d.ts +5 -0
- package/dist/api/callback.d.ts.map +1 -1
- package/dist/api/callback.js +79 -0
- package/dist/api/callback.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/debug/types.d.ts +33 -0
- package/dist/debug/types.d.ts.map +1 -1
- package/dist/debug/websocket-server.d.ts +10 -0
- package/dist/debug/websocket-server.d.ts.map +1 -1
- package/dist/debug/websocket-server.js +332 -1
- package/dist/debug/websocket-server.js.map +1 -1
- package/dist/executor/android-executor.d.ts +20 -0
- package/dist/executor/android-executor.d.ts.map +1 -1
- package/dist/executor/android-executor.js +114 -0
- package/dist/executor/android-executor.js.map +1 -1
- package/dist/proxy/whistle-manager.d.ts +127 -0
- package/dist/proxy/whistle-manager.d.ts.map +1 -0
- package/dist/proxy/whistle-manager.js +374 -0
- package/dist/proxy/whistle-manager.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 +48 -1
- package/dist/task/scheduler.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export interface HostMapping {
|
|
2
|
+
domain: string;
|
|
3
|
+
ip: string;
|
|
4
|
+
}
|
|
5
|
+
/** Whistle 原始请求项(/cgi-bin/get-data 返回) */
|
|
6
|
+
export interface WhistleRawRequest {
|
|
7
|
+
id: string;
|
|
8
|
+
url: string;
|
|
9
|
+
startTime: number;
|
|
10
|
+
endTime: number;
|
|
11
|
+
dnsTime?: number;
|
|
12
|
+
requestTime?: number;
|
|
13
|
+
responseTime?: number;
|
|
14
|
+
req: {
|
|
15
|
+
method: string;
|
|
16
|
+
httpVersion?: string;
|
|
17
|
+
ip?: string;
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
body?: string;
|
|
20
|
+
base64?: string;
|
|
21
|
+
};
|
|
22
|
+
res: {
|
|
23
|
+
statusCode?: number;
|
|
24
|
+
statusMessage?: string;
|
|
25
|
+
headers?: Record<string, string>;
|
|
26
|
+
body?: string;
|
|
27
|
+
base64?: string;
|
|
28
|
+
};
|
|
29
|
+
clientId?: string;
|
|
30
|
+
}
|
|
31
|
+
/** 精简后的捕获请求(推送给前端 / AI 分析用) */
|
|
32
|
+
export interface CapturedRequest {
|
|
33
|
+
id: string;
|
|
34
|
+
url: string;
|
|
35
|
+
method: string;
|
|
36
|
+
statusCode: number | null;
|
|
37
|
+
reqHeaders: Record<string, string>;
|
|
38
|
+
resHeaders: Record<string, string>;
|
|
39
|
+
reqBody: string | null;
|
|
40
|
+
resBody: string | null;
|
|
41
|
+
clientIp: string | null;
|
|
42
|
+
startTime: number;
|
|
43
|
+
endTime: number;
|
|
44
|
+
duration: number;
|
|
45
|
+
dnsTime?: number;
|
|
46
|
+
requestTime?: number;
|
|
47
|
+
responseTime?: number;
|
|
48
|
+
}
|
|
49
|
+
/** 请求抓包选项 */
|
|
50
|
+
export interface CaptureOptions {
|
|
51
|
+
/** 起始请求ID(从此ID之后开始获取) */
|
|
52
|
+
sinceId?: string;
|
|
53
|
+
/** 获取数量(默认100,最大100) */
|
|
54
|
+
count?: number;
|
|
55
|
+
/** 按客户端IP过滤 */
|
|
56
|
+
clientIp?: string;
|
|
57
|
+
/** 按URL关键词过滤 */
|
|
58
|
+
urlKeyword?: string;
|
|
59
|
+
}
|
|
60
|
+
export declare class WhistleRuleManager {
|
|
61
|
+
private readonly apiBase;
|
|
62
|
+
private readonly clientId;
|
|
63
|
+
/** 活跃的抓包会话:sessionId -> CaptureSession */
|
|
64
|
+
private captureSessions;
|
|
65
|
+
constructor();
|
|
66
|
+
private formatRulesText;
|
|
67
|
+
createRuleGroup(ruleGroupName: string, hostMappings: HostMapping[], customApiBase?: string): Promise<boolean>;
|
|
68
|
+
removeRuleGroup(ruleGroupName: string, customApiBase?: string): Promise<boolean>;
|
|
69
|
+
generateRuleGroupName(environmentId: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* 获取 Whistle 当前最新的请求ID
|
|
72
|
+
* 调试开始前调用,记录起始位置,后续增量获取从此ID之后开始
|
|
73
|
+
*/
|
|
74
|
+
getLastDataId(customApiBase?: string): Promise<string | null>;
|
|
75
|
+
/**
|
|
76
|
+
* 增量获取请求列表
|
|
77
|
+
* @param options 抓包选项(sinceId、count、clientIp、urlKeyword)
|
|
78
|
+
* @returns 捕获请求列表 + 本批最后一条ID
|
|
79
|
+
*/
|
|
80
|
+
fetchRequestList(options?: CaptureOptions, customApiBase?: string): Promise<{
|
|
81
|
+
requests: CapturedRequest[];
|
|
82
|
+
lastId: string | null;
|
|
83
|
+
}>;
|
|
84
|
+
/**
|
|
85
|
+
* 按ID获取请求详情(含完整 req/res body)
|
|
86
|
+
* 适用于需要获取完整 body 的场景(/cgi-bin/get-data 默认不返回 body)
|
|
87
|
+
*/
|
|
88
|
+
fetchRequestDetail(ids: string[], customApiBase?: string): Promise<CapturedRequest[]>;
|
|
89
|
+
/**
|
|
90
|
+
* 开始抓包会话
|
|
91
|
+
* 记录当前最新请求ID,后续增量获取从此ID之后开始
|
|
92
|
+
* @param sessionId 调试会话ID
|
|
93
|
+
* @param clientIp 可选,设备IP(用于过滤只看该设备的请求)
|
|
94
|
+
* @param pollInterval 轮询间隔(毫秒),默认3000
|
|
95
|
+
*/
|
|
96
|
+
startCapture(sessionId: string, clientIp?: string, pollInterval?: number, proxyAccount?: string): Promise<boolean>;
|
|
97
|
+
/**
|
|
98
|
+
* 停止抓包会话,返回所有采集到的请求
|
|
99
|
+
* @param sessionId 调试会话ID
|
|
100
|
+
* @returns 采集到的请求列表
|
|
101
|
+
*/
|
|
102
|
+
stopCapture(sessionId: string): Promise<CapturedRequest[]>;
|
|
103
|
+
/**
|
|
104
|
+
* 启动定时轮询采集(调试期间持续采集请求)
|
|
105
|
+
* @param sessionId 调试会话ID
|
|
106
|
+
* @param onNewRequests 每次轮询到新请求时的回调
|
|
107
|
+
*/
|
|
108
|
+
startPolling(sessionId: string, onNewRequests?: (requests: CapturedRequest[]) => void): void;
|
|
109
|
+
/**
|
|
110
|
+
* 停止定时轮询
|
|
111
|
+
*/
|
|
112
|
+
stopPolling(sessionId: string): void;
|
|
113
|
+
/**
|
|
114
|
+
* 获取当前抓包会话已采集的请求数量
|
|
115
|
+
*/
|
|
116
|
+
getCapturedCount(sessionId: string): number;
|
|
117
|
+
/**
|
|
118
|
+
* 获取当前抓包会话已采集的所有请求
|
|
119
|
+
*/
|
|
120
|
+
getCapturedRequests(sessionId: string): CapturedRequest[];
|
|
121
|
+
/**
|
|
122
|
+
* 将 Whistle 原始请求转换为精简的 CapturedRequest
|
|
123
|
+
*/
|
|
124
|
+
private convertRawRequest;
|
|
125
|
+
}
|
|
126
|
+
export declare const whistleManager: WhistleRuleManager;
|
|
127
|
+
//# sourceMappingURL=whistle-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whistle-manager.d.ts","sourceRoot":"","sources":["../../src/proxy/whistle-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { createLogger } from '../core/logger.js';
|
|
2
|
+
const logger = createLogger('WhistleManager');
|
|
3
|
+
export class WhistleRuleManager {
|
|
4
|
+
apiBase;
|
|
5
|
+
clientId;
|
|
6
|
+
/** 活跃的抓包会话:sessionId -> CaptureSession */
|
|
7
|
+
captureSessions = new Map();
|
|
8
|
+
constructor() {
|
|
9
|
+
this.apiBase = process.env.WHISTLE_API_BASE || 'http://127.0.0.1:8899';
|
|
10
|
+
this.clientId = process.env.WHISTLE_CLIENT_ID || 'guada-ai-framework';
|
|
11
|
+
}
|
|
12
|
+
// ==================== 规则管理(已有功能) ====================
|
|
13
|
+
formatRulesText(hostMappings) {
|
|
14
|
+
return hostMappings.map(m => m.ip + ' ' + m.domain).join('\n');
|
|
15
|
+
}
|
|
16
|
+
async createRuleGroup(ruleGroupName, hostMappings, customApiBase) {
|
|
17
|
+
try {
|
|
18
|
+
const apiBase = customApiBase || this.apiBase;
|
|
19
|
+
const addParams = new URLSearchParams();
|
|
20
|
+
addParams.append('clientId', this.clientId);
|
|
21
|
+
addParams.append('name', ruleGroupName);
|
|
22
|
+
addParams.append('addToTop', '');
|
|
23
|
+
const addResponse = await fetch(apiBase + '/cgi-bin/rules/add', {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
26
|
+
body: addParams.toString(),
|
|
27
|
+
});
|
|
28
|
+
const addResult = await addResponse.json();
|
|
29
|
+
if (addResult.ec !== 0) {
|
|
30
|
+
logger.error('Failed to create rule group: ' + JSON.stringify(addResult));
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
logger.info('Rule group [' + ruleGroupName + '] created');
|
|
34
|
+
const rulesText = this.formatRulesText(hostMappings);
|
|
35
|
+
const selectParams = new URLSearchParams();
|
|
36
|
+
selectParams.append('clientId', this.clientId);
|
|
37
|
+
selectParams.append('key', 'w-reactkey-' + Date.now());
|
|
38
|
+
selectParams.append('name', ruleGroupName);
|
|
39
|
+
selectParams.append('value', rulesText);
|
|
40
|
+
selectParams.append('hide', 'false');
|
|
41
|
+
selectParams.append('active', 'true');
|
|
42
|
+
selectParams.append('changed', 'true');
|
|
43
|
+
const selectResponse = await fetch(apiBase + '/cgi-bin/rules/select', {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
46
|
+
body: selectParams.toString(),
|
|
47
|
+
});
|
|
48
|
+
const selectResult = await selectResponse.json();
|
|
49
|
+
if (selectResult.ec !== 0) {
|
|
50
|
+
logger.error('Failed to write rule content: ' + JSON.stringify(selectResult));
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
logger.info('Rule group [' + ruleGroupName + '] wrote ' + hostMappings.length + ' host mappings');
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
logger.error('Error creating rule group [' + ruleGroupName + ']: ' + (error instanceof Error ? error.message : String(error)));
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async removeRuleGroup(ruleGroupName, customApiBase) {
|
|
62
|
+
try {
|
|
63
|
+
const apiBase = customApiBase || this.apiBase;
|
|
64
|
+
const params = new URLSearchParams();
|
|
65
|
+
params.append('clientId', this.clientId);
|
|
66
|
+
params.append('list[]', ruleGroupName);
|
|
67
|
+
const response = await fetch(apiBase + '/cgi-bin/rules/remove', {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
70
|
+
body: params.toString(),
|
|
71
|
+
});
|
|
72
|
+
const result = await response.json();
|
|
73
|
+
if (result.ec !== 0) {
|
|
74
|
+
logger.error('Failed to remove rule group: ' + JSON.stringify(result));
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
logger.info('Rule group [' + ruleGroupName + '] removed');
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
logger.error('Error removing rule group [' + ruleGroupName + ']: ' + (error instanceof Error ? error.message : String(error)));
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
generateRuleGroupName(environmentId) {
|
|
86
|
+
return 'guada-env-' + environmentId;
|
|
87
|
+
}
|
|
88
|
+
// ==================== 请求抓包(新增功能) ====================
|
|
89
|
+
/**
|
|
90
|
+
* 获取 Whistle 当前最新的请求ID
|
|
91
|
+
* 调试开始前调用,记录起始位置,后续增量获取从此ID之后开始
|
|
92
|
+
*/
|
|
93
|
+
async getLastDataId(customApiBase) {
|
|
94
|
+
try {
|
|
95
|
+
const apiBase = customApiBase || this.apiBase;
|
|
96
|
+
const url = apiBase + '/cgi-bin/get-data?count=1';
|
|
97
|
+
const response = await fetch(url);
|
|
98
|
+
const result = await response.json();
|
|
99
|
+
if (result.ec !== 0 || !result.data) {
|
|
100
|
+
logger.warn('Failed to get last data ID: ec=' + result.ec);
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const lastId = result.data.endId || result.data.lastId || null;
|
|
104
|
+
logger.info('Last data ID: ' + lastId);
|
|
105
|
+
return lastId;
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
logger.error('Error getting last data ID: ' + (error instanceof Error ? error.message : String(error)));
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 增量获取请求列表
|
|
114
|
+
* @param options 抓包选项(sinceId、count、clientIp、urlKeyword)
|
|
115
|
+
* @returns 捕获请求列表 + 本批最后一条ID
|
|
116
|
+
*/
|
|
117
|
+
async fetchRequestList(options = {}, customApiBase) {
|
|
118
|
+
try {
|
|
119
|
+
const count = Math.min(options.count || 100, 100);
|
|
120
|
+
const params = new URLSearchParams();
|
|
121
|
+
params.append('count', String(count));
|
|
122
|
+
if (options.sinceId) {
|
|
123
|
+
params.append('startTime', options.sinceId);
|
|
124
|
+
}
|
|
125
|
+
if (options.clientIp) {
|
|
126
|
+
params.append('ip', options.clientIp);
|
|
127
|
+
}
|
|
128
|
+
if (options.urlKeyword) {
|
|
129
|
+
params.append('url', options.urlKeyword);
|
|
130
|
+
}
|
|
131
|
+
const apiBase = customApiBase || this.apiBase;
|
|
132
|
+
const url = apiBase + '/cgi-bin/get-data?' + params.toString();
|
|
133
|
+
const response = await fetch(url);
|
|
134
|
+
const result = await response.json();
|
|
135
|
+
if (result.ec !== 0 || !result.data || !result.data.data) {
|
|
136
|
+
logger.warn('Failed to fetch request list: ec=' + result.ec);
|
|
137
|
+
return { requests: [], lastId: null };
|
|
138
|
+
}
|
|
139
|
+
const rawData = result.data.data;
|
|
140
|
+
const requests = [];
|
|
141
|
+
for (const id of (result.data.newIds || [])) {
|
|
142
|
+
const raw = rawData[id];
|
|
143
|
+
if (raw) {
|
|
144
|
+
requests.push(this.convertRawRequest(raw));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// 按startTime排序(ID本身就是时间戳格式,所以按ID排序即可)
|
|
148
|
+
requests.sort((a, b) => a.startTime - b.startTime);
|
|
149
|
+
const lastId = result.data.lastId || result.data.endId || null;
|
|
150
|
+
return { requests, lastId };
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
logger.error('Error fetching request list: ' + (error instanceof Error ? error.message : String(error)));
|
|
154
|
+
return { requests: [], lastId: null };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 按ID获取请求详情(含完整 req/res body)
|
|
159
|
+
* 适用于需要获取完整 body 的场景(/cgi-bin/get-data 默认不返回 body)
|
|
160
|
+
*/
|
|
161
|
+
async fetchRequestDetail(ids, customApiBase) {
|
|
162
|
+
if (ids.length === 0)
|
|
163
|
+
return [];
|
|
164
|
+
try {
|
|
165
|
+
const params = new URLSearchParams();
|
|
166
|
+
// get-session 要求 reqList 和 resList 为 JSON 数组字符串
|
|
167
|
+
params.append('reqList', JSON.stringify(ids));
|
|
168
|
+
params.append('resList', JSON.stringify(ids));
|
|
169
|
+
const apiBase = customApiBase || this.apiBase;
|
|
170
|
+
const response = await fetch(apiBase + '/cgi-bin/get-session?' + params.toString());
|
|
171
|
+
const result = await response.json();
|
|
172
|
+
// get-session 直接返回数据对象(无 ec 字段),空对象表示无数据
|
|
173
|
+
if (!result || typeof result !== 'object' || Object.keys(result).length === 0) {
|
|
174
|
+
logger.warn('Failed to fetch request detail: empty response');
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
const requests = [];
|
|
178
|
+
for (const id of ids) {
|
|
179
|
+
const raw = result[id];
|
|
180
|
+
if (raw) {
|
|
181
|
+
requests.push(this.convertRawRequest(raw));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return requests;
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
logger.error('Error fetching request detail: ' + (error instanceof Error ? error.message : String(error)));
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 开始抓包会话
|
|
193
|
+
* 记录当前最新请求ID,后续增量获取从此ID之后开始
|
|
194
|
+
* @param sessionId 调试会话ID
|
|
195
|
+
* @param clientIp 可选,设备IP(用于过滤只看该设备的请求)
|
|
196
|
+
* @param pollInterval 轮询间隔(毫秒),默认3000
|
|
197
|
+
*/
|
|
198
|
+
async startCapture(sessionId, clientIp, pollInterval = 3000, proxyAccount) {
|
|
199
|
+
// 如果已有抓包会话,先停止
|
|
200
|
+
if (this.captureSessions.has(sessionId)) {
|
|
201
|
+
this.stopCapture(sessionId);
|
|
202
|
+
}
|
|
203
|
+
// 根据 proxyAccount 决定使用哪个 Whistle 实例
|
|
204
|
+
const captureApiBase = proxyAccount ? `http://proxy-pc.jd.com/account/${proxyAccount}` : this.apiBase;
|
|
205
|
+
// 获取当前最新请求ID作为起始点
|
|
206
|
+
const lastId = await this.getLastDataId(captureApiBase);
|
|
207
|
+
if (lastId === null) {
|
|
208
|
+
logger.warn('[Capture] Failed to get start data ID, using empty string as fallback');
|
|
209
|
+
}
|
|
210
|
+
const captureSession = {
|
|
211
|
+
startDataId: lastId || '',
|
|
212
|
+
sessionId,
|
|
213
|
+
clientIp,
|
|
214
|
+
capturedRequests: [],
|
|
215
|
+
pollTimer: null,
|
|
216
|
+
pollInterval,
|
|
217
|
+
apiBase: captureApiBase,
|
|
218
|
+
};
|
|
219
|
+
this.captureSessions.set(sessionId, captureSession);
|
|
220
|
+
logger.info('[Capture] Started capture session: sessionId=' + sessionId + ', startDataId=' + lastId + ', clientIp=' + (clientIp || 'all'));
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* 停止抓包会话,返回所有采集到的请求
|
|
225
|
+
* @param sessionId 调试会话ID
|
|
226
|
+
* @returns 采集到的请求列表
|
|
227
|
+
*/
|
|
228
|
+
async stopCapture(sessionId) {
|
|
229
|
+
const capture = this.captureSessions.get(sessionId);
|
|
230
|
+
if (!capture) {
|
|
231
|
+
logger.warn('[Capture] No capture session found: sessionId=' + sessionId);
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
// 停止轮询
|
|
235
|
+
if (capture.pollTimer) {
|
|
236
|
+
clearInterval(capture.pollTimer);
|
|
237
|
+
capture.pollTimer = null;
|
|
238
|
+
}
|
|
239
|
+
// 最后一次拉取增量数据
|
|
240
|
+
try {
|
|
241
|
+
const lastId = capture.capturedRequests.length > 0
|
|
242
|
+
? capture.capturedRequests[capture.capturedRequests.length - 1].id
|
|
243
|
+
: capture.startDataId;
|
|
244
|
+
const { requests } = await this.fetchRequestList({
|
|
245
|
+
sinceId: lastId,
|
|
246
|
+
clientIp: capture.clientIp,
|
|
247
|
+
count: 100,
|
|
248
|
+
}, capture.apiBase);
|
|
249
|
+
if (requests.length > 0) {
|
|
250
|
+
capture.capturedRequests.push(...requests);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
logger.warn('[Capture] Final fetch failed: ' + (error instanceof Error ? error.message : String(error)));
|
|
255
|
+
}
|
|
256
|
+
// 清理
|
|
257
|
+
this.captureSessions.delete(sessionId);
|
|
258
|
+
logger.info('[Capture] Stopped capture session: sessionId=' + sessionId + ', totalRequests=' + capture.capturedRequests.length);
|
|
259
|
+
return capture.capturedRequests;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* 启动定时轮询采集(调试期间持续采集请求)
|
|
263
|
+
* @param sessionId 调试会话ID
|
|
264
|
+
* @param onNewRequests 每次轮询到新请求时的回调
|
|
265
|
+
*/
|
|
266
|
+
startPolling(sessionId, onNewRequests) {
|
|
267
|
+
const capture = this.captureSessions.get(sessionId);
|
|
268
|
+
if (!capture) {
|
|
269
|
+
logger.warn('[Capture] Cannot start polling, no capture session: sessionId=' + sessionId);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (capture.pollTimer) {
|
|
273
|
+
clearInterval(capture.pollTimer);
|
|
274
|
+
}
|
|
275
|
+
capture.pollTimer = setInterval(async () => {
|
|
276
|
+
try {
|
|
277
|
+
const lastId = capture.capturedRequests.length > 0
|
|
278
|
+
? capture.capturedRequests[capture.capturedRequests.length - 1].id
|
|
279
|
+
: capture.startDataId;
|
|
280
|
+
const { requests } = await this.fetchRequestList({
|
|
281
|
+
sinceId: lastId,
|
|
282
|
+
clientIp: capture.clientIp,
|
|
283
|
+
count: 100,
|
|
284
|
+
}, capture.apiBase);
|
|
285
|
+
if (requests.length > 0) {
|
|
286
|
+
capture.capturedRequests.push(...requests);
|
|
287
|
+
if (onNewRequests) {
|
|
288
|
+
onNewRequests(requests);
|
|
289
|
+
}
|
|
290
|
+
logger.debug('[Capture] Polling got ' + requests.length + ' new requests for session ' + sessionId);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
logger.warn('[Capture] Polling error: ' + (error instanceof Error ? error.message : String(error)));
|
|
295
|
+
}
|
|
296
|
+
}, capture.pollInterval);
|
|
297
|
+
logger.info('[Capture] Polling started: sessionId=' + sessionId + ', interval=' + capture.pollInterval + 'ms');
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 停止定时轮询
|
|
301
|
+
*/
|
|
302
|
+
stopPolling(sessionId) {
|
|
303
|
+
const capture = this.captureSessions.get(sessionId);
|
|
304
|
+
if (capture && capture.pollTimer) {
|
|
305
|
+
clearInterval(capture.pollTimer);
|
|
306
|
+
capture.pollTimer = null;
|
|
307
|
+
logger.info('[Capture] Polling stopped: sessionId=' + sessionId);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* 获取当前抓包会话已采集的请求数量
|
|
312
|
+
*/
|
|
313
|
+
getCapturedCount(sessionId) {
|
|
314
|
+
const capture = this.captureSessions.get(sessionId);
|
|
315
|
+
return capture ? capture.capturedRequests.length : 0;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* 获取当前抓包会话已采集的所有请求
|
|
319
|
+
*/
|
|
320
|
+
getCapturedRequests(sessionId) {
|
|
321
|
+
const capture = this.captureSessions.get(sessionId);
|
|
322
|
+
return capture ? [...capture.capturedRequests] : [];
|
|
323
|
+
}
|
|
324
|
+
// ==================== 内部工具方法 ====================
|
|
325
|
+
/**
|
|
326
|
+
* 将 Whistle 原始请求转换为精简的 CapturedRequest
|
|
327
|
+
*/
|
|
328
|
+
convertRawRequest(raw) {
|
|
329
|
+
// base64 body 解码
|
|
330
|
+
let reqBody = null;
|
|
331
|
+
if (raw.req.base64) {
|
|
332
|
+
try {
|
|
333
|
+
reqBody = Buffer.from(raw.req.base64, 'base64').toString('utf-8');
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
reqBody = '[binary data]';
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else if (raw.req.body) {
|
|
340
|
+
reqBody = raw.req.body;
|
|
341
|
+
}
|
|
342
|
+
let resBody = null;
|
|
343
|
+
if (raw.res.base64) {
|
|
344
|
+
try {
|
|
345
|
+
resBody = Buffer.from(raw.res.base64, 'base64').toString('utf-8');
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
resBody = '[binary data]';
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
else if (raw.res.body) {
|
|
352
|
+
resBody = raw.res.body;
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
id: raw.id,
|
|
356
|
+
url: raw.url,
|
|
357
|
+
method: raw.req.method,
|
|
358
|
+
statusCode: raw.res.statusCode ?? null,
|
|
359
|
+
reqHeaders: raw.req.headers || {},
|
|
360
|
+
resHeaders: raw.res.headers || {},
|
|
361
|
+
reqBody,
|
|
362
|
+
resBody,
|
|
363
|
+
clientIp: raw.req.ip || null,
|
|
364
|
+
startTime: raw.startTime,
|
|
365
|
+
endTime: raw.endTime,
|
|
366
|
+
duration: raw.endTime - raw.startTime,
|
|
367
|
+
dnsTime: raw.dnsTime,
|
|
368
|
+
requestTime: raw.requestTime,
|
|
369
|
+
responseTime: raw.responseTime,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
export const whistleManager = new WhistleRuleManager();
|
|
374
|
+
//# sourceMappingURL=whistle-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whistle-manager.js","sourceRoot":"","sources":["../../src/proxy/whistle-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/task/scheduler.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/task/scheduler.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC7F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD,qBAAa,aAAa;IACxB,OAAO,CAAC,YAAY,CAAwC;
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/task/scheduler.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC7F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD,qBAAa,aAAa;IACxB,OAAO,CAAC,YAAY,CAAwC;IAE5D,OAAO,CAAC,qBAAqB,CAAkC;IAC/D,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,WAAW,CAAC,CAAc;IAE5B,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAS7H,aAAa,IAAI,OAAO;IAIxB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;YASZ,WAAW;IA8IzB,OAAO,CAAC,gBAAgB;IAgCxB,OAAO,CAAC,mBAAmB;IAkL3B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAsBvB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IA4BjC,QAAQ,IAAI,cAAc;IAUpB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ5B"}
|
package/dist/task/scheduler.js
CHANGED
|
@@ -3,9 +3,12 @@ import { createLogger } from '../core/logger.js';
|
|
|
3
3
|
import { eventBus } from '../core/event-bus.js';
|
|
4
4
|
import { taskRepo } from '../storage/repositories/task-repo.js';
|
|
5
5
|
import { executionLogRepo } from '../storage/repositories/execution-log-repo.js';
|
|
6
|
+
import { whistleManager } from '../proxy/whistle-manager.js';
|
|
6
7
|
const logger = createLogger('TaskScheduler');
|
|
7
8
|
export class TaskScheduler {
|
|
8
9
|
runningTasks = new Map();
|
|
10
|
+
// Track Whistle rule group names per task for cleanup
|
|
11
|
+
whistleRuleGroupNames = new Map();
|
|
9
12
|
maxConcurrent = 50;
|
|
10
13
|
config;
|
|
11
14
|
callbackConfig;
|
|
@@ -29,13 +32,35 @@ export class TaskScheduler {
|
|
|
29
32
|
}
|
|
30
33
|
this.executeTask(task);
|
|
31
34
|
}
|
|
32
|
-
executeTask(task) {
|
|
35
|
+
async executeTask(task) {
|
|
33
36
|
taskRepo.updateStatus(task.taskId, 'running');
|
|
34
37
|
eventBus.emitEvent('task:started', 'scheduler', { taskId: task.taskId, type: task.type });
|
|
35
38
|
// 参考项目不在启动时通知服务器,只在完成/失败时通知
|
|
36
39
|
// 移除了: updateTaskStatusToServer(task.taskId, true, ...)
|
|
37
40
|
// Build executor config
|
|
38
41
|
const config = this.buildExecutorConfig(task);
|
|
42
|
+
// Setup Whistle proxy rules if hostMappings are present in config
|
|
43
|
+
const hostMappings = config.hostMappings;
|
|
44
|
+
if (hostMappings && hostMappings.length > 0) {
|
|
45
|
+
try {
|
|
46
|
+
const environmentId = config.environmentId || task.taskId;
|
|
47
|
+
const ruleGroupName = whistleManager.generateRuleGroupName(environmentId);
|
|
48
|
+
const success = await whistleManager.createRuleGroup(ruleGroupName, hostMappings);
|
|
49
|
+
if (success) {
|
|
50
|
+
this.whistleRuleGroupNames.set(task.taskId, ruleGroupName);
|
|
51
|
+
// Ensure proxyPort is set (default 8899)
|
|
52
|
+
if (!config.proxyPort)
|
|
53
|
+
config.proxyPort = 8899;
|
|
54
|
+
logger.info(`Whistle proxy setup for task ${task.taskId}: ruleGroup=${ruleGroupName}, proxyPort=${config.proxyPort}`);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
logger.warn(`Whistle proxy setup failed for task ${task.taskId}, continuing without proxy`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
logger.warn(`Whistle proxy setup error for task ${task.taskId}: ${err instanceof Error ? err.message : String(err)}, continuing without proxy`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
39
64
|
logger.info(`Executing task ${task.taskId} with config: ${JSON.stringify(config)?.substring(0, 500)}`);
|
|
40
65
|
// Fork a child process to run the executor
|
|
41
66
|
const workerPath = new URL('../executor/worker-entry.js', import.meta.url).pathname;
|
|
@@ -141,6 +166,15 @@ export class TaskScheduler {
|
|
|
141
166
|
}
|
|
142
167
|
handleTaskResult(task, result) {
|
|
143
168
|
const status = result.success ? 'completed' : 'failed';
|
|
169
|
+
// Teardown Whistle proxy rules if they were set
|
|
170
|
+
const ruleGroupName = this.whistleRuleGroupNames.get(task.taskId);
|
|
171
|
+
if (ruleGroupName) {
|
|
172
|
+
this.whistleRuleGroupNames.delete(task.taskId);
|
|
173
|
+
whistleManager.removeRuleGroup(ruleGroupName).catch((err) => {
|
|
174
|
+
logger.warn(`Failed to teardown Whistle proxy for task ${task.taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
175
|
+
});
|
|
176
|
+
logger.info(`Whistle proxy teardown for task ${task.taskId}: ruleGroup=${ruleGroupName}`);
|
|
177
|
+
}
|
|
144
178
|
taskRepo.updateStatus(task.taskId, status, result.errorMessage);
|
|
145
179
|
// Emit event with full result data (including reportFile if available)
|
|
146
180
|
// This triggers report upload and status update in index.ts event listeners
|
|
@@ -307,6 +341,19 @@ export class TaskScheduler {
|
|
|
307
341
|
if (config.mobileMode !== undefined)
|
|
308
342
|
baseConfig.mobileMode = config.mobileMode;
|
|
309
343
|
// Merge remaining params
|
|
344
|
+
// Environment / Proxy specific — 从 config 中提取环境代理参数
|
|
345
|
+
if (config.proxyPort)
|
|
346
|
+
baseConfig.proxyPort = config.proxyPort;
|
|
347
|
+
if (config.hostMappings)
|
|
348
|
+
baseConfig.hostMappings = config.hostMappings;
|
|
349
|
+
if (config.environmentId)
|
|
350
|
+
baseConfig.environmentId = config.environmentId;
|
|
351
|
+
if (params.proxyPort)
|
|
352
|
+
baseConfig.proxyPort = params.proxyPort;
|
|
353
|
+
if (params.hostMappings)
|
|
354
|
+
baseConfig.hostMappings = params.hostMappings;
|
|
355
|
+
if (params.environmentId)
|
|
356
|
+
baseConfig.environmentId = params.environmentId;
|
|
310
357
|
Object.assign(baseConfig, params);
|
|
311
358
|
// After Object.assign, force action type if runCode was detected
|
|
312
359
|
// (Object.assign may have overwritten our type with the original type)
|