benchmark-collector 1.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.
- package/dist/scripts/collect.js +20 -0
- package/dist/src/collector.d.ts +48 -0
- package/dist/src/collector.js +373 -0
- package/dist/src/collector.js.map +1 -0
- package/dist/src/dom-capture.d.ts +7 -0
- package/dist/src/dom-capture.js +96 -0
- package/dist/src/dom-capture.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +12 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/som-annotator.d.ts +9 -0
- package/dist/src/som-annotator.js +108 -0
- package/dist/src/som-annotator.js.map +1 -0
- package/dist/src/types.d.ts +52 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const collector_1 = require("../src/collector");
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
function getArg(name, defaultValue) {
|
|
7
|
+
const idx = args.indexOf(`--${name}`);
|
|
8
|
+
return idx >= 0 && args[idx + 1] ? args[idx + 1] : defaultValue;
|
|
9
|
+
}
|
|
10
|
+
const task = getArg('task', '未命名任务');
|
|
11
|
+
const url = getArg('url', 'https://www.example.com');
|
|
12
|
+
console.log(`Benchmark 数据采集器`);
|
|
13
|
+
console.log(` 任务: ${task}`);
|
|
14
|
+
console.log(` URL: ${url}`);
|
|
15
|
+
const collector = new collector_1.BenchmarkCollector(task, url);
|
|
16
|
+
collector.start().catch((err) => {
|
|
17
|
+
console.error('采集出错:', err);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=collect.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { BrowserContext, Page } from 'playwright-core';
|
|
2
|
+
import { StepRecord } from './types';
|
|
3
|
+
export declare class BenchmarkCollector {
|
|
4
|
+
private task;
|
|
5
|
+
private startUrl;
|
|
6
|
+
private session;
|
|
7
|
+
private outputDir;
|
|
8
|
+
private stepsDir;
|
|
9
|
+
private stepCounter;
|
|
10
|
+
private allCodeLines;
|
|
11
|
+
private context;
|
|
12
|
+
private activePage;
|
|
13
|
+
private stopped;
|
|
14
|
+
private cdpSessionMap;
|
|
15
|
+
private captureQueue;
|
|
16
|
+
private processing;
|
|
17
|
+
private queueDone;
|
|
18
|
+
private queueDoneResolve;
|
|
19
|
+
constructor(task: string, startUrl: string, outputBase?: string);
|
|
20
|
+
get sessionId(): string;
|
|
21
|
+
get outputPath(): string;
|
|
22
|
+
get steps(): StepRecord[];
|
|
23
|
+
start(): Promise<void>;
|
|
24
|
+
/** 非交互式模式,用于测试 */
|
|
25
|
+
startHeadless(options?: {
|
|
26
|
+
headless?: boolean;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
browser: import("playwright-core").Browser;
|
|
29
|
+
context: BrowserContext;
|
|
30
|
+
page: Page;
|
|
31
|
+
}>;
|
|
32
|
+
/** 手动触发一次采集 */
|
|
33
|
+
manualCapture(actionName: string, detail?: string): Promise<void>;
|
|
34
|
+
private setupRecording;
|
|
35
|
+
private enqueueCapture;
|
|
36
|
+
private processQueue;
|
|
37
|
+
private waitForQueueDrain;
|
|
38
|
+
/** 获取或创建页面的 CDP session */
|
|
39
|
+
private getCDP;
|
|
40
|
+
/** CDP 截图,强制 scale:1 保证像素=CSS坐标 */
|
|
41
|
+
private takeScreenshotCDP;
|
|
42
|
+
/** 通过 CDP 执行 JS(不触发 codegen 钩子,零闪烁) */
|
|
43
|
+
private cdpEval;
|
|
44
|
+
private captureStep;
|
|
45
|
+
private saveSessionSync;
|
|
46
|
+
stop(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=collector.d.ts.map
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.BenchmarkCollector = void 0;
|
|
37
|
+
const playwright_core_1 = require("playwright-core");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const dom_capture_1 = require("./dom-capture");
|
|
41
|
+
const som_annotator_1 = require("./som-annotator");
|
|
42
|
+
class BenchmarkCollector {
|
|
43
|
+
constructor(task, startUrl, outputBase = 'output') {
|
|
44
|
+
this.task = task;
|
|
45
|
+
this.startUrl = startUrl;
|
|
46
|
+
this.stepCounter = 0;
|
|
47
|
+
this.allCodeLines = [];
|
|
48
|
+
this.stopped = false;
|
|
49
|
+
// CDP session 缓存
|
|
50
|
+
this.cdpSessionMap = new Map();
|
|
51
|
+
// 采集任务队列:保证不丢失任何 action,按顺序处理
|
|
52
|
+
this.captureQueue = [];
|
|
53
|
+
this.processing = false;
|
|
54
|
+
this.queueDone = Promise.resolve();
|
|
55
|
+
this.queueDoneResolve = null;
|
|
56
|
+
const safeName = task.replace(/[^\w\u4e00-\u9fff-]/g, '_').replace(/_+/g, '_').substring(0, 50);
|
|
57
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
58
|
+
const sessionId = `${safeName}_${timestamp}`;
|
|
59
|
+
this.outputDir = path.resolve(outputBase, sessionId);
|
|
60
|
+
this.stepsDir = path.join(this.outputDir, 'steps');
|
|
61
|
+
fs.mkdirSync(this.stepsDir, { recursive: true });
|
|
62
|
+
this.session = {
|
|
63
|
+
sessionId,
|
|
64
|
+
task,
|
|
65
|
+
startUrl,
|
|
66
|
+
startTime: new Date().toISOString(),
|
|
67
|
+
steps: [],
|
|
68
|
+
codegenScript: '',
|
|
69
|
+
traceFile: '',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
get sessionId() { return this.session.sessionId; }
|
|
73
|
+
get outputPath() { return this.outputDir; }
|
|
74
|
+
get steps() { return this.session.steps; }
|
|
75
|
+
async start() {
|
|
76
|
+
let sigintResolve = null;
|
|
77
|
+
const sigintHandler = () => {
|
|
78
|
+
console.log('\n⏳ 收到退出信号,正在保存数据...');
|
|
79
|
+
if (sigintResolve)
|
|
80
|
+
sigintResolve();
|
|
81
|
+
};
|
|
82
|
+
process.on('SIGINT', sigintHandler);
|
|
83
|
+
const exitHandler = () => { try {
|
|
84
|
+
this.saveSessionSync();
|
|
85
|
+
}
|
|
86
|
+
catch (_) { } };
|
|
87
|
+
process.on('exit', exitHandler);
|
|
88
|
+
const browser = await playwright_core_1.chromium.launch({ headless: false });
|
|
89
|
+
this.context = await browser.newContext({
|
|
90
|
+
viewport: { width: 1280, height: 720 },
|
|
91
|
+
});
|
|
92
|
+
await this.context.tracing.start({ screenshots: true, snapshots: true });
|
|
93
|
+
this.activePage = await this.context.newPage();
|
|
94
|
+
await this.setupRecording();
|
|
95
|
+
try {
|
|
96
|
+
await this.activePage.goto(this.startUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
console.log(`\n⚠️ 导航失败: ${e.message?.split('\n')[0]}`);
|
|
100
|
+
console.log(` 浏览器已打开,请手动导航到目标页面`);
|
|
101
|
+
}
|
|
102
|
+
await this.activePage.waitForTimeout(500);
|
|
103
|
+
this.enqueueCapture({
|
|
104
|
+
page: this.activePage,
|
|
105
|
+
action: { name: 'initial_state', url: this.activePage.url() },
|
|
106
|
+
codegenCode: `// Initial state: ${this.activePage.url()}`,
|
|
107
|
+
timestamp: Date.now(),
|
|
108
|
+
});
|
|
109
|
+
await this.waitForQueueDrain();
|
|
110
|
+
console.log(`\n✅ 采集已启动,请在浏览器中操作`);
|
|
111
|
+
console.log(` 任务: ${this.task}`);
|
|
112
|
+
console.log(` 按 Ctrl+C 结束采集\n`);
|
|
113
|
+
await new Promise((resolve) => {
|
|
114
|
+
sigintResolve = resolve;
|
|
115
|
+
browser.on('disconnected', () => resolve());
|
|
116
|
+
});
|
|
117
|
+
await this.stop();
|
|
118
|
+
process.removeListener('SIGINT', sigintHandler);
|
|
119
|
+
process.removeListener('exit', exitHandler);
|
|
120
|
+
await browser.close().catch(() => { });
|
|
121
|
+
console.log('👋 采集结束');
|
|
122
|
+
process.exit(0);
|
|
123
|
+
}
|
|
124
|
+
/** 非交互式模式,用于测试 */
|
|
125
|
+
async startHeadless(options) {
|
|
126
|
+
const browser = await playwright_core_1.chromium.launch({ headless: options?.headless ?? true });
|
|
127
|
+
this.context = await browser.newContext({
|
|
128
|
+
viewport: { width: 1280, height: 720 },
|
|
129
|
+
});
|
|
130
|
+
await this.context.tracing.start({ screenshots: true, snapshots: true });
|
|
131
|
+
this.activePage = await this.context.newPage();
|
|
132
|
+
await this.setupRecording();
|
|
133
|
+
return { browser, context: this.context, page: this.activePage };
|
|
134
|
+
}
|
|
135
|
+
/** 手动触发一次采集 */
|
|
136
|
+
async manualCapture(actionName, detail) {
|
|
137
|
+
this.enqueueCapture({
|
|
138
|
+
page: this.activePage,
|
|
139
|
+
action: { name: actionName, url: detail },
|
|
140
|
+
codegenCode: `// Manual: ${actionName} ${detail || ''}`,
|
|
141
|
+
timestamp: Date.now(),
|
|
142
|
+
});
|
|
143
|
+
await this.waitForQueueDrain();
|
|
144
|
+
}
|
|
145
|
+
// ========== 录制设置 ==========
|
|
146
|
+
async setupRecording() {
|
|
147
|
+
// 1. Codegen API — 与 codegen 完全一致的操作记录
|
|
148
|
+
await this.context._enableRecorder({ language: 'javascript', mode: 'recording', recorderMode: 'api' }, {
|
|
149
|
+
actionAdded: (page, data, code) => {
|
|
150
|
+
if (this.stopped)
|
|
151
|
+
return;
|
|
152
|
+
if (page)
|
|
153
|
+
this.activePage = page;
|
|
154
|
+
this.allCodeLines.push(code);
|
|
155
|
+
this.enqueueCapture({
|
|
156
|
+
page: page || this.activePage,
|
|
157
|
+
action: data.action,
|
|
158
|
+
codegenCode: code,
|
|
159
|
+
timestamp: Date.now(),
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
actionUpdated: (page, data, code) => {
|
|
163
|
+
if (this.stopped)
|
|
164
|
+
return;
|
|
165
|
+
if (page)
|
|
166
|
+
this.activePage = page;
|
|
167
|
+
if (this.session.steps.length > 0) {
|
|
168
|
+
const lastStep = this.session.steps[this.session.steps.length - 1];
|
|
169
|
+
lastStep.action = {
|
|
170
|
+
name: data.action.name,
|
|
171
|
+
selector: data.action.selector,
|
|
172
|
+
text: data.action.text,
|
|
173
|
+
url: data.action.url,
|
|
174
|
+
position: data.action.position,
|
|
175
|
+
modifiers: data.action.modifiers,
|
|
176
|
+
};
|
|
177
|
+
lastStep.codegenCode = code;
|
|
178
|
+
this.allCodeLines[this.allCodeLines.length - 1] = code;
|
|
179
|
+
this.saveSessionSync();
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
signalAdded: (_page, data) => {
|
|
183
|
+
console.log(` [Signal] ${data.name || JSON.stringify(data)}`);
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
// 2. 页面跳转记录
|
|
187
|
+
const trackNavigation = (p) => {
|
|
188
|
+
let lastUrl = p.url();
|
|
189
|
+
p.on('framenavigated', (frame) => {
|
|
190
|
+
if (this.stopped || frame !== p.mainFrame())
|
|
191
|
+
return;
|
|
192
|
+
const newUrl = p.url();
|
|
193
|
+
if (newUrl && newUrl !== lastUrl && newUrl !== 'about:blank') {
|
|
194
|
+
const code = `await page.goto('${newUrl}');`;
|
|
195
|
+
this.allCodeLines.push(code);
|
|
196
|
+
this.enqueueCapture({
|
|
197
|
+
page: p,
|
|
198
|
+
action: { name: 'navigate', url: newUrl },
|
|
199
|
+
codegenCode: code,
|
|
200
|
+
timestamp: Date.now(),
|
|
201
|
+
});
|
|
202
|
+
lastUrl = newUrl;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
trackNavigation(this.activePage);
|
|
207
|
+
// 3. 新页面跟踪(popup 等)
|
|
208
|
+
this.context.on('page', (newPage) => {
|
|
209
|
+
const url = newPage.url();
|
|
210
|
+
console.log(` [Page] 新页面打开: ${url}`);
|
|
211
|
+
// 记录新 tab 打开事件
|
|
212
|
+
const code = `// New page opened: ${url}`;
|
|
213
|
+
this.allCodeLines.push(code);
|
|
214
|
+
this.enqueueCapture({
|
|
215
|
+
page: newPage,
|
|
216
|
+
action: { name: 'new_page', url },
|
|
217
|
+
codegenCode: code,
|
|
218
|
+
timestamp: Date.now(),
|
|
219
|
+
});
|
|
220
|
+
trackNavigation(newPage);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
// ========== 采集任务队列 ==========
|
|
224
|
+
enqueueCapture(task) {
|
|
225
|
+
this.captureQueue.push(task);
|
|
226
|
+
if (!this.processing) {
|
|
227
|
+
this.queueDone = new Promise((resolve) => { this.queueDoneResolve = resolve; });
|
|
228
|
+
this.processQueue();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async processQueue() {
|
|
232
|
+
this.processing = true;
|
|
233
|
+
while (this.captureQueue.length > 0) {
|
|
234
|
+
const task = this.captureQueue.shift();
|
|
235
|
+
if (!this.stopped) {
|
|
236
|
+
await this.captureStep(task).catch((e) => {
|
|
237
|
+
console.error(` [Error] 采集失败: ${e.message?.split('\n')[0] || e}`);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
this.processing = false;
|
|
242
|
+
if (this.queueDoneResolve) {
|
|
243
|
+
this.queueDoneResolve();
|
|
244
|
+
this.queueDoneResolve = null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async waitForQueueDrain() {
|
|
248
|
+
await this.queueDone;
|
|
249
|
+
}
|
|
250
|
+
// ========== CDP 工具方法 ==========
|
|
251
|
+
/** 获取或创建页面的 CDP session */
|
|
252
|
+
async getCDP(page) {
|
|
253
|
+
let cdp = this.cdpSessionMap.get(page);
|
|
254
|
+
if (!cdp) {
|
|
255
|
+
cdp = await page.context().newCDPSession(page);
|
|
256
|
+
this.cdpSessionMap.set(page, cdp);
|
|
257
|
+
}
|
|
258
|
+
return cdp;
|
|
259
|
+
}
|
|
260
|
+
/** CDP 截图,强制 scale:1 保证像素=CSS坐标 */
|
|
261
|
+
async takeScreenshotCDP(cdp) {
|
|
262
|
+
// 先尝试带 clip.scale=1(精确控制分辨率)
|
|
263
|
+
try {
|
|
264
|
+
const metrics = await cdp.send('Page.getLayoutMetrics');
|
|
265
|
+
const vp = metrics.cssLayoutViewport || metrics.layoutViewport;
|
|
266
|
+
if (vp && vp.width > 0 && vp.height > 0) {
|
|
267
|
+
const result = await cdp.send('Page.captureScreenshot', {
|
|
268
|
+
format: 'png',
|
|
269
|
+
clip: { x: 0, y: 0, width: vp.width, height: vp.height, scale: 1 },
|
|
270
|
+
});
|
|
271
|
+
return result.data;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch (_) { }
|
|
275
|
+
// 回退:captureBeyondViewport=false,再用 JS 查 dpr 做后处理
|
|
276
|
+
// 实际上对 about:blank 等简单页面直接截即可
|
|
277
|
+
const result = await cdp.send('Page.captureScreenshot', { format: 'png' });
|
|
278
|
+
return result.data;
|
|
279
|
+
}
|
|
280
|
+
/** 通过 CDP 执行 JS(不触发 codegen 钩子,零闪烁) */
|
|
281
|
+
async cdpEval(cdp, expression) {
|
|
282
|
+
const result = await cdp.send('Runtime.evaluate', {
|
|
283
|
+
expression,
|
|
284
|
+
returnByValue: true,
|
|
285
|
+
awaitPromise: true,
|
|
286
|
+
});
|
|
287
|
+
if (result.exceptionDetails) {
|
|
288
|
+
throw new Error(result.exceptionDetails.text || 'CDP eval error');
|
|
289
|
+
}
|
|
290
|
+
return result.result.value;
|
|
291
|
+
}
|
|
292
|
+
// ========== 核心采集(全部走 CDP,零闪烁) ==========
|
|
293
|
+
async captureStep(task) {
|
|
294
|
+
const { page, action, codegenCode, timestamp } = task;
|
|
295
|
+
const stepId = this.stepCounter++;
|
|
296
|
+
const prefix = String(stepId).padStart(4, '0');
|
|
297
|
+
const cdp = await this.getCDP(page);
|
|
298
|
+
// 等待页面稳定(CDP 方式,不走 Playwright)
|
|
299
|
+
await new Promise(r => setTimeout(r, 200));
|
|
300
|
+
// ① 纯净截图(CDP, scale:1 保证截图像素=CSS像素)
|
|
301
|
+
const cleanPath = `${prefix}_clean.png`;
|
|
302
|
+
const screenshotResult = await this.takeScreenshotCDP(cdp);
|
|
303
|
+
const cleanBuffer = Buffer.from(screenshotResult, 'base64');
|
|
304
|
+
fs.writeFileSync(path.join(this.stepsDir, cleanPath), cleanBuffer);
|
|
305
|
+
// ② DOM(CDP)
|
|
306
|
+
const htmlPath = `${prefix}_dom.html`;
|
|
307
|
+
const html = await this.cdpEval(cdp, 'document.documentElement.outerHTML');
|
|
308
|
+
fs.writeFileSync(path.join(this.stepsDir, htmlPath), html);
|
|
309
|
+
// ③ 可交互元素(CDP)
|
|
310
|
+
const elementsPath = `${prefix}_elements.json`;
|
|
311
|
+
const elementsJson = await this.cdpEval(cdp, (0, dom_capture_1.getCollectScript)());
|
|
312
|
+
const elements = JSON.parse(elementsJson);
|
|
313
|
+
fs.writeFileSync(path.join(this.stepsDir, elementsPath), JSON.stringify(elements, null, 2));
|
|
314
|
+
// ④ SoM 标注截图(CDP Canvas 合成)
|
|
315
|
+
const somPath = `${prefix}_som.png`;
|
|
316
|
+
const somScript = (0, som_annotator_1.getSoMRenderScript)(screenshotResult, elements);
|
|
317
|
+
const somBase64 = await this.cdpEval(cdp, somScript);
|
|
318
|
+
const somBuffer = Buffer.from(somBase64, 'base64');
|
|
319
|
+
fs.writeFileSync(path.join(this.stepsDir, somPath), somBuffer);
|
|
320
|
+
// ④ 记录
|
|
321
|
+
const step = {
|
|
322
|
+
stepId,
|
|
323
|
+
action: {
|
|
324
|
+
name: action.name,
|
|
325
|
+
selector: action.selector,
|
|
326
|
+
text: action.text,
|
|
327
|
+
url: action.url,
|
|
328
|
+
position: action.position,
|
|
329
|
+
modifiers: action.modifiers,
|
|
330
|
+
},
|
|
331
|
+
codegenCode,
|
|
332
|
+
timestamp,
|
|
333
|
+
pageUrl: page.url(),
|
|
334
|
+
screenshots: { clean: cleanPath, som: somPath },
|
|
335
|
+
dom: { html: htmlPath, interactiveElements: elementsPath },
|
|
336
|
+
};
|
|
337
|
+
this.session.steps.push(step);
|
|
338
|
+
this.saveSessionSync();
|
|
339
|
+
console.log(`[Step ${stepId}] ${step.action.name}` +
|
|
340
|
+
`${step.action.selector ? ' → ' + step.action.selector : ''}` +
|
|
341
|
+
`${step.action.text ? ' "' + step.action.text + '"' : ''}`);
|
|
342
|
+
}
|
|
343
|
+
// ========== 数据保存 ==========
|
|
344
|
+
saveSessionSync() {
|
|
345
|
+
try {
|
|
346
|
+
this.session.codegenScript = this.allCodeLines.join('\n');
|
|
347
|
+
fs.writeFileSync(path.join(this.outputDir, 'session.json'), JSON.stringify(this.session, null, 2));
|
|
348
|
+
fs.writeFileSync(path.join(this.outputDir, 'recorded-script.js'), this.session.codegenScript);
|
|
349
|
+
}
|
|
350
|
+
catch (_) { }
|
|
351
|
+
}
|
|
352
|
+
async stop() {
|
|
353
|
+
await this.waitForQueueDrain();
|
|
354
|
+
this.stopped = true;
|
|
355
|
+
this.session.endTime = new Date().toISOString();
|
|
356
|
+
for (const cdp of this.cdpSessionMap.values()) {
|
|
357
|
+
await cdp.detach().catch(() => { });
|
|
358
|
+
}
|
|
359
|
+
this.cdpSessionMap.clear();
|
|
360
|
+
this.session.traceFile = 'trace.zip';
|
|
361
|
+
try {
|
|
362
|
+
await this.context.tracing.stop({ path: path.join(this.outputDir, 'trace.zip') });
|
|
363
|
+
}
|
|
364
|
+
catch (_) { }
|
|
365
|
+
await this.context._disableRecorder().catch(() => { });
|
|
366
|
+
this.saveSessionSync();
|
|
367
|
+
console.log(`\n✅ 采集完成`);
|
|
368
|
+
console.log(` 共 ${this.session.steps.length} 步操作`);
|
|
369
|
+
console.log(` 数据目录: ${this.outputDir}/`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
exports.BenchmarkCollector = BenchmarkCollector;
|
|
373
|
+
//# sourceMappingURL=collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collector.js","sourceRoot":"","sources":["../../src/collector.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qDAAiE;AACjE,uCAAyB;AACzB,2CAA6B;AAC7B,+CAAiD;AACjD,mDAAqD;AAWrD,MAAa,kBAAkB;IAiB7B,YAAoB,IAAY,EAAU,QAAgB,EAAE,UAAU,GAAG,QAAQ;QAA7D,SAAI,GAAJ,IAAI,CAAQ;QAAU,aAAQ,GAAR,QAAQ,CAAQ;QAblD,gBAAW,GAAG,CAAC,CAAC;QAChB,iBAAY,GAAa,EAAE,CAAC;QAG5B,YAAO,GAAG,KAAK,CAAC;QACxB,iBAAiB;QACT,kBAAa,GAAG,IAAI,GAAG,EAAa,CAAC;QAC7C,8BAA8B;QACtB,iBAAY,GAAkB,EAAE,CAAC;QACjC,eAAU,GAAG,KAAK,CAAC;QACnB,cAAS,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;QAC7C,qBAAgB,GAAwB,IAAI,CAAC;QAGnD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChG,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9E,MAAM,SAAS,GAAG,GAAG,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC7C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACnD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjD,IAAI,CAAC,OAAO,GAAG;YACb,SAAS;YACT,IAAI;YACJ,QAAQ;YACR,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,KAAK,EAAE,EAAE;YACT,aAAa,EAAE,EAAE;YACjB,SAAS,EAAE,EAAE;SACd,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAClD,IAAI,UAAU,KAAK,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAE1C,KAAK,CAAC,KAAK;QACT,IAAI,aAAa,GAAwB,IAAI,CAAC;QAC9C,MAAM,aAAa,GAAG,GAAG,EAAE;YACzB,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;YACpC,IAAI,aAAa;gBAAE,aAAa,EAAE,CAAC;QACrC,CAAC,CAAC;QACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QACpC,MAAM,WAAW,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;YAAC,IAAI,CAAC,eAAe,EAAE,CAAC;QAAC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC,CAAA,CAAC,CAAC,CAAC,CAAC;QAC3E,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAEhC,MAAM,OAAO,GAAG,MAAM,0BAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YACtC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;SACvC,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC/C,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAE5B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/F,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACxD,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACtC,CAAC;QAED,MAAM,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,cAAc,CAAC;YAClB,IAAI,EAAE,IAAI,CAAC,UAAU;YACrB,MAAM,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE;YAC7D,WAAW,EAAE,qBAAqB,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE;YACzD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAElC,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,aAAa,GAAG,OAAO,CAAC;YACxB,OAAO,CAAC,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QAChD,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAC5C,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,kBAAkB;IAClB,KAAK,CAAC,aAAa,CAAC,OAAgC;QAClD,MAAM,OAAO,GAAG,MAAM,0BAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YACtC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;SACvC,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC/C,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IACnE,CAAC;IAED,eAAe;IACf,KAAK,CAAC,aAAa,CAAC,UAAkB,EAAE,MAAe;QACrD,IAAI,CAAC,cAAc,CAAC;YAClB,IAAI,EAAE,IAAI,CAAC,UAAU;YACrB,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE;YACzC,WAAW,EAAE,cAAc,UAAU,IAAI,MAAM,IAAI,EAAE,EAAE;YACvD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;IACjC,CAAC;IAED,6BAA6B;IAErB,KAAK,CAAC,cAAc;QAC1B,uCAAuC;QACvC,MAAO,IAAI,CAAC,OAAe,CAAC,eAAe,CACzC,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,EAAE,EAClE;YACE,WAAW,EAAE,CAAC,IAAU,EAAE,IAAS,EAAE,IAAY,EAAE,EAAE;gBACnD,IAAI,IAAI,CAAC,OAAO;oBAAE,OAAO;gBACzB,IAAI,IAAI;oBAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;gBACjC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC7B,IAAI,CAAC,cAAc,CAAC;oBAClB,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,UAAU;oBAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,WAAW,EAAE,IAAI;oBACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACtB,CAAC,CAAC;YACL,CAAC;YACD,aAAa,EAAE,CAAC,IAAU,EAAE,IAAS,EAAE,IAAY,EAAE,EAAE;gBACrD,IAAI,IAAI,CAAC,OAAO;oBAAE,OAAO;gBACzB,IAAI,IAAI;oBAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;gBACjC,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;oBACnE,QAAQ,CAAC,MAAM,GAAG;wBAChB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;wBACtB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;wBAC9B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;wBACtB,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG;wBACpB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;wBAC9B,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;qBACjC,CAAC;oBACF,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC;oBAC5B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;oBACvD,IAAI,CAAC,eAAe,EAAE,CAAC;gBACzB,CAAC;YACH,CAAC;YACD,WAAW,EAAE,CAAC,KAAW,EAAE,IAAS,EAAE,EAAE;gBACtC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjE,CAAC;SACF,CACF,CAAC;QAEF,YAAY;QACZ,MAAM,eAAe,GAAG,CAAC,CAAO,EAAE,EAAE;YAClC,IAAI,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;YACtB,CAAC,CAAC,EAAE,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC/B,IAAI,IAAI,CAAC,OAAO,IAAI,KAAK,KAAK,CAAC,CAAC,SAAS,EAAE;oBAAE,OAAO;gBACpD,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;gBACvB,IAAI,MAAM,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;oBAC7D,MAAM,IAAI,GAAG,oBAAoB,MAAM,KAAK,CAAC;oBAC7C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC7B,IAAI,CAAC,cAAc,CAAC;wBAClB,IAAI,EAAE,CAAC;wBACP,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE;wBACzC,WAAW,EAAE,IAAI;wBACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;qBACtB,CAAC,CAAC;oBACH,OAAO,GAAG,MAAM,CAAC;gBACnB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAEjC,oBAAoB;QACpB,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE;YAClC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,EAAE,CAAC,CAAC;YACtC,eAAe;YACf,MAAM,IAAI,GAAG,uBAAuB,GAAG,EAAE,CAAC;YAC1C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,cAAc,CAAC;gBAClB,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE;gBACjC,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC,CAAC;YACH,eAAe,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,+BAA+B;IAEvB,cAAc,CAAC,IAAiB;QACtC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,CAAC,SAAS,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YAChF,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAG,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;oBACvC,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACrE,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC/B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,iBAAiB;QAC7B,MAAM,IAAI,CAAC,SAAS,CAAC;IACvB,CAAC;IAED,iCAAiC;IAEjC,2BAA2B;IACnB,KAAK,CAAC,MAAM,CAAC,IAAU;QAC7B,IAAI,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,mCAAmC;IAC3B,KAAK,CAAC,iBAAiB,CAAC,GAAQ;QACtC,6BAA6B;QAC7B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;YACxD,MAAM,EAAE,GAAG,OAAO,CAAC,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC;YAC/D,IAAI,EAAE,IAAI,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,wBAAwB,EAAE;oBACtD,MAAM,EAAE,KAAK;oBACb,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE;iBACnE,CAAC,CAAC;gBACH,OAAO,MAAM,CAAC,IAAI,CAAC;YACrB,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC,CAAA,CAAC;QACd,kDAAkD;QAClD,8BAA8B;QAC9B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3E,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,uCAAuC;IAC/B,KAAK,CAAC,OAAO,CAAC,GAAQ,EAAE,UAAkB;QAChD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE;YAChD,UAAU;YACV,aAAa,EAAE,IAAI;YACnB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,IAAI,gBAAgB,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;IAC7B,CAAC;IAED,0CAA0C;IAElC,KAAK,CAAC,WAAW,CAAC,IAAiB;QACzC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAE/C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEpC,+BAA+B;QAC/B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAE3C,oCAAoC;QACpC,MAAM,SAAS,GAAG,GAAG,MAAM,YAAY,CAAC;QACxC,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;QAC3D,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QAC5D,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;QAEnE,aAAa;QACb,MAAM,QAAQ,GAAG,GAAG,MAAM,WAAW,CAAC;QACtC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,oCAAoC,CAAC,CAAC;QAC3E,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;QAE3D,eAAe;QACf,MAAM,YAAY,GAAG,GAAG,MAAM,gBAAgB,CAAC;QAC/C,MAAM,YAAY,GAAW,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAA,8BAAgB,GAAE,CAAC,CAAC;QACzE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC1C,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAE5F,4BAA4B;QAC5B,MAAM,OAAO,GAAG,GAAG,MAAM,UAAU,CAAC;QACpC,MAAM,SAAS,GAAG,IAAA,kCAAkB,EAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QACjE,MAAM,SAAS,GAAW,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC7D,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACnD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC;QAE/D,OAAO;QACP,MAAM,IAAI,GAAe;YACvB,MAAM;YACN,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B;YACD,WAAW;YACX,SAAS;YACT,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;YACnB,WAAW,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE;YAC/C,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,mBAAmB,EAAE,YAAY,EAAE;SAC3D,CAAC;QAEF,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,OAAO,CAAC,GAAG,CACT,SAAS,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;YACtC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE;YAC7D,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3D,CAAC;IACJ,CAAC;IAED,6BAA6B;IAErB,eAAe;QACrB,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1D,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,EACzC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CACtC,CAAC;YACF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,EAC/C,IAAI,CAAC,OAAO,CAAC,aAAa,CAC3B,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC,CAAA,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAEhD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAE3B,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,WAAW,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;QACpF,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC,CAAA,CAAC;QAEd,MAAO,IAAI,CAAC,OAAe,CAAC,gBAAgB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,MAAM,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7C,CAAC;CACF;AAtXD,gDAsXC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Page } from 'playwright-core';
|
|
2
|
+
import { SoMElement } from './types';
|
|
3
|
+
/** 通过 page.evaluate 采集(用于测试等非 codegen 场景) */
|
|
4
|
+
export declare function collectInteractiveElements(page: Page): Promise<SoMElement[]>;
|
|
5
|
+
/** 返回可通过 CDP Runtime.evaluate 执行的采集脚本 */
|
|
6
|
+
export declare function getCollectScript(): string;
|
|
7
|
+
//# sourceMappingURL=dom-capture.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectInteractiveElements = collectInteractiveElements;
|
|
4
|
+
exports.getCollectScript = getCollectScript;
|
|
5
|
+
const INTERACTIVE_SELECTORS = [
|
|
6
|
+
'a[href]', 'button', 'input', 'select', 'textarea',
|
|
7
|
+
'[role="button"]', '[role="link"]', '[role="menuitem"]',
|
|
8
|
+
'[role="tab"]', '[role="checkbox"]', '[role="radio"]',
|
|
9
|
+
'[role="combobox"]', '[role="textbox"]', '[role="option"]',
|
|
10
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
11
|
+
'[onclick]', '[contenteditable="true"]',
|
|
12
|
+
].join(', ');
|
|
13
|
+
/** 采集脚本(纯字符串,可通过 CDP Runtime.evaluate 执行) */
|
|
14
|
+
const COLLECT_SCRIPT = `(() => {
|
|
15
|
+
const selector = ${JSON.stringify(INTERACTIVE_SELECTORS)};
|
|
16
|
+
const ATTR_WHITELIST = ['id','class','name','type','placeholder','aria-label','role','href','value','title','alt'];
|
|
17
|
+
function collect(root, results) {
|
|
18
|
+
const elements = root.querySelectorAll(selector);
|
|
19
|
+
elements.forEach(el => {
|
|
20
|
+
const rect = el.getBoundingClientRect();
|
|
21
|
+
if (rect.width < 2 || rect.height < 2) return;
|
|
22
|
+
if (rect.bottom < 0 || rect.top > window.innerHeight) return;
|
|
23
|
+
if (rect.right < 0 || rect.left > window.innerWidth) return;
|
|
24
|
+
const attrs = {};
|
|
25
|
+
for (const a of el.attributes) {
|
|
26
|
+
if (ATTR_WHITELIST.includes(a.name)) attrs[a.name] = a.value;
|
|
27
|
+
}
|
|
28
|
+
results.push({
|
|
29
|
+
index: results.length,
|
|
30
|
+
tag: el.tagName.toLowerCase(),
|
|
31
|
+
role: el.getAttribute('role') || undefined,
|
|
32
|
+
text: (el.textContent || '').trim().substring(0, 200),
|
|
33
|
+
bbox: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height) },
|
|
34
|
+
attributes: attrs,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
root.querySelectorAll('*').forEach(el => {
|
|
38
|
+
if (el.shadowRoot) collect(el.shadowRoot, results);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const results = [];
|
|
42
|
+
collect(document.documentElement, results);
|
|
43
|
+
return JSON.stringify(results);
|
|
44
|
+
})()`;
|
|
45
|
+
/** 通过 page.evaluate 采集(用于测试等非 codegen 场景) */
|
|
46
|
+
async function collectInteractiveElements(page) {
|
|
47
|
+
return await page.evaluate((selector) => {
|
|
48
|
+
const ATTR_WHITELIST = [
|
|
49
|
+
'id', 'class', 'name', 'type', 'placeholder', 'aria-label',
|
|
50
|
+
'role', 'href', 'value', 'title', 'alt',
|
|
51
|
+
];
|
|
52
|
+
function collect(root, results) {
|
|
53
|
+
const elements = root.querySelectorAll(selector);
|
|
54
|
+
elements.forEach((el) => {
|
|
55
|
+
const rect = el.getBoundingClientRect();
|
|
56
|
+
if (rect.width < 2 || rect.height < 2)
|
|
57
|
+
return;
|
|
58
|
+
if (rect.bottom < 0 || rect.top > window.innerHeight)
|
|
59
|
+
return;
|
|
60
|
+
if (rect.right < 0 || rect.left > window.innerWidth)
|
|
61
|
+
return;
|
|
62
|
+
const attrs = {};
|
|
63
|
+
for (const a of Array.from(el.attributes)) {
|
|
64
|
+
if (ATTR_WHITELIST.includes(a.name)) {
|
|
65
|
+
attrs[a.name] = a.value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
results.push({
|
|
69
|
+
index: results.length,
|
|
70
|
+
tag: el.tagName.toLowerCase(),
|
|
71
|
+
role: el.getAttribute('role') || undefined,
|
|
72
|
+
text: (el.textContent || '').trim().substring(0, 200),
|
|
73
|
+
bbox: {
|
|
74
|
+
x: Math.round(rect.x),
|
|
75
|
+
y: Math.round(rect.y),
|
|
76
|
+
width: Math.round(rect.width),
|
|
77
|
+
height: Math.round(rect.height),
|
|
78
|
+
},
|
|
79
|
+
attributes: attrs,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
root.querySelectorAll('*').forEach((el) => {
|
|
83
|
+
if (el.shadowRoot)
|
|
84
|
+
collect(el.shadowRoot, results);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const results = [];
|
|
88
|
+
collect(document.documentElement, results);
|
|
89
|
+
return results;
|
|
90
|
+
}, INTERACTIVE_SELECTORS);
|
|
91
|
+
}
|
|
92
|
+
/** 返回可通过 CDP Runtime.evaluate 执行的采集脚本 */
|
|
93
|
+
function getCollectScript() {
|
|
94
|
+
return COLLECT_SCRIPT;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=dom-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-capture.js","sourceRoot":"","sources":["../../src/dom-capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BenchmarkCollector } from './collector';
|
|
2
|
+
export { collectInteractiveElements, getCollectScript } from './dom-capture';
|
|
3
|
+
export { renderSoMScreenshot, getSoMRenderScript } from './som-annotator';
|
|
4
|
+
export type { SoMElement, StepRecord, SessionRecord } from './types';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSoMRenderScript = exports.renderSoMScreenshot = exports.getCollectScript = exports.collectInteractiveElements = exports.BenchmarkCollector = void 0;
|
|
4
|
+
var collector_1 = require("./collector");
|
|
5
|
+
Object.defineProperty(exports, "BenchmarkCollector", { enumerable: true, get: function () { return collector_1.BenchmarkCollector; } });
|
|
6
|
+
var dom_capture_1 = require("./dom-capture");
|
|
7
|
+
Object.defineProperty(exports, "collectInteractiveElements", { enumerable: true, get: function () { return dom_capture_1.collectInteractiveElements; } });
|
|
8
|
+
Object.defineProperty(exports, "getCollectScript", { enumerable: true, get: function () { return dom_capture_1.getCollectScript; } });
|
|
9
|
+
var som_annotator_1 = require("./som-annotator");
|
|
10
|
+
Object.defineProperty(exports, "renderSoMScreenshot", { enumerable: true, get: function () { return som_annotator_1.renderSoMScreenshot; } });
|
|
11
|
+
Object.defineProperty(exports, "getSoMRenderScript", { enumerable: true, get: function () { return som_annotator_1.getSoMRenderScript; } });
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAAiD;AAAxC,+GAAA,kBAAkB,OAAA;AAC3B,6CAA6E;AAApE,yHAAA,0BAA0B,OAAA;AAAE,+GAAA,gBAAgB,OAAA;AACrD,iDAA0E;AAAjE,oHAAA,mBAAmB,OAAA;AAAE,mHAAA,kBAAkB,OAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Page } from 'playwright-core';
|
|
2
|
+
import { SoMElement } from './types';
|
|
3
|
+
/** 生成 SoM 渲染脚本(纯字符串,可通过 CDP Runtime.evaluate 执行) */
|
|
4
|
+
export declare function getSoMRenderScript(imgBase64: string, elements: SoMElement[]): string;
|
|
5
|
+
/**
|
|
6
|
+
* 通过 page.evaluate 生成 SoM 截图(用于测试等非 codegen 场景)
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderSoMScreenshot(page: Page, cleanScreenshot: Buffer, elements: SoMElement[]): Promise<Buffer>;
|
|
9
|
+
//# sourceMappingURL=som-annotator.d.ts.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSoMRenderScript = getSoMRenderScript;
|
|
4
|
+
exports.renderSoMScreenshot = renderSoMScreenshot;
|
|
5
|
+
const COLORS = [
|
|
6
|
+
'#E6194B', '#3CB44B', '#FFE119', '#4363D8', '#F58231',
|
|
7
|
+
'#911EB4', '#42D4F4', '#F032E6', '#BFEF45', '#FABED4',
|
|
8
|
+
'#469990', '#DCBEFF', '#9A6324', '#FFFAC8', '#800000',
|
|
9
|
+
'#AAFFC3', '#808000', '#FFD8B1', '#000075', '#A9A9A9',
|
|
10
|
+
];
|
|
11
|
+
/** 生成 SoM 渲染脚本(纯字符串,可通过 CDP Runtime.evaluate 执行) */
|
|
12
|
+
function getSoMRenderScript(imgBase64, elements) {
|
|
13
|
+
return `new Promise((resolve, reject) => {
|
|
14
|
+
const colors = ${JSON.stringify(COLORS)};
|
|
15
|
+
const elems = ${JSON.stringify(elements)};
|
|
16
|
+
const img = new Image();
|
|
17
|
+
img.onload = () => {
|
|
18
|
+
const canvas = document.createElement('canvas');
|
|
19
|
+
canvas.width = img.width;
|
|
20
|
+
canvas.height = img.height;
|
|
21
|
+
const ctx = canvas.getContext('2d');
|
|
22
|
+
if (!ctx) { reject('no canvas context'); return; }
|
|
23
|
+
ctx.drawImage(img, 0, 0);
|
|
24
|
+
// 计算缩放比:截图实际像素 / CSS视口像素
|
|
25
|
+
// 无论截图是 1x 还是 2x,都能正确对齐
|
|
26
|
+
const scaleX = img.width / window.innerWidth;
|
|
27
|
+
const scaleY = img.height / window.innerHeight;
|
|
28
|
+
elems.forEach(el => {
|
|
29
|
+
const color = colors[el.index % colors.length];
|
|
30
|
+
const sx = el.bbox.x * scaleX;
|
|
31
|
+
const sy = el.bbox.y * scaleY;
|
|
32
|
+
const sw = el.bbox.width * scaleX;
|
|
33
|
+
const sh = el.bbox.height * scaleY;
|
|
34
|
+
ctx.fillStyle = color + '20';
|
|
35
|
+
ctx.fillRect(sx, sy, sw, sh);
|
|
36
|
+
ctx.strokeStyle = color;
|
|
37
|
+
ctx.lineWidth = 2;
|
|
38
|
+
ctx.strokeRect(sx, sy, sw, sh);
|
|
39
|
+
const labelText = String(el.index);
|
|
40
|
+
const fontSize = Math.max(10, 10 * scaleX);
|
|
41
|
+
ctx.font = 'bold ' + fontSize + 'px monospace';
|
|
42
|
+
const tw = ctx.measureText(labelText).width;
|
|
43
|
+
const lx = sx, ly = Math.max(0, sy - 16 * scaleY);
|
|
44
|
+
ctx.fillStyle = color;
|
|
45
|
+
ctx.fillRect(lx, ly, tw + 6 * scaleX, 14 * scaleY);
|
|
46
|
+
ctx.fillStyle = 'white';
|
|
47
|
+
ctx.textBaseline = 'top';
|
|
48
|
+
ctx.fillText(labelText, lx + 3 * scaleX, ly + 1 * scaleY);
|
|
49
|
+
});
|
|
50
|
+
resolve(canvas.toDataURL('image/png').split(',')[1]);
|
|
51
|
+
};
|
|
52
|
+
img.onerror = () => reject('failed to load image');
|
|
53
|
+
img.src = 'data:image/png;base64,${imgBase64}';
|
|
54
|
+
})`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 通过 page.evaluate 生成 SoM 截图(用于测试等非 codegen 场景)
|
|
58
|
+
*/
|
|
59
|
+
async function renderSoMScreenshot(page, cleanScreenshot, elements) {
|
|
60
|
+
const cleanBase64 = cleanScreenshot.toString('base64');
|
|
61
|
+
const somBase64 = await page.evaluate(async ({ imgBase64, elems, colors }) => {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const img = new Image();
|
|
64
|
+
img.onload = () => {
|
|
65
|
+
const canvas = document.createElement('canvas');
|
|
66
|
+
canvas.width = img.width;
|
|
67
|
+
canvas.height = img.height;
|
|
68
|
+
const ctx = canvas.getContext('2d');
|
|
69
|
+
if (!ctx) {
|
|
70
|
+
reject('no canvas context');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
ctx.drawImage(img, 0, 0);
|
|
74
|
+
// 计算缩放比:截图实际像素 / CSS视口像素
|
|
75
|
+
const scaleX = img.width / window.innerWidth;
|
|
76
|
+
const scaleY = img.height / window.innerHeight;
|
|
77
|
+
elems.forEach((el) => {
|
|
78
|
+
const color = colors[el.index % colors.length];
|
|
79
|
+
const sx = el.bbox.x * scaleX;
|
|
80
|
+
const sy = el.bbox.y * scaleY;
|
|
81
|
+
const sw = el.bbox.width * scaleX;
|
|
82
|
+
const sh = el.bbox.height * scaleY;
|
|
83
|
+
ctx.fillStyle = color + '20';
|
|
84
|
+
ctx.fillRect(sx, sy, sw, sh);
|
|
85
|
+
ctx.strokeStyle = color;
|
|
86
|
+
ctx.lineWidth = 2;
|
|
87
|
+
ctx.strokeRect(sx, sy, sw, sh);
|
|
88
|
+
const labelText = String(el.index);
|
|
89
|
+
const fontSize = Math.max(10, 10 * scaleX);
|
|
90
|
+
ctx.font = 'bold ' + fontSize + 'px monospace';
|
|
91
|
+
const textWidth = ctx.measureText(labelText).width;
|
|
92
|
+
const labelX = sx;
|
|
93
|
+
const labelY = Math.max(0, sy - 16 * scaleY);
|
|
94
|
+
ctx.fillStyle = color;
|
|
95
|
+
ctx.fillRect(labelX, labelY, textWidth + 6 * scaleX, 14 * scaleY);
|
|
96
|
+
ctx.fillStyle = 'white';
|
|
97
|
+
ctx.textBaseline = 'top';
|
|
98
|
+
ctx.fillText(labelText, labelX + 3 * scaleX, labelY + 1 * scaleY);
|
|
99
|
+
});
|
|
100
|
+
resolve(canvas.toDataURL('image/png').split(',')[1]);
|
|
101
|
+
};
|
|
102
|
+
img.onerror = () => reject('failed to load screenshot into canvas');
|
|
103
|
+
img.src = 'data:image/png;base64,' + imgBase64;
|
|
104
|
+
});
|
|
105
|
+
}, { imgBase64: cleanBase64, elems: elements, colors: COLORS });
|
|
106
|
+
return Buffer.from(somBase64, 'base64');
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=som-annotator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"som-annotator.js","sourceRoot":"","sources":["../../src/som-annotator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** SoM 可交互元素 */
|
|
2
|
+
export interface SoMElement {
|
|
3
|
+
index: number;
|
|
4
|
+
tag: string;
|
|
5
|
+
role?: string;
|
|
6
|
+
text: string;
|
|
7
|
+
bbox: {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
attributes: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
/** 单步操作记录 */
|
|
16
|
+
export interface StepRecord {
|
|
17
|
+
stepId: number;
|
|
18
|
+
action: {
|
|
19
|
+
name: string;
|
|
20
|
+
selector?: string;
|
|
21
|
+
text?: string;
|
|
22
|
+
url?: string;
|
|
23
|
+
position?: {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
};
|
|
27
|
+
modifiers?: string[];
|
|
28
|
+
};
|
|
29
|
+
codegenCode: string;
|
|
30
|
+
timestamp: number;
|
|
31
|
+
pageUrl: string;
|
|
32
|
+
screenshots: {
|
|
33
|
+
clean: string;
|
|
34
|
+
som: string;
|
|
35
|
+
};
|
|
36
|
+
dom: {
|
|
37
|
+
html: string;
|
|
38
|
+
interactiveElements: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** 采集会话记录 */
|
|
42
|
+
export interface SessionRecord {
|
|
43
|
+
sessionId: string;
|
|
44
|
+
task: string;
|
|
45
|
+
startUrl: string;
|
|
46
|
+
startTime: string;
|
|
47
|
+
endTime?: string;
|
|
48
|
+
steps: StepRecord[];
|
|
49
|
+
codegenScript: string;
|
|
50
|
+
traceFile: string;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "benchmark-collector",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Playwright-based benchmark data collector for web agent training",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"benchmark-collect": "dist/scripts/collect.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/src/**/*.js",
|
|
12
|
+
"dist/src/**/*.d.ts",
|
|
13
|
+
"dist/src/**/*.js.map",
|
|
14
|
+
"dist/scripts/**/*.js"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"collect": "ts-node scripts/collect.ts",
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"postinstall": "npx playwright-core install chromium",
|
|
21
|
+
"test": "npx playwright test"
|
|
22
|
+
},
|
|
23
|
+
"keywords": ["benchmark", "playwright", "web-agent", "data-collection", "som"],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@playwright/test": "^1.58.2",
|
|
28
|
+
"@types/node": "^25.5.0",
|
|
29
|
+
"ts-node": "^10.9.2",
|
|
30
|
+
"typescript": "^5.7.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"playwright-core": "1.58.2"
|
|
34
|
+
}
|
|
35
|
+
}
|