@xbrowser/cli 0.14.3 → 0.15.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/README.md +63 -1
- package/dist/cli.js +126 -99
- package/dist/daemon-main.js +67 -34
- package/dist/index.d.ts +2026 -5
- package/dist/index.js +124 -97
- package/package.json +19 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,797 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { Server } from 'http';
|
|
3
|
+
import { Page, Browser, BrowserContext } from 'playwright';
|
|
4
|
+
import { CommandContext, CommandScope, XCLIAPI, Core, PluginInstance, PluginStatus } from '@dyyz1993/xcli-core';
|
|
5
|
+
export { PluginStatus } from '@dyyz1993/xcli-core';
|
|
6
|
+
import { ZodType, ZodTypeDef, z } from 'zod';
|
|
7
|
+
import * as playwright_core from 'playwright-core';
|
|
8
|
+
|
|
9
|
+
declare const version: any;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Configuration for the WebSocket server.
|
|
13
|
+
*/
|
|
14
|
+
interface WSServerConfig {
|
|
15
|
+
port?: number;
|
|
16
|
+
host?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Outbound WebSocket message types sent to connected clients.
|
|
20
|
+
*/
|
|
21
|
+
type WSMessage = {
|
|
22
|
+
type: 'screenshot';
|
|
23
|
+
data: ScreencastMessage;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'command';
|
|
26
|
+
data: CommandMessage;
|
|
27
|
+
} | {
|
|
28
|
+
type: 'status';
|
|
29
|
+
data: StatusMessage;
|
|
30
|
+
} | {
|
|
31
|
+
type: 'captcha-detected';
|
|
32
|
+
sessionId: string;
|
|
33
|
+
url: string;
|
|
34
|
+
reason: string;
|
|
35
|
+
timeout: number;
|
|
36
|
+
} | {
|
|
37
|
+
type: 'resolved';
|
|
38
|
+
sessionId: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'navigation';
|
|
41
|
+
url: string;
|
|
42
|
+
title: string;
|
|
43
|
+
} | {
|
|
44
|
+
type: 'input_focused';
|
|
45
|
+
selector: string;
|
|
46
|
+
value: string;
|
|
47
|
+
tag: string;
|
|
48
|
+
placeholder?: string;
|
|
49
|
+
} | {
|
|
50
|
+
type: 'input_blur';
|
|
51
|
+
selector: string;
|
|
52
|
+
} | {
|
|
53
|
+
type: 'file_upload_result';
|
|
54
|
+
success: boolean;
|
|
55
|
+
fileName: string;
|
|
56
|
+
error?: string;
|
|
57
|
+
} | {
|
|
58
|
+
type: 'file_list_result';
|
|
59
|
+
path: string;
|
|
60
|
+
files: Array<{
|
|
61
|
+
name: string;
|
|
62
|
+
isDir: boolean;
|
|
63
|
+
size: number;
|
|
64
|
+
modified: string;
|
|
65
|
+
}>;
|
|
66
|
+
error?: string;
|
|
67
|
+
} | {
|
|
68
|
+
type: 'file_download_result';
|
|
69
|
+
fileName: string;
|
|
70
|
+
mimeType: string;
|
|
71
|
+
data: string;
|
|
72
|
+
error?: string;
|
|
73
|
+
} | {
|
|
74
|
+
type: 'error';
|
|
75
|
+
data: {
|
|
76
|
+
code: string;
|
|
77
|
+
message: string;
|
|
78
|
+
availableSessions?: string[];
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Inbound WebSocket message types received from clients.
|
|
83
|
+
*/
|
|
84
|
+
type WSInboundMessage = {
|
|
85
|
+
type: 'click';
|
|
86
|
+
x: number;
|
|
87
|
+
y: number;
|
|
88
|
+
button?: 'left' | 'right';
|
|
89
|
+
} | {
|
|
90
|
+
type: 'type';
|
|
91
|
+
text: string;
|
|
92
|
+
} | {
|
|
93
|
+
type: 'keypress';
|
|
94
|
+
key: string;
|
|
95
|
+
} | {
|
|
96
|
+
type: 'scroll';
|
|
97
|
+
deltaX: number;
|
|
98
|
+
deltaY: number;
|
|
99
|
+
} | {
|
|
100
|
+
type: 'solved';
|
|
101
|
+
} | {
|
|
102
|
+
type: 'bind';
|
|
103
|
+
sessionId: string;
|
|
104
|
+
} | {
|
|
105
|
+
type: 'input_mouse';
|
|
106
|
+
action: 'move' | 'down' | 'up' | 'click';
|
|
107
|
+
x: number;
|
|
108
|
+
y: number;
|
|
109
|
+
button?: 'left' | 'middle' | 'right';
|
|
110
|
+
} | {
|
|
111
|
+
type: 'input_keyboard';
|
|
112
|
+
action: 'down' | 'up';
|
|
113
|
+
key: string;
|
|
114
|
+
modifiers?: number;
|
|
115
|
+
} | {
|
|
116
|
+
type: 'input_fill';
|
|
117
|
+
text: string;
|
|
118
|
+
selector: string;
|
|
119
|
+
} | {
|
|
120
|
+
type: 'input_insert_text';
|
|
121
|
+
text: string;
|
|
122
|
+
} | {
|
|
123
|
+
type: 'file_upload';
|
|
124
|
+
fileName: string;
|
|
125
|
+
mimeType: string;
|
|
126
|
+
data: string;
|
|
127
|
+
selector?: string;
|
|
128
|
+
} | {
|
|
129
|
+
type: 'file_list';
|
|
130
|
+
path: string;
|
|
131
|
+
} | {
|
|
132
|
+
type: 'file_download';
|
|
133
|
+
path: string;
|
|
134
|
+
} | {
|
|
135
|
+
type: 'focus_element';
|
|
136
|
+
selector: string;
|
|
137
|
+
} | {
|
|
138
|
+
type: 'focus_clear';
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* A screencast frame message with binary image data.
|
|
142
|
+
*/
|
|
143
|
+
interface ScreencastMessage {
|
|
144
|
+
sessionId: string;
|
|
145
|
+
id: string;
|
|
146
|
+
timestamp: number;
|
|
147
|
+
data: Buffer;
|
|
148
|
+
url: string;
|
|
149
|
+
viewport: {
|
|
150
|
+
width: number;
|
|
151
|
+
height: number;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* A command execution event message streamed during command lifecycle.
|
|
156
|
+
*/
|
|
157
|
+
interface CommandMessage {
|
|
158
|
+
sessionId: string;
|
|
159
|
+
command: string;
|
|
160
|
+
args: unknown[];
|
|
161
|
+
phase: 'before' | 'after';
|
|
162
|
+
result?: unknown;
|
|
163
|
+
error?: string;
|
|
164
|
+
timestamp: number;
|
|
165
|
+
duration?: number;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* A server status change notification.
|
|
169
|
+
*/
|
|
170
|
+
interface StatusMessage {
|
|
171
|
+
status: 'connected' | 'disconnected' | 'error';
|
|
172
|
+
sessionId?: string;
|
|
173
|
+
message?: string;
|
|
174
|
+
viewport?: {
|
|
175
|
+
width: number;
|
|
176
|
+
height: number;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* WebSocket server for streaming browser screenshots and handling remote input.
|
|
181
|
+
*
|
|
182
|
+
* Two modes:
|
|
183
|
+
* 1. **Standalone**: `start()` creates its own WS server on a port.
|
|
184
|
+
* 2. **Attached**: `attachToServer(httpServer)` shares an existing HTTP server
|
|
185
|
+
* via the WS upgrade mechanism (same port as HTTP, e.g., daemon port 9224).
|
|
186
|
+
*
|
|
187
|
+
* **Lazy screencast**: Screencast capture only starts when the first WS client
|
|
188
|
+
* binds to a session, and stops when the last client for that session disconnects.
|
|
189
|
+
*/
|
|
190
|
+
declare class WSServer extends EventEmitter {
|
|
191
|
+
private port;
|
|
192
|
+
private host;
|
|
193
|
+
private clients;
|
|
194
|
+
private sessionClients;
|
|
195
|
+
private screencasts;
|
|
196
|
+
private wsServer;
|
|
197
|
+
private isRunning;
|
|
198
|
+
private stateManager;
|
|
199
|
+
private frameRateController;
|
|
200
|
+
private frameProcessor;
|
|
201
|
+
private lastFrameData;
|
|
202
|
+
private lastFrameViewport;
|
|
203
|
+
private sessionCrops;
|
|
204
|
+
constructor(config?: WSServerConfig);
|
|
205
|
+
private processAndBroadcast;
|
|
206
|
+
/**
|
|
207
|
+
* Register a session page for screencast streaming.
|
|
208
|
+
* Call this when a session is created. The capturer will only start
|
|
209
|
+
* when a WS client binds to this session.
|
|
210
|
+
*/
|
|
211
|
+
registerSession(sessionId: string, page: Page, options?: {
|
|
212
|
+
interval?: number;
|
|
213
|
+
quality?: number;
|
|
214
|
+
type?: 'jpeg' | 'png';
|
|
215
|
+
width?: number;
|
|
216
|
+
height?: number;
|
|
217
|
+
}): void;
|
|
218
|
+
/**
|
|
219
|
+
* Unregister a session. Stops screencast if running.
|
|
220
|
+
*/
|
|
221
|
+
unregisterSession(sessionId: string): void;
|
|
222
|
+
/**
|
|
223
|
+
* Start standalone WS server on its own port.
|
|
224
|
+
*/
|
|
225
|
+
start(): Promise<void>;
|
|
226
|
+
/**
|
|
227
|
+
* Attach to an existing HTTP server via WS upgrade.
|
|
228
|
+
* Shares the same port as the HTTP server (e.g., daemon port 9224).
|
|
229
|
+
* The WS path defaults to `/preview`.
|
|
230
|
+
*/
|
|
231
|
+
attachToServer(httpServer: Server, path?: string): Promise<void>;
|
|
232
|
+
/**
|
|
233
|
+
* Set up the connection handler for incoming WS connections.
|
|
234
|
+
* When attached to an HTTP server, the third argument carries the sessionId
|
|
235
|
+
* extracted from the URL path (e.g., /preview/default).
|
|
236
|
+
*/
|
|
237
|
+
private setupConnectionHandler;
|
|
238
|
+
/**
|
|
239
|
+
* Lazy screencast: start when first client binds, stop when last unbinds.
|
|
240
|
+
*/
|
|
241
|
+
private startScreencastIfNeeded;
|
|
242
|
+
private stopScreencastIfNeeded;
|
|
243
|
+
pauseScreencast(sessionId: string): Promise<void>;
|
|
244
|
+
resumeScreencast(sessionId: string): Promise<void>;
|
|
245
|
+
private sendToClient;
|
|
246
|
+
private getClientPage;
|
|
247
|
+
private handleInboundMessage;
|
|
248
|
+
stop(): Promise<void>;
|
|
249
|
+
bindClientToSession(clientId: string, sessionId: string): void;
|
|
250
|
+
broadcastToSession(sessionId: string, message: WSMessage): void;
|
|
251
|
+
broadcast(message: WSMessage): void;
|
|
252
|
+
broadcastBinaryToSession(sessionId: string, payload: Buffer): void;
|
|
253
|
+
private handleClientDisconnect;
|
|
254
|
+
getClientCount(): number;
|
|
255
|
+
getSessionClientCount(sessionId: string): number;
|
|
256
|
+
getRunning(): boolean;
|
|
257
|
+
getPort(): number;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Result of a single command execution.
|
|
262
|
+
*/
|
|
263
|
+
interface HookOutput {
|
|
264
|
+
_hook: string;
|
|
265
|
+
[key: string]: unknown;
|
|
266
|
+
}
|
|
267
|
+
interface ExecutionResult {
|
|
268
|
+
success: boolean;
|
|
269
|
+
data: unknown;
|
|
270
|
+
message?: string;
|
|
271
|
+
duration: number;
|
|
272
|
+
tips?: string[];
|
|
273
|
+
hookOutputs?: HookOutput[];
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Result of a single step within a command chain execution.
|
|
277
|
+
*/
|
|
278
|
+
interface ChainStepResult {
|
|
279
|
+
command: string;
|
|
280
|
+
raw: string;
|
|
281
|
+
success: boolean;
|
|
282
|
+
data: unknown;
|
|
283
|
+
message?: string;
|
|
284
|
+
duration: number;
|
|
285
|
+
tips?: string[];
|
|
286
|
+
hookOutputs?: HookOutput[];
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Result of executing a command chain (multiple commands linked with &&, ||, etc.).
|
|
290
|
+
*/
|
|
291
|
+
interface ChainExecutionResult {
|
|
292
|
+
success: boolean;
|
|
293
|
+
steps: ChainStepResult[];
|
|
294
|
+
totalDuration: number;
|
|
295
|
+
stoppedAt?: number;
|
|
296
|
+
stoppedReason?: string;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Set or clear the WebSocket server used for streaming command events.
|
|
300
|
+
*
|
|
301
|
+
* @param server - A WSServer instance to stream events to, or null to disable.
|
|
302
|
+
*/
|
|
303
|
+
declare function setWSServer(server: WSServer | null): void;
|
|
304
|
+
/**
|
|
305
|
+
* Execute a single browser command against an existing session.
|
|
306
|
+
*
|
|
307
|
+
* Looks up the command by name, validates parameters via Zod schema,
|
|
308
|
+
* resolves the target session, and runs the command handler.
|
|
309
|
+
* Streams before/after events to the configured WebSocket server.
|
|
310
|
+
*
|
|
311
|
+
* @param commandName - Registered command name (e.g. "goto", "click", "fill").
|
|
312
|
+
* @param params - Key-value parameters forwarded to the command handler.
|
|
313
|
+
* @param sessionName - Name of the target session. Defaults to "default".
|
|
314
|
+
* @returns An {@link ExecutionResult} with success status, data, and duration.
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```ts
|
|
318
|
+
* const result = await executeCommand('click', { selector: '#submit' }, 'default');
|
|
319
|
+
* if (result.success) console.log('Clicked!', result.data);
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
declare function executeCommand(commandName: string, params: Record<string, unknown>, sessionName?: string, extraOpts?: {
|
|
323
|
+
cdpEndpoint?: string;
|
|
324
|
+
skipCleanup?: boolean;
|
|
325
|
+
}): Promise<ExecutionResult>;
|
|
326
|
+
/**
|
|
327
|
+
* Execute a chain of browser commands parsed from a string expression.
|
|
328
|
+
*
|
|
329
|
+
* Supports `&&` (and-chain, stops on first failure), `||` (or-chain, stops on
|
|
330
|
+
* first success), `;` (sequence separator), `->`, `,`, and `+` operators.
|
|
331
|
+
* Automatically creates a browser session if none exists and destroys it
|
|
332
|
+
* afterwards.
|
|
333
|
+
*
|
|
334
|
+
* @param input - Raw chain expression (e.g. `"goto https://example.com && click #btn"`).
|
|
335
|
+
* @param options - Optional configuration for CDP endpoint, session name, and file mode.
|
|
336
|
+
* @returns A {@link ChainExecutionResult} with per-step results and total duration.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```ts
|
|
340
|
+
* const result = await executeChain('goto https://example.com && click #btn');
|
|
341
|
+
* console.log(result.success, result.steps.length);
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
declare function executeChain(input: string, options?: {
|
|
345
|
+
cdpEndpoint?: string;
|
|
346
|
+
sessionName?: string;
|
|
347
|
+
fileMode?: boolean;
|
|
348
|
+
}): Promise<ChainExecutionResult>;
|
|
349
|
+
/**
|
|
350
|
+
* Check whether the given input string contains chain operators.
|
|
351
|
+
*
|
|
352
|
+
* Detects `&&`, `;`, `,`, `+`, and `->` surrounded by whitespace.
|
|
353
|
+
*
|
|
354
|
+
* @param input - The raw input string to test.
|
|
355
|
+
* @returns `true` if chain operators are present.
|
|
356
|
+
*/
|
|
357
|
+
declare function isChainInput(input: string): boolean;
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* CDP Interceptor — Core Types
|
|
361
|
+
*
|
|
362
|
+
* Models the JSON-RPC 2.0 messages flowing over the CDP WebSocket and
|
|
363
|
+
* defines the rule/decision system for intercepting, blocking, or
|
|
364
|
+
* transforming automation traffic to avoid anti-crawler detection.
|
|
365
|
+
*/
|
|
366
|
+
/** Raw JSON-RPC 2.0 request as it appears on the wire */
|
|
367
|
+
interface CDPRequest {
|
|
368
|
+
id: number;
|
|
369
|
+
method: string;
|
|
370
|
+
params?: Record<string, unknown>;
|
|
371
|
+
sessionId?: string;
|
|
372
|
+
}
|
|
373
|
+
/** Raw JSON-RPC 2.0 response (success) */
|
|
374
|
+
interface CDPResponse {
|
|
375
|
+
id: number;
|
|
376
|
+
result?: Record<string, unknown>;
|
|
377
|
+
sessionId?: string;
|
|
378
|
+
}
|
|
379
|
+
/** Raw JSON-RPC 2.0 error response */
|
|
380
|
+
interface CDPError {
|
|
381
|
+
id: number;
|
|
382
|
+
error: {
|
|
383
|
+
code: number;
|
|
384
|
+
message: string;
|
|
385
|
+
data?: unknown;
|
|
386
|
+
};
|
|
387
|
+
sessionId?: string;
|
|
388
|
+
}
|
|
389
|
+
/** Union of all possible CDP messages */
|
|
390
|
+
type CDPMessage = CDPRequest | CDPResponse | CDPError;
|
|
391
|
+
/** Direction: from client → browser or browser → client */
|
|
392
|
+
type MessageDirection = 'client→browser' | 'browser→client';
|
|
393
|
+
/** Structured log entry for a single CDP message */
|
|
394
|
+
interface CDPLogEntry {
|
|
395
|
+
/** Unix timestamp in ms */
|
|
396
|
+
timestamp: number;
|
|
397
|
+
/** Message direction */
|
|
398
|
+
direction: MessageDirection;
|
|
399
|
+
/** Session identifier (for grouping messages) */
|
|
400
|
+
sessionId: string;
|
|
401
|
+
/** CDP method (e.g. "Runtime.evaluate"), undefined for responses */
|
|
402
|
+
method?: string;
|
|
403
|
+
/** Sanitized payload summary */
|
|
404
|
+
payload: Record<string, unknown>;
|
|
405
|
+
/** Rule decision that was applied, if any */
|
|
406
|
+
decision?: DecisionResult;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Severity of a rule violation
|
|
410
|
+
*
|
|
411
|
+
* - **danger**: definitely triggers anti-crawler detection — block immediately
|
|
412
|
+
* - **warn**: likely problematic — log and warn, but don't block yet
|
|
413
|
+
* - **info**: informational only — could be a future concern
|
|
414
|
+
*/
|
|
415
|
+
type ViolationSeverity = 'danger' | 'warn' | 'info';
|
|
416
|
+
/**
|
|
417
|
+
* Action the interceptor should take for a matched message
|
|
418
|
+
*
|
|
419
|
+
* - **block**: reject the message, return CDP error, never reach browser
|
|
420
|
+
* - **transform**: modify the message before forwarding
|
|
421
|
+
* - **pass**: forward unchanged (log only)
|
|
422
|
+
*/
|
|
423
|
+
type DecisionAction = 'block' | 'transform' | 'pass';
|
|
424
|
+
/** Result of a rule evaluation */
|
|
425
|
+
interface DecisionResult {
|
|
426
|
+
/** Matched rule id */
|
|
427
|
+
ruleId: string;
|
|
428
|
+
action: DecisionAction;
|
|
429
|
+
severity: ViolationSeverity;
|
|
430
|
+
/** Human-readable explanation of why this was flagged */
|
|
431
|
+
reason: string;
|
|
432
|
+
/** Suggested fix / alternative approach */
|
|
433
|
+
suggestion?: string;
|
|
434
|
+
/** Transformed params (only for 'transform' action) */
|
|
435
|
+
transformedParams?: Record<string, unknown>;
|
|
436
|
+
/** Custom CDP error code (only for 'block' action) */
|
|
437
|
+
errorCode?: number;
|
|
438
|
+
/** Custom CDP error message (only for 'block' action) */
|
|
439
|
+
errorMessage?: string;
|
|
440
|
+
}
|
|
441
|
+
/** Handler context passed to each rule */
|
|
442
|
+
interface RuleContext {
|
|
443
|
+
method: string;
|
|
444
|
+
params: Record<string, unknown>;
|
|
445
|
+
sessionId: string;
|
|
446
|
+
/** Direction: the message is being sent TO the browser */
|
|
447
|
+
direction: MessageDirection;
|
|
448
|
+
/** Per-session state store (rules can stash data here) */
|
|
449
|
+
sessionState: Map<string, unknown>;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* A CDP interception rule
|
|
453
|
+
*
|
|
454
|
+
* Rules are evaluated **in priority order** (lower number = earlier).
|
|
455
|
+
* The first rule that returns a decision wins.
|
|
456
|
+
*/
|
|
457
|
+
interface CDPInterceptorRule {
|
|
458
|
+
/** Unique rule identifier */
|
|
459
|
+
id: string;
|
|
460
|
+
/** Human-readable name */
|
|
461
|
+
name: string;
|
|
462
|
+
/** Priority: lower numbers are evaluated first */
|
|
463
|
+
priority: number;
|
|
464
|
+
/**
|
|
465
|
+
* Quick pre-filter — skip expensive checks for messages that can't match.
|
|
466
|
+
* Return false to skip this rule.
|
|
467
|
+
*/
|
|
468
|
+
canHandle?(ctx: RuleContext): boolean;
|
|
469
|
+
/**
|
|
470
|
+
* Evaluate the message and return a decision.
|
|
471
|
+
* Return null to pass (no match).
|
|
472
|
+
*/
|
|
473
|
+
evaluate(ctx: RuleContext): DecisionResult | null;
|
|
474
|
+
}
|
|
475
|
+
/** Configuration for the CDP interceptor proxy */
|
|
476
|
+
interface CDPInterceptorConfig {
|
|
477
|
+
/** Chromium CDP WebSocket endpoint (e.g. ws://localhost:9222/...) */
|
|
478
|
+
cdpEndpoint: string;
|
|
479
|
+
/** Port to listen on for incoming client connections */
|
|
480
|
+
listenPort?: number;
|
|
481
|
+
/** Rules to apply, defaults to built-in rules */
|
|
482
|
+
rules?: CDPInterceptorRule[];
|
|
483
|
+
/** Enable logging (default: true) */
|
|
484
|
+
enableLogging?: boolean;
|
|
485
|
+
/** Log output directory (default: no file logging) */
|
|
486
|
+
logDir?: string;
|
|
487
|
+
/** Block mode: 'strict' blocks danger only, 'paranoid' blocks warn+ */
|
|
488
|
+
blockMode?: 'strict' | 'paranoid';
|
|
489
|
+
}
|
|
490
|
+
/** Statistics collected during a proxy session */
|
|
491
|
+
interface CDPInterceptorStats {
|
|
492
|
+
totalMessages: number;
|
|
493
|
+
blockedMessages: number;
|
|
494
|
+
transformedMessages: number;
|
|
495
|
+
passedMessages: number;
|
|
496
|
+
/** Breakdown by rule id */
|
|
497
|
+
byRule: Record<string, {
|
|
498
|
+
matched: number;
|
|
499
|
+
blocked: number;
|
|
500
|
+
transformed: number;
|
|
501
|
+
}>;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Options for the wait-for-human interaction flow.
|
|
506
|
+
*/
|
|
507
|
+
interface WaitForHumanOptions {
|
|
508
|
+
reason?: string;
|
|
509
|
+
timeout?: number;
|
|
510
|
+
autoDetect?: boolean;
|
|
511
|
+
detectInterval?: number;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Result of a wait-for-human interaction attempt.
|
|
515
|
+
*/
|
|
516
|
+
interface WaitForHumanResult {
|
|
517
|
+
solved: boolean;
|
|
518
|
+
method: 'preview' | 'auto-detected' | 'timeout' | 'manual';
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Manages human-in-the-loop interactions for CAPTCHA solving and manual intervention.
|
|
522
|
+
*
|
|
523
|
+
* Streams the page via screencast, sends webhook notifications, and waits
|
|
524
|
+
* for either auto-detection of CAPTCHA resolution, manual solving via
|
|
525
|
+
* the preview UI, or timeout.
|
|
526
|
+
*/
|
|
527
|
+
declare class HumanInteractionManager {
|
|
528
|
+
private wsServer;
|
|
529
|
+
private page;
|
|
530
|
+
private capturer;
|
|
531
|
+
private webhook;
|
|
532
|
+
private autoOpen;
|
|
533
|
+
constructor(wsServer: WSServer, page: Page);
|
|
534
|
+
private sendWebhook;
|
|
535
|
+
private tryAutoOpen;
|
|
536
|
+
/**
|
|
537
|
+
* Wait for a human to solve a CAPTCHA or complete an interaction.
|
|
538
|
+
*
|
|
539
|
+
* Starts screencast streaming, sends webhook and broadcast notifications,
|
|
540
|
+
* then polls for CAPTCHA resolution or waits for a manual solve signal.
|
|
541
|
+
*
|
|
542
|
+
* @param options - Configuration for timeout, auto-detection, and reason text.
|
|
543
|
+
* @returns Result indicating whether the CAPTCHA was solved and by what method.
|
|
544
|
+
*/
|
|
545
|
+
waitForHuman(options?: WaitForHumanOptions): Promise<WaitForHumanResult>;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Extended command context for browser automation commands.
|
|
550
|
+
*
|
|
551
|
+
* Provides Playwright Page, Browser, and BrowserContext instances,
|
|
552
|
+
* along with an optional `waitForHuman` function for CAPTCHA handling.
|
|
553
|
+
*/
|
|
554
|
+
interface BrowserCommandContext extends CommandContext {
|
|
555
|
+
page: Page;
|
|
556
|
+
browser: Browser;
|
|
557
|
+
browserContext: BrowserContext;
|
|
558
|
+
sessionId?: string;
|
|
559
|
+
cdpEndpoint?: string;
|
|
560
|
+
waitForHuman?: (options?: WaitForHumanOptions) => Promise<WaitForHumanResult>;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Validate that the required browser scope is available in the context.
|
|
564
|
+
*
|
|
565
|
+
* @param scope - The required command scope level.
|
|
566
|
+
* @param ctx - The current browser command context.
|
|
567
|
+
* @returns An error message string if the scope is not satisfied, or `null` if valid.
|
|
568
|
+
*/
|
|
569
|
+
declare function checkBrowserScope(scope: CommandScope, ctx: BrowserCommandContext): string | null;
|
|
570
|
+
/**
|
|
571
|
+
* Assert that the context has an active page, narrowing the type.
|
|
572
|
+
*
|
|
573
|
+
* @param ctx - The browser command context to validate.
|
|
574
|
+
* @throws If no active page is available in the context.
|
|
575
|
+
*/
|
|
576
|
+
declare function assertPageScope(ctx: BrowserCommandContext): asserts ctx is BrowserCommandContext & {
|
|
577
|
+
page: Page;
|
|
578
|
+
};
|
|
579
|
+
/**
|
|
580
|
+
* Attach a `waitForHuman` function to the browser command context.
|
|
581
|
+
*
|
|
582
|
+
* Lazily imports the human interaction manager and binds it to the
|
|
583
|
+
* context's page via the provided WebSocket server factory.
|
|
584
|
+
*
|
|
585
|
+
* @param ctx - The browser command context to augment.
|
|
586
|
+
* @param getOrCreateWSServer - Factory that returns a WSServer for the given BrowserContext.
|
|
587
|
+
*/
|
|
588
|
+
declare function attachWaitForHuman(ctx: BrowserCommandContext, getOrCreateWSServer: (browserContext: BrowserContext) => Promise<WSServer>): void;
|
|
589
|
+
/**
|
|
590
|
+
* Retrieve a cached WSServer instance for the given BrowserContext.
|
|
591
|
+
*
|
|
592
|
+
* @param browserContext - The Playwright BrowserContext.
|
|
593
|
+
* @returns The cached WSServer, or `undefined` if none is cached.
|
|
594
|
+
*/
|
|
595
|
+
declare function getWSServerFromCache(browserContext: BrowserContext): WSServer | undefined;
|
|
596
|
+
/**
|
|
597
|
+
* Cache a WSServer instance for the given BrowserContext.
|
|
598
|
+
*
|
|
599
|
+
* @param browserContext - The Playwright BrowserContext.
|
|
600
|
+
* @param server - The WSServer to associate with this context.
|
|
601
|
+
*/
|
|
602
|
+
declare function setWSServerCache(browserContext: BrowserContext, server: WSServer): void;
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Represents a managed browser session with its Playwright context, page,
|
|
606
|
+
* and its own dedicated Browser connection.
|
|
607
|
+
*/
|
|
608
|
+
interface ManagedSession {
|
|
609
|
+
id: string;
|
|
610
|
+
name: string;
|
|
611
|
+
context: BrowserContext;
|
|
612
|
+
page: Page;
|
|
613
|
+
browser: Browser;
|
|
614
|
+
createdAt: string;
|
|
615
|
+
lastActivityAt: number;
|
|
616
|
+
isCDP?: boolean;
|
|
617
|
+
cdpEndpoint?: string;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Options for launching or connecting to a browser instance.
|
|
621
|
+
*/
|
|
622
|
+
interface BrowserLaunchOptions {
|
|
623
|
+
headless?: boolean;
|
|
624
|
+
executablePath?: string;
|
|
625
|
+
cdpEndpoint?: string;
|
|
626
|
+
/** Enable CDP-level interception for anti-crawler protection */
|
|
627
|
+
intercept?: boolean | CDPInterceptorConfig;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Get or create the shared browser instance (lazy singleton).
|
|
631
|
+
*
|
|
632
|
+
* Used by non-session callers such as {@link createEphemeralContext} and
|
|
633
|
+
* project-scope commands that don't need per-session isolation.
|
|
634
|
+
*
|
|
635
|
+
* @param options - Launch options including headless mode, executable path, or CDP endpoint.
|
|
636
|
+
* @returns The shared Playwright Browser instance.
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* ```ts
|
|
640
|
+
* const browser = await getBrowser({ headless: true });
|
|
641
|
+
* ```
|
|
642
|
+
*/
|
|
643
|
+
declare function getBrowser(options?: BrowserLaunchOptions): Promise<Browser>;
|
|
644
|
+
/**
|
|
645
|
+
* Find a managed session by its name.
|
|
646
|
+
*
|
|
647
|
+
* @param name - The session name to search for.
|
|
648
|
+
* @returns The matching session, or `undefined` if not found.
|
|
649
|
+
*/
|
|
650
|
+
declare function findSession(name: string): ManagedSession | undefined;
|
|
651
|
+
/**
|
|
652
|
+
* Get all active managed sessions.
|
|
653
|
+
*
|
|
654
|
+
* @returns Array of all active sessions.
|
|
655
|
+
*/
|
|
656
|
+
declare function getAllSessions(): ManagedSession[];
|
|
657
|
+
/**
|
|
658
|
+
* Create a new browser session with a page and optional initial URL.
|
|
659
|
+
*
|
|
660
|
+
* Each session gets its own dedicated Browser connection. If connecting via
|
|
661
|
+
* CDP, reuses existing pages when possible instead of creating a new
|
|
662
|
+
* context/page pair.
|
|
663
|
+
*
|
|
664
|
+
* @param name - Unique name for the session.
|
|
665
|
+
* @param url - Optional URL to navigate to after creation.
|
|
666
|
+
* @param options - Browser launch or CDP connection options.
|
|
667
|
+
* @returns The newly created managed session.
|
|
668
|
+
*
|
|
669
|
+
* @example
|
|
670
|
+
* ```ts
|
|
671
|
+
* const session = await createSession('default', 'https://example.com');
|
|
672
|
+
* ```
|
|
673
|
+
*/
|
|
674
|
+
declare function createSession(name: string, url?: string, options?: BrowserLaunchOptions): Promise<ManagedSession>;
|
|
675
|
+
/**
|
|
676
|
+
* Close a session by its name or ID.
|
|
677
|
+
*
|
|
678
|
+
* @param name - Session name or UUID to close.
|
|
679
|
+
* @returns `true` if a session was found and closed, `false` otherwise.
|
|
680
|
+
*/
|
|
681
|
+
declare function closeSessionByName(name: string): Promise<boolean>;
|
|
682
|
+
/**
|
|
683
|
+
* Close all active browser sessions.
|
|
684
|
+
*
|
|
685
|
+
* Closes every managed context and its dedicated browser connection,
|
|
686
|
+
* ignoring individual close errors.
|
|
687
|
+
*/
|
|
688
|
+
declare function closeAllSessions(): Promise<void>;
|
|
689
|
+
/**
|
|
690
|
+
* Close all sessions and destroy all browser instances.
|
|
691
|
+
*
|
|
692
|
+
* After calling this, the module returns to a clean state and
|
|
693
|
+
* {@link getBrowser} will create a new instance on next call.
|
|
694
|
+
*/
|
|
695
|
+
declare function destroyBrowser(): Promise<void>;
|
|
696
|
+
/**
|
|
697
|
+
* Reset all internal state for testing purposes.
|
|
698
|
+
*
|
|
699
|
+
* Clears the session map and drops the browser reference without
|
|
700
|
+
* closing anything. Intended for use in test teardown.
|
|
701
|
+
*/
|
|
702
|
+
declare function resetForTesting(): void;
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Describes a single scope level in the browser automation hierarchy.
|
|
706
|
+
*/
|
|
707
|
+
interface ScopeLevel {
|
|
708
|
+
name: CommandScope;
|
|
709
|
+
description: string;
|
|
710
|
+
order: number;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* A named scope definition with a description and ordered levels.
|
|
714
|
+
*/
|
|
715
|
+
interface ScopeDefinition {
|
|
716
|
+
name: string;
|
|
717
|
+
description: string;
|
|
718
|
+
levels: ScopeLevel[];
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Built-in browser automation scope hierarchy.
|
|
722
|
+
*
|
|
723
|
+
* Defines four levels: project, browser, page, and element,
|
|
724
|
+
* ordered from coarsest to finest granularity.
|
|
725
|
+
*/
|
|
726
|
+
declare const BROWSER_SCOPE: ScopeDefinition;
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Definition of a browser command, including its Zod-validated parameters,
|
|
730
|
+
* scope, and handler function.
|
|
731
|
+
*/
|
|
732
|
+
interface BrowserCommandDefinition<P extends ZodType<unknown, ZodTypeDef, unknown> = ZodType<unknown, ZodTypeDef, unknown>> {
|
|
733
|
+
name: string;
|
|
734
|
+
description: string;
|
|
735
|
+
scope: CommandScope;
|
|
736
|
+
parameters?: P;
|
|
737
|
+
result?: ZodType<unknown>;
|
|
738
|
+
/**
|
|
739
|
+
* Declare which parameter keys contain CSS selectors.
|
|
740
|
+
* The executor will resolve `eN` ref values to real selectors before calling the handler.
|
|
741
|
+
*/
|
|
742
|
+
selectorParams?: string[];
|
|
743
|
+
handler: (params: z.infer<P>, ctx: BrowserCommandContext) => Promise<unknown>;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* A command that has been registered and is available for execution.
|
|
747
|
+
*/
|
|
748
|
+
interface RegisteredCommand {
|
|
749
|
+
readonly name: string;
|
|
750
|
+
readonly description: string;
|
|
751
|
+
readonly scope: CommandScope;
|
|
752
|
+
readonly parameters?: ZodType<unknown>;
|
|
753
|
+
readonly result?: ZodType<unknown>;
|
|
754
|
+
readonly selectorParams?: string[];
|
|
755
|
+
readonly handler: (params: Record<string, unknown>, ctx: BrowserCommandContext) => Promise<unknown>;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Register a browser command in the global registry.
|
|
759
|
+
*
|
|
760
|
+
* @param def - The command definition including name, description, scope, parameters, and handler.
|
|
761
|
+
* @returns The registered command instance.
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* ```ts
|
|
765
|
+
* registerCommand({
|
|
766
|
+
* name: 'click',
|
|
767
|
+
* description: 'Click an element',
|
|
768
|
+
* scope: 'element',
|
|
769
|
+
* result: z.object({ success: z.boolean() }),
|
|
770
|
+
* handler: async (params, ctx) => { await ctx.page.click(params.selector); return ok({ success: true }); },
|
|
771
|
+
* });
|
|
772
|
+
* ```
|
|
773
|
+
*/
|
|
774
|
+
declare function registerCommand<P extends ZodType<unknown, ZodTypeDef, unknown>>(def: BrowserCommandDefinition<P>): RegisteredCommand;
|
|
775
|
+
/**
|
|
776
|
+
* Retrieve a registered command by name.
|
|
777
|
+
*
|
|
778
|
+
* @param name - The command name.
|
|
779
|
+
* @returns The registered command, or `undefined` if not found.
|
|
780
|
+
*/
|
|
781
|
+
declare function getCommand(name: string): RegisteredCommand | undefined;
|
|
782
|
+
/**
|
|
783
|
+
* Get all registered commands.
|
|
784
|
+
*
|
|
785
|
+
* @returns Array of all registered commands.
|
|
786
|
+
*/
|
|
787
|
+
declare function getAllCommands(): RegisteredCommand[];
|
|
788
|
+
/**
|
|
789
|
+
* Get the names of all registered commands.
|
|
790
|
+
*
|
|
791
|
+
* @returns Array of command name strings.
|
|
792
|
+
*/
|
|
793
|
+
declare function getCommandNames(): string[];
|
|
794
|
+
|
|
1
795
|
interface AISearchResultItem {
|
|
2
796
|
title: string;
|
|
3
797
|
url: string;
|
|
@@ -49,6 +843,740 @@ interface AISearchResult {
|
|
|
49
843
|
duration?: string;
|
|
50
844
|
}
|
|
51
845
|
|
|
846
|
+
/**
|
|
847
|
+
* Route CLI arguments to the appropriate handler.
|
|
848
|
+
*
|
|
849
|
+
* Dispatches stdin commands, eval flags, chain input, and sub-commands
|
|
850
|
+
* (session, plugin, daemon, record, replay, etc.) to their respective
|
|
851
|
+
* handler functions.
|
|
852
|
+
*
|
|
853
|
+
* @param argv - Raw CLI argument array (typically `process.argv.slice(2)`).
|
|
854
|
+
* @param stdinCommands - Optional array of commands read from stdin.
|
|
855
|
+
*/
|
|
856
|
+
declare function routeCommand(argv: string[], stdinCommands?: string[]): Promise<void>;
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Read non-empty, non-comment lines from stdin.
|
|
860
|
+
*
|
|
861
|
+
* Returns an empty array when stdin is a TTY. Lines starting with `#` are
|
|
862
|
+
* treated as comments and skipped.
|
|
863
|
+
*
|
|
864
|
+
* @returns Array of trimmed, non-comment lines.
|
|
865
|
+
*/
|
|
866
|
+
declare function readStdin(): Promise<string[]>;
|
|
867
|
+
/**
|
|
868
|
+
* Read a command file and return non-empty, non-comment lines.
|
|
869
|
+
*
|
|
870
|
+
* @param filePath - Path to the file to read.
|
|
871
|
+
* @returns Array of trimmed, non-comment lines.
|
|
872
|
+
*/
|
|
873
|
+
declare function readCommandFile(filePath: string): string[];
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Open a new browser session, navigate to the given URL, and persist session metadata.
|
|
877
|
+
*
|
|
878
|
+
* @param name - Unique name for the session.
|
|
879
|
+
* @param url - The initial URL to navigate to.
|
|
880
|
+
* @param options - Optional CDP endpoint configuration.
|
|
881
|
+
* @returns Session info including id, name, url, and creation timestamp.
|
|
882
|
+
*
|
|
883
|
+
* @example
|
|
884
|
+
* ```ts
|
|
885
|
+
* const info = await openSession('default', 'https://example.com');
|
|
886
|
+
* ```
|
|
887
|
+
*/
|
|
888
|
+
declare function openSession(name: string, url: string, options?: {
|
|
889
|
+
cdpEndpoint?: string;
|
|
890
|
+
}): Promise<{
|
|
891
|
+
id: string;
|
|
892
|
+
name: string;
|
|
893
|
+
url: string;
|
|
894
|
+
createdAt: string;
|
|
895
|
+
}>;
|
|
896
|
+
/**
|
|
897
|
+
* Close a browser session by name.
|
|
898
|
+
*
|
|
899
|
+
* @param name - The session name to close.
|
|
900
|
+
*/
|
|
901
|
+
declare function closeSession(name: string): Promise<void>;
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* List all active sessions with their IDs and names.
|
|
905
|
+
*
|
|
906
|
+
* @returns Array of objects with `id` and `name` fields.
|
|
907
|
+
*/
|
|
908
|
+
declare function listSessions(): Promise<Array<{
|
|
909
|
+
id: string;
|
|
910
|
+
name: string;
|
|
911
|
+
}>>;
|
|
912
|
+
/**
|
|
913
|
+
* Get the Playwright Page for a named session.
|
|
914
|
+
*
|
|
915
|
+
* @param name - The session name. Defaults to "default".
|
|
916
|
+
* @returns The page instance, or `null` if the session does not exist.
|
|
917
|
+
*/
|
|
918
|
+
declare function getSessionPage(name?: string, cdpEndpoint?: string): Promise<playwright_core.Page | null>;
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* A built-in CLI command with help text and an execute function.
|
|
922
|
+
*/
|
|
923
|
+
interface BuiltinCommand {
|
|
924
|
+
name: string;
|
|
925
|
+
description: string;
|
|
926
|
+
aliases?: string[];
|
|
927
|
+
help: {
|
|
928
|
+
usage: string;
|
|
929
|
+
description: string;
|
|
930
|
+
options: {
|
|
931
|
+
name: string;
|
|
932
|
+
description: string;
|
|
933
|
+
}[];
|
|
934
|
+
examples?: {
|
|
935
|
+
cmd: string;
|
|
936
|
+
description: string;
|
|
937
|
+
}[];
|
|
938
|
+
};
|
|
939
|
+
execute: (args: string[], options: Record<string, unknown>, ctx: BuiltinContext) => Promise<void>;
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Minimal execution context for built-in commands.
|
|
943
|
+
*/
|
|
944
|
+
interface BuiltinContext {
|
|
945
|
+
cwd: string;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* All built-in CLI commands (session, config, plugin, create, preview).
|
|
950
|
+
*/
|
|
951
|
+
declare const allBuiltins: BuiltinCommand[];
|
|
952
|
+
/**
|
|
953
|
+
* Find a built-in command by name or alias.
|
|
954
|
+
*
|
|
955
|
+
* @param name - The command name or alias to look up.
|
|
956
|
+
* @returns The matching built-in command, or `undefined` if not found.
|
|
957
|
+
*/
|
|
958
|
+
declare function getBuiltin(name: string): BuiltinCommand | undefined;
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Options for configuring the plugin loader's search directories.
|
|
962
|
+
*/
|
|
963
|
+
interface PluginLoaderOptions {
|
|
964
|
+
cwd?: string;
|
|
965
|
+
userDir?: string;
|
|
966
|
+
globalDir?: string;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Plugin loader for discovering and managing xbrowser plugins.
|
|
970
|
+
*
|
|
971
|
+
* Wraps the xcli-core PluginLoader and provides xbrowser-specific
|
|
972
|
+
* directory conventions for plugin discovery.
|
|
973
|
+
*/
|
|
974
|
+
declare class XBrowserPluginLoader {
|
|
975
|
+
private core;
|
|
976
|
+
private loader;
|
|
977
|
+
private options;
|
|
978
|
+
constructor(options?: PluginLoaderOptions);
|
|
979
|
+
getAPI(): XCLIAPI;
|
|
980
|
+
/**
|
|
981
|
+
* Get the core instance for external use.
|
|
982
|
+
* @returns The xcli-core Core instance.
|
|
983
|
+
*/
|
|
984
|
+
getCore(): Core;
|
|
985
|
+
getPlugin(id: string): PluginInstance | undefined;
|
|
986
|
+
getPluginStatus(id: string): PluginStatus;
|
|
987
|
+
getLoadedPlugins(): PluginInstance[];
|
|
988
|
+
loadPlugin(pluginPath: string, id?: string): Promise<PluginInstance>;
|
|
989
|
+
unloadPlugin(id: string): Promise<void>;
|
|
990
|
+
reloadPlugin(id: string): Promise<PluginInstance>;
|
|
991
|
+
loadFromFunction(setup: (api: XCLIAPI) => void): Promise<void>;
|
|
992
|
+
scanAndLoad(): Promise<PluginInstance[]>;
|
|
993
|
+
unload(): Promise<void>;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
interface XBrowserPluginMetadata {
|
|
997
|
+
id: string;
|
|
998
|
+
name: string;
|
|
999
|
+
description: string;
|
|
1000
|
+
version: string;
|
|
1001
|
+
author: string;
|
|
1002
|
+
homepage?: string;
|
|
1003
|
+
commands?: string[];
|
|
1004
|
+
sites?: string[];
|
|
1005
|
+
tags?: string[];
|
|
1006
|
+
screenshot?: string;
|
|
1007
|
+
license?: string;
|
|
1008
|
+
}
|
|
1009
|
+
interface PluginListOptions {
|
|
1010
|
+
json?: boolean;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
interface InstalledPlugin {
|
|
1014
|
+
id: string;
|
|
1015
|
+
name: string;
|
|
1016
|
+
path: string;
|
|
1017
|
+
source: 'local' | 'npm' | 'git' | 'url' | 'builtin' | 'marketplace';
|
|
1018
|
+
installedAt: string;
|
|
1019
|
+
metadata?: XBrowserPluginMetadata;
|
|
1020
|
+
warnings?: string[];
|
|
1021
|
+
}
|
|
1022
|
+
interface InstallOptions {
|
|
1023
|
+
name?: string;
|
|
1024
|
+
force?: boolean;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Manages installation, uninstallation, and listing of plugins.
|
|
1029
|
+
*
|
|
1030
|
+
* Supports local paths, npm packages, git repositories, and URL sources.
|
|
1031
|
+
*/
|
|
1032
|
+
declare class PluginInstaller {
|
|
1033
|
+
private pluginsDir;
|
|
1034
|
+
constructor(pluginsDir?: string);
|
|
1035
|
+
getPluginsDir(): string;
|
|
1036
|
+
/**
|
|
1037
|
+
* Install a plugin from a local path, npm package, git repository, or URL.
|
|
1038
|
+
*
|
|
1039
|
+
* @param source - The plugin source (local path, npm name, git URL, or HTTP URL).
|
|
1040
|
+
* @param options - Install options including name override and force flag.
|
|
1041
|
+
* @returns Information about the installed plugin.
|
|
1042
|
+
* @throws If the plugin already exists without `force`, or if installation fails.
|
|
1043
|
+
*/
|
|
1044
|
+
install(source: string, options?: InstallOptions): Promise<InstalledPlugin>;
|
|
1045
|
+
installFromMarketplace(slug: string, options?: InstallOptions): Promise<InstalledPlugin>;
|
|
1046
|
+
installWithMarketplaceFallback(source: string, options?: InstallOptions): Promise<InstalledPlugin>;
|
|
1047
|
+
/**
|
|
1048
|
+
* Uninstall a plugin by name.
|
|
1049
|
+
*
|
|
1050
|
+
* @param name - The plugin directory name to remove.
|
|
1051
|
+
* @throws If the plugin is not installed.
|
|
1052
|
+
*/
|
|
1053
|
+
uninstall(name: string): Promise<void>;
|
|
1054
|
+
/**
|
|
1055
|
+
* List all installed plugins with metadata.
|
|
1056
|
+
*
|
|
1057
|
+
* @returns Array of installed plugin information.
|
|
1058
|
+
*/
|
|
1059
|
+
list(_options?: PluginListOptions): Promise<InstalledPlugin[]>;
|
|
1060
|
+
private detectSourceType;
|
|
1061
|
+
private deriveName;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* A single recorded browser event (click, type, scroll, navigate, etc.).
|
|
1066
|
+
*/
|
|
1067
|
+
interface RecordedEvent {
|
|
1068
|
+
id: string;
|
|
1069
|
+
type: 'click' | 'type' | 'scroll' | 'navigate' | 'keypress' | 'page_load';
|
|
1070
|
+
timestamp: number;
|
|
1071
|
+
selector?: string;
|
|
1072
|
+
data?: Record<string, unknown>;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* A complete recording session with metadata and captured events.
|
|
1076
|
+
*/
|
|
1077
|
+
interface RecordingSession {
|
|
1078
|
+
id: string;
|
|
1079
|
+
name: string;
|
|
1080
|
+
startUrl: string;
|
|
1081
|
+
startTime: string;
|
|
1082
|
+
duration: number;
|
|
1083
|
+
events: RecordedEvent[];
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Current status of an active recording.
|
|
1087
|
+
*/
|
|
1088
|
+
interface RecorderStatus {
|
|
1089
|
+
isRecording: boolean;
|
|
1090
|
+
eventCount: number;
|
|
1091
|
+
duration: number;
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Controller for recording browser interactions on a page.
|
|
1095
|
+
*
|
|
1096
|
+
* Injects a client-side recorder script that captures click, input,
|
|
1097
|
+
* scroll, and keyboard events. The recording can be stopped and saved
|
|
1098
|
+
* to a YAML file.
|
|
1099
|
+
*/
|
|
1100
|
+
declare class RecorderController {
|
|
1101
|
+
private page;
|
|
1102
|
+
private isRecordingFlag;
|
|
1103
|
+
private events;
|
|
1104
|
+
private startTime;
|
|
1105
|
+
private startUrl;
|
|
1106
|
+
private name;
|
|
1107
|
+
constructor(page: Page);
|
|
1108
|
+
/**
|
|
1109
|
+
* Start recording browser interactions.
|
|
1110
|
+
*
|
|
1111
|
+
* Injects the recorder script into the page and optionally navigates
|
|
1112
|
+
* to a starting URL.
|
|
1113
|
+
*
|
|
1114
|
+
* @param options - Recording options with optional starting URL and session name.
|
|
1115
|
+
* @throws If a recording is already in progress.
|
|
1116
|
+
*/
|
|
1117
|
+
start(options?: {
|
|
1118
|
+
url?: string;
|
|
1119
|
+
name?: string;
|
|
1120
|
+
}): Promise<void>;
|
|
1121
|
+
/**
|
|
1122
|
+
* Stop the current recording and save it to a YAML file.
|
|
1123
|
+
*
|
|
1124
|
+
* Collects all captured events from the page, stops the recorder,
|
|
1125
|
+
* and writes the session to disk.
|
|
1126
|
+
*
|
|
1127
|
+
* @param outputPath - Optional file path to save the recording. Defaults to `recordings/` directory.
|
|
1128
|
+
* @returns An object with the output file path and the recording session data.
|
|
1129
|
+
* @throws If no recording is in progress.
|
|
1130
|
+
*/
|
|
1131
|
+
stop(outputPath?: string): Promise<{
|
|
1132
|
+
path: string;
|
|
1133
|
+
session: RecordingSession;
|
|
1134
|
+
}>;
|
|
1135
|
+
/**
|
|
1136
|
+
* Get the current recording status.
|
|
1137
|
+
*
|
|
1138
|
+
* @returns The recorder status, or `null` if not recording.
|
|
1139
|
+
*/
|
|
1140
|
+
getStatus(): RecorderStatus | null;
|
|
1141
|
+
private getDefaultOutputPath;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
interface ClickContextItem {
|
|
1145
|
+
text: string;
|
|
1146
|
+
tag?: string;
|
|
1147
|
+
disabled?: boolean;
|
|
1148
|
+
href?: string;
|
|
1149
|
+
}
|
|
1150
|
+
interface ClickContextElement {
|
|
1151
|
+
tag: string;
|
|
1152
|
+
selector?: string;
|
|
1153
|
+
role?: string;
|
|
1154
|
+
text: string;
|
|
1155
|
+
rect?: {
|
|
1156
|
+
x: number;
|
|
1157
|
+
y: number;
|
|
1158
|
+
w: number;
|
|
1159
|
+
h: number;
|
|
1160
|
+
};
|
|
1161
|
+
items: ClickContextItem[];
|
|
1162
|
+
}
|
|
1163
|
+
interface ClickContextStateChange {
|
|
1164
|
+
tag: string;
|
|
1165
|
+
text: string;
|
|
1166
|
+
id?: string;
|
|
1167
|
+
ariaExpanded?: string;
|
|
1168
|
+
ariaSelected?: string;
|
|
1169
|
+
disabled?: boolean;
|
|
1170
|
+
dataState?: string;
|
|
1171
|
+
changed?: boolean;
|
|
1172
|
+
}
|
|
1173
|
+
interface ClickContext {
|
|
1174
|
+
appeared: ClickContextElement[];
|
|
1175
|
+
disappeared: unknown[];
|
|
1176
|
+
stateChanges: ClickContextStateChange[];
|
|
1177
|
+
}
|
|
1178
|
+
interface UserAction {
|
|
1179
|
+
id: number;
|
|
1180
|
+
type: 'click' | 'input' | 'change' | 'keydown' | 'submit' | 'scroll';
|
|
1181
|
+
timestamp: number;
|
|
1182
|
+
url: string;
|
|
1183
|
+
pageTitle: string;
|
|
1184
|
+
element?: {
|
|
1185
|
+
tag: string;
|
|
1186
|
+
selector?: string;
|
|
1187
|
+
text: string;
|
|
1188
|
+
role?: string;
|
|
1189
|
+
type?: string;
|
|
1190
|
+
placeholder?: string;
|
|
1191
|
+
ariaLabel?: string;
|
|
1192
|
+
href?: string;
|
|
1193
|
+
};
|
|
1194
|
+
value?: string;
|
|
1195
|
+
key?: string;
|
|
1196
|
+
x?: number;
|
|
1197
|
+
y?: number;
|
|
1198
|
+
scrollX?: number;
|
|
1199
|
+
scrollY?: number;
|
|
1200
|
+
/** Click context: popover/dropdown/menu items captured 200ms after click */
|
|
1201
|
+
clickContext?: ClickContext;
|
|
1202
|
+
}
|
|
1203
|
+
interface NetworkEntry {
|
|
1204
|
+
id: number;
|
|
1205
|
+
timestamp: number;
|
|
1206
|
+
method: string;
|
|
1207
|
+
url: string;
|
|
1208
|
+
path: string;
|
|
1209
|
+
status: number;
|
|
1210
|
+
resourceType: string;
|
|
1211
|
+
contentType: string;
|
|
1212
|
+
requestBody?: unknown;
|
|
1213
|
+
responseBody?: unknown;
|
|
1214
|
+
responseSize: number;
|
|
1215
|
+
}
|
|
1216
|
+
interface ContextChange {
|
|
1217
|
+
id: number;
|
|
1218
|
+
timestamp: number;
|
|
1219
|
+
type: 'navigate' | 'new_tab' | 'tab_closed';
|
|
1220
|
+
url?: string;
|
|
1221
|
+
detail?: string;
|
|
1222
|
+
}
|
|
1223
|
+
interface ElementRef {
|
|
1224
|
+
selector: string;
|
|
1225
|
+
tag: string;
|
|
1226
|
+
text: string;
|
|
1227
|
+
role?: string;
|
|
1228
|
+
type?: string;
|
|
1229
|
+
placeholder?: string;
|
|
1230
|
+
ariaLabel?: string;
|
|
1231
|
+
href?: string;
|
|
1232
|
+
}
|
|
1233
|
+
interface RecordingStep {
|
|
1234
|
+
step: number;
|
|
1235
|
+
ref: string;
|
|
1236
|
+
action: UserAction;
|
|
1237
|
+
network: NetworkEntry[];
|
|
1238
|
+
contextChanges: ContextChange[];
|
|
1239
|
+
matchedInputs: Array<{
|
|
1240
|
+
inputValue: string;
|
|
1241
|
+
networkId: number;
|
|
1242
|
+
paramName: string;
|
|
1243
|
+
}>;
|
|
1244
|
+
}
|
|
1245
|
+
interface RecordingSummary {
|
|
1246
|
+
startUrl: string;
|
|
1247
|
+
recordedAt: string;
|
|
1248
|
+
durationMs: number;
|
|
1249
|
+
totalActions: number;
|
|
1250
|
+
totalNetworkRequests: number;
|
|
1251
|
+
steps: RecordingStep[];
|
|
1252
|
+
elements: Record<string, ElementRef>;
|
|
1253
|
+
checkpoints: CheckpointEntry[];
|
|
1254
|
+
}
|
|
1255
|
+
type CheckpointType = 'dialog' | 'captcha' | 'login' | 'iframe' | 'slider' | 'custom';
|
|
1256
|
+
interface CheckpointEntry {
|
|
1257
|
+
id: number;
|
|
1258
|
+
type: CheckpointType;
|
|
1259
|
+
timestamp: number;
|
|
1260
|
+
url: string;
|
|
1261
|
+
pageTitle: string;
|
|
1262
|
+
hint: string;
|
|
1263
|
+
selector?: string;
|
|
1264
|
+
source: 'auto' | 'manual';
|
|
1265
|
+
relatedActionId?: number;
|
|
1266
|
+
context?: Record<string, unknown>;
|
|
1267
|
+
}
|
|
1268
|
+
interface RecordingData {
|
|
1269
|
+
startUrl: string;
|
|
1270
|
+
sessionName: string;
|
|
1271
|
+
startedAt: string;
|
|
1272
|
+
actions: UserAction[];
|
|
1273
|
+
network: NetworkEntry[];
|
|
1274
|
+
contextChanges: ContextChange[];
|
|
1275
|
+
checkpoints: CheckpointEntry[];
|
|
1276
|
+
}
|
|
1277
|
+
/** Written to disk so `record stop` (separate process) can signal the recorder. */
|
|
1278
|
+
interface RecordingControlFile {
|
|
1279
|
+
pid: number;
|
|
1280
|
+
startedAt: string;
|
|
1281
|
+
startUrl: string;
|
|
1282
|
+
sessionName: string;
|
|
1283
|
+
}
|
|
1284
|
+
declare class SessionRecorder {
|
|
1285
|
+
private context;
|
|
1286
|
+
private page;
|
|
1287
|
+
private sessionName;
|
|
1288
|
+
private startUrl;
|
|
1289
|
+
private startedAt;
|
|
1290
|
+
private actions;
|
|
1291
|
+
private network;
|
|
1292
|
+
private contextChanges;
|
|
1293
|
+
private checkpoints;
|
|
1294
|
+
private actionCounter;
|
|
1295
|
+
private networkCounter;
|
|
1296
|
+
private contextCounter;
|
|
1297
|
+
private checkpointCounter;
|
|
1298
|
+
private pollTimer;
|
|
1299
|
+
private flushTimer;
|
|
1300
|
+
private lastActionTs;
|
|
1301
|
+
private activePages;
|
|
1302
|
+
private _isRecording;
|
|
1303
|
+
constructor(context: BrowserContext, page: Page, sessionName: string);
|
|
1304
|
+
get isRecording(): boolean;
|
|
1305
|
+
get actionCount(): number;
|
|
1306
|
+
get networkCount(): number;
|
|
1307
|
+
getLiveData(): RecordingData;
|
|
1308
|
+
addManualCheckpoint(type: string, hint: string, selector?: string): CheckpointEntry;
|
|
1309
|
+
/** Directory for this session's recordings. */
|
|
1310
|
+
get recordingsDir(): string;
|
|
1311
|
+
static getRecordingsDir(sessionName: string): string;
|
|
1312
|
+
/** Path to the control file (used by record stop to signal this process). */
|
|
1313
|
+
get controlFilePath(): string;
|
|
1314
|
+
/** Path to the stop signal file (written by `record stop`). */
|
|
1315
|
+
get stopSignalPath(): string;
|
|
1316
|
+
start(url?: string): Promise<void>;
|
|
1317
|
+
stop(): Promise<{
|
|
1318
|
+
data: RecordingData;
|
|
1319
|
+
summary: RecordingSummary;
|
|
1320
|
+
}>;
|
|
1321
|
+
static cleanup(sessionName: string): void;
|
|
1322
|
+
waitForStopSignal(): Promise<void>;
|
|
1323
|
+
static sendStopSignal(sessionName: string): Promise<RecordingControlFile | null>;
|
|
1324
|
+
static readSummary(sessionName: string): RecordingSummary | null;
|
|
1325
|
+
static readData(sessionName: string): RecordingData | null;
|
|
1326
|
+
private injectActionScript;
|
|
1327
|
+
private handleRequest;
|
|
1328
|
+
private handleResponse;
|
|
1329
|
+
private handleNewPage;
|
|
1330
|
+
private handleFrameNavigated;
|
|
1331
|
+
private handleDialog;
|
|
1332
|
+
private pollActions;
|
|
1333
|
+
private flushPendingActions;
|
|
1334
|
+
/**
|
|
1335
|
+
* After a click, wait 300ms then scan for popover/dropdown/menu elements
|
|
1336
|
+
* near the click position. This runs server-side to avoid race conditions
|
|
1337
|
+
* with the client-side poll interval.
|
|
1338
|
+
*/
|
|
1339
|
+
private captureClickContext;
|
|
1340
|
+
private detectCheckpoints;
|
|
1341
|
+
private flushToDisk;
|
|
1342
|
+
private writeFinalOutput;
|
|
1343
|
+
private buildData;
|
|
1344
|
+
private buildSummary;
|
|
1345
|
+
private matchActionToNetwork;
|
|
1346
|
+
private searchObjectForValue;
|
|
1347
|
+
private buildMarkdownSummary;
|
|
1348
|
+
static readMarkdownSummary(sessionName: string): string | null;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
interface PlaybackOptions {
|
|
1352
|
+
slowMo?: number;
|
|
1353
|
+
stopOnError?: boolean;
|
|
1354
|
+
onProgress?: (info: {
|
|
1355
|
+
current: number;
|
|
1356
|
+
total: number;
|
|
1357
|
+
event: RecordedEvent;
|
|
1358
|
+
}) => void;
|
|
1359
|
+
onCheckpoint?: (checkpoint: {
|
|
1360
|
+
type: string;
|
|
1361
|
+
hint: string;
|
|
1362
|
+
selector?: string;
|
|
1363
|
+
}) => Promise<boolean>;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Result of playing back a recording session.
|
|
1367
|
+
*/
|
|
1368
|
+
interface PlaybackResult {
|
|
1369
|
+
success: boolean;
|
|
1370
|
+
duration: number;
|
|
1371
|
+
eventsPlayed: number;
|
|
1372
|
+
totalEvents: number;
|
|
1373
|
+
errors: Array<{
|
|
1374
|
+
eventIndex: number;
|
|
1375
|
+
event: RecordedEvent;
|
|
1376
|
+
error: string;
|
|
1377
|
+
}>;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Engine for playing back a recorded browser session.
|
|
1381
|
+
*
|
|
1382
|
+
* Replays events (click, type, scroll, navigate, keypress) on a live
|
|
1383
|
+
* Playwright page, respecting original timing with an optional slow-motion factor.
|
|
1384
|
+
*/
|
|
1385
|
+
declare class PlaybackEngine {
|
|
1386
|
+
private page;
|
|
1387
|
+
private recording;
|
|
1388
|
+
private checkpoints;
|
|
1389
|
+
constructor(page: Page, recording: RecordingSession);
|
|
1390
|
+
/**
|
|
1391
|
+
* Create a PlaybackEngine from a YAML recording file.
|
|
1392
|
+
*
|
|
1393
|
+
* @param page - The Playwright page to replay events on.
|
|
1394
|
+
* @param filePath - Path to the YAML recording file.
|
|
1395
|
+
* @returns A new PlaybackEngine instance.
|
|
1396
|
+
*/
|
|
1397
|
+
static fromFile(page: Page, filePath: string): PlaybackEngine;
|
|
1398
|
+
withCheckpoints(checkpoints: CheckpointEntry[]): PlaybackEngine;
|
|
1399
|
+
play(options?: PlaybackOptions): Promise<PlaybackResult>;
|
|
1400
|
+
private executeEvent;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
interface DaemonInfo {
|
|
1404
|
+
pid: number;
|
|
1405
|
+
port: number;
|
|
1406
|
+
startedAt: string;
|
|
1407
|
+
cdpEndpoint?: string;
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Start the daemon process. Spawns daemon-main.js as a detached child.
|
|
1411
|
+
*
|
|
1412
|
+
* Uses xcli-core's isDaemonRunning() for quick check, then getDaemonStatus()
|
|
1413
|
+
* for detailed health polling after spawn.
|
|
1414
|
+
*
|
|
1415
|
+
* Does NOT use xcli-core's startDaemon() because that spawns with
|
|
1416
|
+
* --import tsx, which requires tsx to be installed. xbrowser's
|
|
1417
|
+
* daemon-main.js is compiled JS and loads without tsx.
|
|
1418
|
+
*/
|
|
1419
|
+
declare function startDaemonProcess(port?: number): Promise<DaemonInfo>;
|
|
1420
|
+
/**
|
|
1421
|
+
* Stop the daemon process using xcli-core's stopDaemon().
|
|
1422
|
+
*/
|
|
1423
|
+
declare function stopDaemonProcess(): Promise<void>;
|
|
1424
|
+
/**
|
|
1425
|
+
* Get daemon process status using xcli-core's isDaemonRunning() + getDaemonStatus().
|
|
1426
|
+
*/
|
|
1427
|
+
declare function getDaemonProcessStatus(): {
|
|
1428
|
+
running: boolean;
|
|
1429
|
+
pid: number;
|
|
1430
|
+
port: number;
|
|
1431
|
+
info: DaemonInfo | null;
|
|
1432
|
+
};
|
|
1433
|
+
|
|
1434
|
+
/**
|
|
1435
|
+
* Result of a CAPTCHA detection scan.
|
|
1436
|
+
*/
|
|
1437
|
+
interface CaptchaDetectionResult {
|
|
1438
|
+
detected: boolean;
|
|
1439
|
+
type?: string;
|
|
1440
|
+
selector?: string;
|
|
1441
|
+
confidence: 'high' | 'medium' | 'low';
|
|
1442
|
+
}
|
|
1443
|
+
/**
|
|
1444
|
+
* Detect CAPTCHAs on the current page using selector-based rules and text patterns.
|
|
1445
|
+
*
|
|
1446
|
+
* Supports reCAPTCHA, hCaptcha, Cloudflare Turnstile, and generic CAPTCHAs.
|
|
1447
|
+
*/
|
|
1448
|
+
declare class CaptchaDetector {
|
|
1449
|
+
/**
|
|
1450
|
+
* Scan the page for visible CAPTCHA elements or challenge text.
|
|
1451
|
+
*
|
|
1452
|
+
* @param page - The Playwright page to scan.
|
|
1453
|
+
* @returns Detection result with type, selector, and confidence level.
|
|
1454
|
+
*/
|
|
1455
|
+
static detect(page: Page): Promise<CaptchaDetectionResult>;
|
|
1456
|
+
/**
|
|
1457
|
+
* Check whether a previously detected CAPTCHA has been solved.
|
|
1458
|
+
*
|
|
1459
|
+
* @param page - The Playwright page to check.
|
|
1460
|
+
* @param previousSelector - The selector from a previous detection result.
|
|
1461
|
+
* @returns `true` if the CAPTCHA is no longer visible.
|
|
1462
|
+
*/
|
|
1463
|
+
static isSolved(page: Page, previousSelector?: string): Promise<boolean>;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
/**
|
|
1467
|
+
* A single screencast frame with binary image data.
|
|
1468
|
+
*/
|
|
1469
|
+
interface ScreencastFrame {
|
|
1470
|
+
id: string;
|
|
1471
|
+
sessionId: string;
|
|
1472
|
+
timestamp: number;
|
|
1473
|
+
data: Buffer;
|
|
1474
|
+
url: string;
|
|
1475
|
+
viewport: {
|
|
1476
|
+
width: number;
|
|
1477
|
+
height: number;
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Options for configuring screencast capture behavior.
|
|
1482
|
+
*/
|
|
1483
|
+
interface ScreencastOptions {
|
|
1484
|
+
/** Target frame interval in ms (used as hint; CDP Cast delivers frames as fast as the page updates). Default: 100 (~10fps target) */
|
|
1485
|
+
interval?: number;
|
|
1486
|
+
/** JPEG quality 0-100. Default: 80 */
|
|
1487
|
+
quality?: number;
|
|
1488
|
+
/** Image format. Default: 'jpeg' */
|
|
1489
|
+
type?: 'jpeg' | 'png';
|
|
1490
|
+
/** Max capture width. Default: 1920 */
|
|
1491
|
+
width?: number;
|
|
1492
|
+
/** Max capture height. Default: 1080 */
|
|
1493
|
+
height?: number;
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Captures screenshots from a Playwright page using CDP `Page.startScreencast`.
|
|
1497
|
+
*
|
|
1498
|
+
* CDP Cast delivers frames as the page repaints — no polling needed.
|
|
1499
|
+
* This gives 10-30+ fps depending on page activity, vs ~2 fps with the old
|
|
1500
|
+
* `page.screenshot()` polling approach.
|
|
1501
|
+
*
|
|
1502
|
+
* Falls back to `page.screenshot()` polling if CDP session creation fails
|
|
1503
|
+
* (e.g., non-Chromium browser or restricted CDP access).
|
|
1504
|
+
*/
|
|
1505
|
+
declare class ScreencastCapturer {
|
|
1506
|
+
private interval;
|
|
1507
|
+
private quality;
|
|
1508
|
+
private type;
|
|
1509
|
+
private maxWidth;
|
|
1510
|
+
private maxHeight;
|
|
1511
|
+
private isCapturing;
|
|
1512
|
+
private frameCallback;
|
|
1513
|
+
private cdpSession;
|
|
1514
|
+
private sessionId;
|
|
1515
|
+
private fallbackTimer;
|
|
1516
|
+
constructor(options?: ScreencastOptions);
|
|
1517
|
+
/**
|
|
1518
|
+
* Start screencast capture using CDP Page.startScreencast.
|
|
1519
|
+
*
|
|
1520
|
+
* If CDP session creation fails (non-Chromium, restricted access),
|
|
1521
|
+
* automatically falls back to `page.screenshot()` polling.
|
|
1522
|
+
*/
|
|
1523
|
+
startCapture(page: Page, sessionId: string, onFrame: (frame: ScreencastFrame) => void): Promise<void>;
|
|
1524
|
+
/**
|
|
1525
|
+
* Fallback: periodic page.screenshot() polling when CDP Cast is unavailable.
|
|
1526
|
+
*/
|
|
1527
|
+
private startFallbackPolling;
|
|
1528
|
+
/**
|
|
1529
|
+
* Capture a single screenshot frame from the page (fallback mode).
|
|
1530
|
+
*/
|
|
1531
|
+
private captureFrame;
|
|
1532
|
+
/**
|
|
1533
|
+
* Stop the current screencast capture.
|
|
1534
|
+
*/
|
|
1535
|
+
stopCapture(): Promise<void>;
|
|
1536
|
+
isActive(): boolean;
|
|
1537
|
+
setInterval(interval: number): void;
|
|
1538
|
+
setQuality(quality: number): void;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/**
|
|
1542
|
+
* Payload sent to a webhook endpoint for lifecycle events.
|
|
1543
|
+
*/
|
|
1544
|
+
interface WebhookPayload {
|
|
1545
|
+
event: 'captcha-detected' | 'captcha-resolved' | 'session-started' | 'session-ended';
|
|
1546
|
+
timestamp: string;
|
|
1547
|
+
sessionId?: string;
|
|
1548
|
+
url?: string;
|
|
1549
|
+
reason?: string;
|
|
1550
|
+
previewUrl?: string;
|
|
1551
|
+
targetUrl?: string;
|
|
1552
|
+
timeout?: number;
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Sends webhook notifications for browser automation lifecycle events.
|
|
1556
|
+
*
|
|
1557
|
+
* Reads the webhook URL from the constructor argument or the
|
|
1558
|
+
* `XBROWSER_NOTIFY_URL` environment variable.
|
|
1559
|
+
*/
|
|
1560
|
+
declare class WebhookNotifier {
|
|
1561
|
+
private url;
|
|
1562
|
+
constructor(url?: string);
|
|
1563
|
+
/**
|
|
1564
|
+
* Send a webhook notification payload.
|
|
1565
|
+
*
|
|
1566
|
+
* @param payload - The event payload to send.
|
|
1567
|
+
* @returns `true` if the request succeeded (HTTP 2xx), `false` otherwise.
|
|
1568
|
+
*/
|
|
1569
|
+
notify(payload: WebhookPayload): Promise<boolean>;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
interface CaptchaConfig {
|
|
1573
|
+
notifyUrl: string | undefined;
|
|
1574
|
+
autoOpen: boolean;
|
|
1575
|
+
timeout: number;
|
|
1576
|
+
previewPort: number;
|
|
1577
|
+
}
|
|
1578
|
+
declare function getCaptchaConfig(): CaptchaConfig;
|
|
1579
|
+
|
|
52
1580
|
interface SearchResult extends AISearchResult {
|
|
53
1581
|
id: string;
|
|
54
1582
|
collectedAt: number;
|
|
@@ -132,10 +1660,6 @@ interface BatchCollectResult {
|
|
|
132
1660
|
duration: number;
|
|
133
1661
|
}
|
|
134
1662
|
|
|
135
|
-
declare const DEFAULT_STORAGE_CONFIG: StorageConfig;
|
|
136
|
-
declare const DEFAULT_COLLECTOR_CONFIG: CollectorConfig;
|
|
137
|
-
declare const PLATFORM_MAPPING: Record<string, string>;
|
|
138
|
-
declare const EXCLUDED_DOMAINS: Set<string>;
|
|
139
1663
|
declare function getPlatformName(domain: string): string | undefined;
|
|
140
1664
|
declare function getCompanyType(domain: string): 'job-platform' | 'media' | 'gov' | 'ai-platform' | 'enterprise' | 'other';
|
|
141
1665
|
|
|
@@ -228,4 +1752,501 @@ declare class DataCollector {
|
|
|
228
1752
|
getConfig(): CollectorConfig;
|
|
229
1753
|
}
|
|
230
1754
|
|
|
231
|
-
|
|
1755
|
+
/**
|
|
1756
|
+
* A parsed pipeline of commands with its chain type.
|
|
1757
|
+
*/
|
|
1758
|
+
interface ParsedPipeline {
|
|
1759
|
+
pipeline: string[];
|
|
1760
|
+
type: 'sequence' | 'and' | 'or';
|
|
1761
|
+
}
|
|
1762
|
+
interface ParseOptions {
|
|
1763
|
+
fileMode?: boolean;
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Parse a command chain string into ordered pipelines.
|
|
1767
|
+
*
|
|
1768
|
+
* Supports `&&` (and), `||` (or), `;` (sequence), `->`, `,`, and `+` operators.
|
|
1769
|
+
* Respects quoted strings and parenthesized groups.
|
|
1770
|
+
*
|
|
1771
|
+
* @param input - The raw command chain string.
|
|
1772
|
+
* @param options - Parse options; `fileMode` treats single `|` as a pipeline separator.
|
|
1773
|
+
* @returns Array of parsed pipelines, each with commands and a chain type.
|
|
1774
|
+
*
|
|
1775
|
+
* @example
|
|
1776
|
+
* ```ts
|
|
1777
|
+
* const pipelines = parseCommandChain('goto https://example.com && click #btn');
|
|
1778
|
+
* // [{ pipeline: ['goto https://example.com', 'click #btn'], type: 'and' }]
|
|
1779
|
+
* ```
|
|
1780
|
+
*/
|
|
1781
|
+
declare function parseCommandChain(input: string, options?: ParseOptions): ParsedPipeline[];
|
|
1782
|
+
/**
|
|
1783
|
+
* Split a command string into whitespace-separated tokens, respecting quotes.
|
|
1784
|
+
*
|
|
1785
|
+
* @param cmdStr - The raw command string (e.g. `"click '#my-btn'"`).
|
|
1786
|
+
* @returns Array of string tokens.
|
|
1787
|
+
*/
|
|
1788
|
+
declare function splitCommand(cmdStr: string): string[];
|
|
1789
|
+
/**
|
|
1790
|
+
* Parse positional and flagged arguments into a parameter object.
|
|
1791
|
+
*
|
|
1792
|
+
* Supports `--key value`, `-s value` (short flags), and positional arguments
|
|
1793
|
+
* mapped via registered command definitions. Values are automatically coerced
|
|
1794
|
+
* to boolean, number, or string.
|
|
1795
|
+
*
|
|
1796
|
+
* @param name - The command name (used to look up positional parameter names).
|
|
1797
|
+
* @param args - Array of argument strings.
|
|
1798
|
+
* @returns An object with the command name and parsed params.
|
|
1799
|
+
*
|
|
1800
|
+
* @example
|
|
1801
|
+
* ```ts
|
|
1802
|
+
* const { params } = parseCommandArgs('fill', ['#email', 'hello']);
|
|
1803
|
+
* // { command: 'fill', params: { selector: '#email', value: 'hello' } }
|
|
1804
|
+
* ```
|
|
1805
|
+
*/
|
|
1806
|
+
declare function parseCommandArgs(name: string, args: string[]): {
|
|
1807
|
+
command: string;
|
|
1808
|
+
params: Record<string, unknown>;
|
|
1809
|
+
};
|
|
1810
|
+
/**
|
|
1811
|
+
* Register positional parameter names for a command used by {@link parseCommandArgs}.
|
|
1812
|
+
*
|
|
1813
|
+
* @param name - The command name.
|
|
1814
|
+
* @param positional - Ordered array of positional parameter names.
|
|
1815
|
+
*/
|
|
1816
|
+
declare function registerCommandDefinition(name: string, positional: string[]): void;
|
|
1817
|
+
|
|
1818
|
+
declare function normalizeSelector(input: string): string;
|
|
1819
|
+
|
|
1820
|
+
/**
|
|
1821
|
+
* A single event in a browser recording.
|
|
1822
|
+
*/
|
|
1823
|
+
interface RecordingEvent {
|
|
1824
|
+
type: string;
|
|
1825
|
+
selector?: string;
|
|
1826
|
+
tagName?: string;
|
|
1827
|
+
data?: {
|
|
1828
|
+
value?: string;
|
|
1829
|
+
key?: string;
|
|
1830
|
+
x?: number;
|
|
1831
|
+
y?: number;
|
|
1832
|
+
};
|
|
1833
|
+
timestamp?: number;
|
|
1834
|
+
pageState?: {
|
|
1835
|
+
url?: string;
|
|
1836
|
+
title?: string;
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
/**
|
|
1840
|
+
* A browser recording session with a start URL and captured events.
|
|
1841
|
+
*/
|
|
1842
|
+
interface Recording {
|
|
1843
|
+
startUrl: string;
|
|
1844
|
+
events?: RecordingEvent[];
|
|
1845
|
+
id?: string;
|
|
1846
|
+
name?: string;
|
|
1847
|
+
startTime?: string;
|
|
1848
|
+
duration?: number;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
/**
|
|
1852
|
+
* Generate a Node.js replay script from a recording.
|
|
1853
|
+
*
|
|
1854
|
+
* @param recording - The recording session to convert.
|
|
1855
|
+
* @returns A self-contained JavaScript script string.
|
|
1856
|
+
*/
|
|
1857
|
+
declare function generateJSScript(recording: Recording): string;
|
|
1858
|
+
/**
|
|
1859
|
+
* Generate a Python replay script from a recording.
|
|
1860
|
+
*
|
|
1861
|
+
* @param recording - The recording session to convert.
|
|
1862
|
+
* @returns A self-contained Python script string using Playwright async API.
|
|
1863
|
+
*/
|
|
1864
|
+
declare function generatePythonScript(recording: Recording): string;
|
|
1865
|
+
/**
|
|
1866
|
+
* Generate a Bash replay script from a recording using CDP HTTP endpoints.
|
|
1867
|
+
*
|
|
1868
|
+
* @param recording - The recording session to convert.
|
|
1869
|
+
* @returns A self-contained Bash script string.
|
|
1870
|
+
*/
|
|
1871
|
+
declare function generateBashScript(recording: Recording): string;
|
|
1872
|
+
|
|
1873
|
+
interface ExtractSummary {
|
|
1874
|
+
startUrl: string;
|
|
1875
|
+
totalEvents: number;
|
|
1876
|
+
keyEventsCount: number;
|
|
1877
|
+
eventTypes: Record<string, number>;
|
|
1878
|
+
operations: Array<{
|
|
1879
|
+
step: number;
|
|
1880
|
+
type: string;
|
|
1881
|
+
selector?: string;
|
|
1882
|
+
tagName?: string;
|
|
1883
|
+
data?: RecordingEvent['data'];
|
|
1884
|
+
url?: string;
|
|
1885
|
+
}>;
|
|
1886
|
+
}
|
|
1887
|
+
/**
|
|
1888
|
+
* Extract and summarize key events from a YAML recording file.
|
|
1889
|
+
*
|
|
1890
|
+
* @param filePath - Path to the YAML recording file.
|
|
1891
|
+
* @returns A summary with start URL, event counts, type statistics, and key operations.
|
|
1892
|
+
*/
|
|
1893
|
+
declare function extractRecording(filePath: string): ExtractSummary;
|
|
1894
|
+
/**
|
|
1895
|
+
* Extract a recording summary and save it as a JSON file alongside the original.
|
|
1896
|
+
*
|
|
1897
|
+
* @param filePath - Path to the YAML recording file.
|
|
1898
|
+
* @returns An object with the summary and the output file path.
|
|
1899
|
+
*/
|
|
1900
|
+
declare function extractAndSave(filePath: string): {
|
|
1901
|
+
summary: ExtractSummary;
|
|
1902
|
+
outputPath: string;
|
|
1903
|
+
};
|
|
1904
|
+
/**
|
|
1905
|
+
* Print a human-readable recording summary to stdout.
|
|
1906
|
+
*
|
|
1907
|
+
* @param summary - The extracted recording summary to display.
|
|
1908
|
+
*/
|
|
1909
|
+
declare function printExtractSummary(summary: ExtractSummary): void;
|
|
1910
|
+
|
|
1911
|
+
/**
|
|
1912
|
+
* Filter a recording file by removing events of specified types.
|
|
1913
|
+
*
|
|
1914
|
+
* Reads the YAML recording, removes events matching the exclude list,
|
|
1915
|
+
* and writes the filtered result to the output path.
|
|
1916
|
+
*
|
|
1917
|
+
* @param inputPath - Path to the input YAML recording file.
|
|
1918
|
+
* @param outputPath - Path to write the filtered recording.
|
|
1919
|
+
* @param excludeTypes - Event types to remove. Defaults to a built-in list of noise events.
|
|
1920
|
+
* @returns Statistics about original count, filtered count, and removal percentage.
|
|
1921
|
+
*/
|
|
1922
|
+
declare function filterRecording(inputPath: string, outputPath: string, excludeTypes?: string[]): {
|
|
1923
|
+
originalCount: number;
|
|
1924
|
+
filteredCount: number;
|
|
1925
|
+
removed: number;
|
|
1926
|
+
percentage: number;
|
|
1927
|
+
};
|
|
1928
|
+
/**
|
|
1929
|
+
* Parse the `--exclude-types` flag from CLI arguments.
|
|
1930
|
+
*
|
|
1931
|
+
* @param args - CLI argument array.
|
|
1932
|
+
* @returns Array of event type strings, or `undefined` if not specified.
|
|
1933
|
+
*/
|
|
1934
|
+
declare function parseExcludeTypes(args: string[]): string[] | undefined;
|
|
1935
|
+
|
|
1936
|
+
interface APIRequest {
|
|
1937
|
+
method: string;
|
|
1938
|
+
url: string;
|
|
1939
|
+
headers: Record<string, string | undefined>;
|
|
1940
|
+
body: unknown;
|
|
1941
|
+
params: Record<string, string>;
|
|
1942
|
+
query: Record<string, string>;
|
|
1943
|
+
}
|
|
1944
|
+
interface APIResponse {
|
|
1945
|
+
statusCode: number;
|
|
1946
|
+
body: unknown;
|
|
1947
|
+
headers?: Record<string, string>;
|
|
1948
|
+
}
|
|
1949
|
+
interface ExecRequest {
|
|
1950
|
+
command: string;
|
|
1951
|
+
params?: Record<string, unknown>;
|
|
1952
|
+
session?: string;
|
|
1953
|
+
}
|
|
1954
|
+
interface ChainRequest {
|
|
1955
|
+
chain: string;
|
|
1956
|
+
session?: string;
|
|
1957
|
+
cdpEndpoint?: string;
|
|
1958
|
+
}
|
|
1959
|
+
interface HTTPServerError {
|
|
1960
|
+
error: string;
|
|
1961
|
+
message: string;
|
|
1962
|
+
statusCode: number;
|
|
1963
|
+
}
|
|
1964
|
+
interface HTTPServerConfig {
|
|
1965
|
+
port?: number;
|
|
1966
|
+
host?: string;
|
|
1967
|
+
tokens?: string[];
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
/**
|
|
1971
|
+
* HTTP server exposing the xbrowser REST API for remote command execution.
|
|
1972
|
+
*
|
|
1973
|
+
* Provides endpoints for health checks, session management, single command
|
|
1974
|
+
* execution, and command chain execution. Supports optional Bearer token
|
|
1975
|
+
* authentication via config or environment variable.
|
|
1976
|
+
*/
|
|
1977
|
+
declare class HTTPServer {
|
|
1978
|
+
private port;
|
|
1979
|
+
private host;
|
|
1980
|
+
private server;
|
|
1981
|
+
private validTokens;
|
|
1982
|
+
constructor(config?: HTTPServerConfig);
|
|
1983
|
+
/**
|
|
1984
|
+
* Start the HTTP server and begin listening for requests.
|
|
1985
|
+
*
|
|
1986
|
+
* @returns The actual port and host the server bound to.
|
|
1987
|
+
* @throws If the server is already running or fails to start.
|
|
1988
|
+
*/
|
|
1989
|
+
start(): Promise<{
|
|
1990
|
+
port: number;
|
|
1991
|
+
host: string;
|
|
1992
|
+
}>;
|
|
1993
|
+
/**
|
|
1994
|
+
* Stop the HTTP server gracefully.
|
|
1995
|
+
*
|
|
1996
|
+
* Closes all active connections and stops accepting new ones.
|
|
1997
|
+
*/
|
|
1998
|
+
stop(): Promise<void>;
|
|
1999
|
+
/**
|
|
2000
|
+
* Get the address the server is bound to.
|
|
2001
|
+
*
|
|
2002
|
+
* @returns The port and host, or `null` if the server is not running.
|
|
2003
|
+
*/
|
|
2004
|
+
getAddress(): {
|
|
2005
|
+
port: number;
|
|
2006
|
+
host: string;
|
|
2007
|
+
} | null;
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
/**
|
|
2011
|
+
* CDP Interceptor Proxy
|
|
2012
|
+
*
|
|
2013
|
+
* A thin WebSocket proxy that sits between any automation tool (Playwright,
|
|
2014
|
+
* Puppeteer, etc.) and Chromium's CDP endpoint. Every JSON-RPC message is
|
|
2015
|
+
* intercepted, analyzed against a rule engine, and either passed through,
|
|
2016
|
+
* blocked (with error response), or transformed before reaching the browser.
|
|
2017
|
+
*
|
|
2018
|
+
* Usage:
|
|
2019
|
+
* ```
|
|
2020
|
+
* const proxy = new CDPInterceptorProxy({ cdpEndpoint: 'ws://localhost:9222/...' });
|
|
2021
|
+
* await proxy.start(); // starts listening on a random port
|
|
2022
|
+
* // Now connect your automation tool to: ws://localhost:{proxy.port}
|
|
2023
|
+
* ```
|
|
2024
|
+
*/
|
|
2025
|
+
|
|
2026
|
+
declare class CDPInterceptorProxy {
|
|
2027
|
+
private wss;
|
|
2028
|
+
private engine;
|
|
2029
|
+
private config;
|
|
2030
|
+
private logger;
|
|
2031
|
+
private started;
|
|
2032
|
+
stats: CDPInterceptorStats;
|
|
2033
|
+
constructor(config: CDPInterceptorConfig);
|
|
2034
|
+
/** The port the proxy is listening on (only valid after start()) */
|
|
2035
|
+
get port(): number;
|
|
2036
|
+
/** Start the proxy server */
|
|
2037
|
+
start(): Promise<number>;
|
|
2038
|
+
/** Stop the proxy server */
|
|
2039
|
+
stop(): Promise<void>;
|
|
2040
|
+
/** Get accumulated statistics */
|
|
2041
|
+
getStats(): CDPInterceptorStats;
|
|
2042
|
+
/** Get recent log entries (for inspection) */
|
|
2043
|
+
getRecentLogs(count?: number): CDPLogEntry[];
|
|
2044
|
+
private handleConnection;
|
|
2045
|
+
private handleClientMessage;
|
|
2046
|
+
private handleBrowserMessage;
|
|
2047
|
+
private parseMessage;
|
|
2048
|
+
private recordDecision;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
/**
|
|
2052
|
+
* CDP Interceptor — Rule Engine
|
|
2053
|
+
*
|
|
2054
|
+
* Evaluates messages against a prioritized list of interception rules.
|
|
2055
|
+
* Rules that need per-session state can store data in sessionState.
|
|
2056
|
+
*
|
|
2057
|
+
* Total built-in patterns: ~200+ across 9 rule modules.
|
|
2058
|
+
*/
|
|
2059
|
+
|
|
2060
|
+
interface RuleEngine {
|
|
2061
|
+
start(): void;
|
|
2062
|
+
stop(): void;
|
|
2063
|
+
evaluate(ctx: Omit<RuleContext, 'sessionState'>): DecisionResult | null;
|
|
2064
|
+
}
|
|
2065
|
+
declare function createRuleEngine(customRules?: CDPInterceptorRule[]): RuleEngine;
|
|
2066
|
+
|
|
2067
|
+
/**
|
|
2068
|
+
* Rule: DOM Property Mutation — Comprehensive Edition
|
|
2069
|
+
*
|
|
2070
|
+
* Detects ALL direct DOM property setters via Runtime.evaluate / Runtime.callFunctionOn.
|
|
2071
|
+
* Each pattern bypasses framework reactivity (React setter, Vue proxy, Angular zone.js).
|
|
2072
|
+
*
|
|
2073
|
+
* Severity tiers:
|
|
2074
|
+
* danger → hard block (100% automation signal)
|
|
2075
|
+
* warn → soft block (likely automation, some legitimate uses)
|
|
2076
|
+
* info → log only (could be context-dependent)
|
|
2077
|
+
*/
|
|
2078
|
+
|
|
2079
|
+
declare const domMutationRule: CDPInterceptorRule;
|
|
2080
|
+
|
|
2081
|
+
/**
|
|
2082
|
+
* Rule: Mouse Trajectory Analysis
|
|
2083
|
+
*
|
|
2084
|
+
* Detects automated mouse interactions by analyzing mouse movement patterns.
|
|
2085
|
+
* Natural human mouse movements exhibit acceleration curves, jitter, and
|
|
2086
|
+
* non-linear paths. Automated movements are often perfect straight lines
|
|
2087
|
+
* with constant speed.
|
|
2088
|
+
*
|
|
2089
|
+
* Detection heuristics:
|
|
2090
|
+
* 1. **Collinearity**: Path from A→B lies on a near-perfect straight line
|
|
2091
|
+
* (very low residuals from linear regression)
|
|
2092
|
+
* 2. **Constant velocity**: Speed between consecutive move events is too uniform
|
|
2093
|
+
* 3. **No jitter**: Human hands have micro-tremors (1-3px variation)
|
|
2094
|
+
*
|
|
2095
|
+
* Stateful: tracks mouse positions per CDP session to analyze trajectories.
|
|
2096
|
+
*/
|
|
2097
|
+
|
|
2098
|
+
declare const mouseTrajectoryRule: CDPInterceptorRule;
|
|
2099
|
+
|
|
2100
|
+
/**
|
|
2101
|
+
* Rule: Input Keystroke Timing Analysis
|
|
2102
|
+
*
|
|
2103
|
+
* Analyzes `Input.dispatchKeyEvent` sequences for patterns that betray
|
|
2104
|
+
* automation:
|
|
2105
|
+
*
|
|
2106
|
+
* 1. **Constant inter-key timing**: natural typing has 30-200ms variation;
|
|
2107
|
+
* automation tools like `page.type(..., {delay: 50})` produce exact 50ms
|
|
2108
|
+
* intervals with zero variance.
|
|
2109
|
+
*
|
|
2110
|
+
* 2. **Input.insertText**: bypasses native keyboard events entirely.
|
|
2111
|
+
* Real users always dispatch keyDown → keyPress → input → keyUp.
|
|
2112
|
+
* `Input.insertText` skips all of these and is a strong automation signal.
|
|
2113
|
+
*
|
|
2114
|
+
* 3. **Unnatural key order**: typing characters one-by-one without variation
|
|
2115
|
+
* in hold time (keyDown → keyUp interval).
|
|
2116
|
+
*/
|
|
2117
|
+
|
|
2118
|
+
declare const inputKeystrokeRule: CDPInterceptorRule;
|
|
2119
|
+
|
|
2120
|
+
/**
|
|
2121
|
+
* Rule: Browser Automation Signals
|
|
2122
|
+
*
|
|
2123
|
+
* Detects telltale markers left in the page context by browser automation
|
|
2124
|
+
* tools (Playwright, Puppeteer, Selenium, WebDriver). These markers are
|
|
2125
|
+
* the #1 thing anti-crawler systems check for.
|
|
2126
|
+
*
|
|
2127
|
+
* Detection categories:
|
|
2128
|
+
* 1. Tool-specific global objects (window.__playwright, etc.)
|
|
2129
|
+
* 2. navigator.webdriver flag checks
|
|
2130
|
+
* 3. Chrome headless API surface detection
|
|
2131
|
+
* 4. Anti-detection script injection attempts (already too late)
|
|
2132
|
+
*
|
|
2133
|
+
* Both Runtime.evaluate and Page.addScriptToEvaluateOnNewDocument are checked.
|
|
2134
|
+
*/
|
|
2135
|
+
|
|
2136
|
+
declare const automationSignalsRule: CDPInterceptorRule;
|
|
2137
|
+
|
|
2138
|
+
/**
|
|
2139
|
+
* Rule: Browser Fingerprinting Access Detection
|
|
2140
|
+
*
|
|
2141
|
+
* Blocks access to known browser fingerprinting APIs via Runtime.evaluate.
|
|
2142
|
+
* Anti-crawler systems use these APIs to build a unique device fingerprint
|
|
2143
|
+
* and detect headless/automated browsers.
|
|
2144
|
+
*
|
|
2145
|
+
* Instead of blocking these calls (which would break the page), we:
|
|
2146
|
+
* - Log the access (info)
|
|
2147
|
+
* - Warn the developer (warn)
|
|
2148
|
+
* - Suggest alternatives
|
|
2149
|
+
*
|
|
2150
|
+
* The goal is to make the developer aware that their code is triggering
|
|
2151
|
+
* fingerprinting APIs, which means the target site can identify them.
|
|
2152
|
+
*
|
|
2153
|
+
* Severity reflects how diagnostic the API is (not how dangerous the call is):
|
|
2154
|
+
* - canvas.toDataURL() → danger (fingerprint hash, highly diagnostic)
|
|
2155
|
+
* - AudioContext → warn (common fingerprint method)
|
|
2156
|
+
* - platform info → info (less diagnostic alone)
|
|
2157
|
+
*/
|
|
2158
|
+
|
|
2159
|
+
declare const fingerprintingRule: CDPInterceptorRule;
|
|
2160
|
+
|
|
2161
|
+
/**
|
|
2162
|
+
* Rule: Event Simulation Detection
|
|
2163
|
+
*
|
|
2164
|
+
* Detects CDP calls that simulate user interaction events via
|
|
2165
|
+
* evaluate/callFunctionOn, bypassing the browser's trusted event system.
|
|
2166
|
+
*
|
|
2167
|
+
* Key distinction:
|
|
2168
|
+
* - Input.dispatchMouseEvent / Input.dispatchKeyEvent → isTrusted=true ✓
|
|
2169
|
+
* - element.click() / element.dispatchEvent(new Event(...)) → isTrusted=false ✗
|
|
2170
|
+
*
|
|
2171
|
+
* Any event with isTrusted=false is immediately detectable by anti-crawler
|
|
2172
|
+
* systems that check the event's isTrusted property.
|
|
2173
|
+
*
|
|
2174
|
+
* This rule covers:
|
|
2175
|
+
* 1. Direct method calls (el.click(), el.focus(), el.submit(), etc.)
|
|
2176
|
+
* 2. dispatchEvent with synthetic event objects
|
|
2177
|
+
* 3. Dialog/modal/fullscreen programmatic triggers
|
|
2178
|
+
*/
|
|
2179
|
+
|
|
2180
|
+
declare const eventSimulationRule: CDPInterceptorRule;
|
|
2181
|
+
|
|
2182
|
+
/**
|
|
2183
|
+
* Rule: CDP Emulation / Override Detection
|
|
2184
|
+
*
|
|
2185
|
+
* Detects calls to CDP methods that override browser behavior in ways that
|
|
2186
|
+
* can be detected by anti-crawler systems. Each override creates an
|
|
2187
|
+
* inconsistency between the JS environment and the real browser state.
|
|
2188
|
+
*
|
|
2189
|
+
* Examples:
|
|
2190
|
+
* - Emulation.setUserAgentOverride → navigator.userAgent vs HTTP headers mismatch
|
|
2191
|
+
* - Emulation.setDeviceMetricsOverride → matchMedia vs actual viewport mismatch
|
|
2192
|
+
* - Emulation.setGeolocationOverride → geo suddenly changes mid-session
|
|
2193
|
+
* - Network.setExtraHTTPHeaders → header conflicts with JS environment
|
|
2194
|
+
*/
|
|
2195
|
+
|
|
2196
|
+
declare const emulationOverrideRule: CDPInterceptorRule;
|
|
2197
|
+
|
|
2198
|
+
/**
|
|
2199
|
+
* Rule: Network Request Anomaly Detection
|
|
2200
|
+
*
|
|
2201
|
+
* Detects network request patterns that betray automation at the
|
|
2202
|
+
* CDP protocol level. While most network analysis requires a proxy,
|
|
2203
|
+
* certain CDP methods and timing patterns are visible at the CDP layer.
|
|
2204
|
+
*
|
|
2205
|
+
* Detection patterns:
|
|
2206
|
+
* 1. Network.setExtraHTTPHeaders with suspicious headers
|
|
2207
|
+
* 2. Network.setUserAgentOverride (also caught by emulation-override)
|
|
2208
|
+
* 3. Requests too fast after navigation (via PageLifecycle state)
|
|
2209
|
+
* 4. Network.clearBrowserCache (automation optimization giveaway)
|
|
2210
|
+
* 5. Network.enable called at suspicious times
|
|
2211
|
+
*/
|
|
2212
|
+
|
|
2213
|
+
declare const networkAnomalyRule: CDPInterceptorRule;
|
|
2214
|
+
|
|
2215
|
+
/**
|
|
2216
|
+
* Rule: Page Lifecycle Anomaly Detection
|
|
2217
|
+
*
|
|
2218
|
+
* Detects unnatural page interaction sequences that betray automation:
|
|
2219
|
+
* 1. Rapid navigation → screenshot → close (content extraction pattern)
|
|
2220
|
+
* 2. Page.printToPDF (scraper intent giveaway)
|
|
2221
|
+
* 3. Zero mouse/scroll events across full session
|
|
2222
|
+
* 4. Multiple navigations without waiting for load
|
|
2223
|
+
* 5. Tab/window operations without user action
|
|
2224
|
+
*
|
|
2225
|
+
* Stateful: tracks page-level metrics per CDP session.
|
|
2226
|
+
*/
|
|
2227
|
+
|
|
2228
|
+
declare const pageLifecycleRule: CDPInterceptorRule;
|
|
2229
|
+
|
|
2230
|
+
/**
|
|
2231
|
+
* CDP Interceptor — Advisor
|
|
2232
|
+
*
|
|
2233
|
+
* Generates actionable suggestions when rules flag a message.
|
|
2234
|
+
* Designed to be LLM-friendly: any AI agent reading the error
|
|
2235
|
+
* should know exactly what to do instead.
|
|
2236
|
+
*/
|
|
2237
|
+
|
|
2238
|
+
interface AdvisoryResult {
|
|
2239
|
+
ruleId: string;
|
|
2240
|
+
title: string;
|
|
2241
|
+
detail: string;
|
|
2242
|
+
codeExample?: string;
|
|
2243
|
+
}
|
|
2244
|
+
/**
|
|
2245
|
+
* Generate an advisory message from a rule decision.
|
|
2246
|
+
*/
|
|
2247
|
+
declare function advise(decision: DecisionResult, originalMethod: string): AdvisoryResult;
|
|
2248
|
+
|
|
2249
|
+
/** Convenience factory: create and start a CDP interceptor proxy with defaults. */
|
|
2250
|
+
declare function createCDPInterceptor(config: CDPInterceptorConfig): Promise<CDPInterceptorProxy>;
|
|
2251
|
+
|
|
2252
|
+
export { type APIRequest, type APIResponse, type AdvisoryResult, type AnalysisResult, BROWSER_SCOPE, type BatchCollectResult, type BrowserCommandContext, type BrowserCommandDefinition, type BrowserLaunchOptions, type BuiltinCommand, type BuiltinContext, type CDPError, type CDPInterceptorConfig, CDPInterceptorProxy, type CDPInterceptorRule, type CDPInterceptorStats, type CDPLogEntry, type CDPMessage, type CDPRequest, type CDPResponse, type CaptchaDetectionResult, CaptchaDetector, type ChainExecutionResult, type ChainRequest, type ChainStepResult, type CollectResult, type CollectorConfig, type CommandMessage, type CompanyInfo, type ContextChange, type DaemonInfo, DataCollector, DataStorage, type DecisionAction, type DecisionResult, type DomainStat, type ElementRef, type ExecRequest, type ExecutionResult, HTTPServer, type HTTPServerConfig, type HTTPServerError, HumanInteractionManager, type InstallOptions, type InstalledPlugin, type ManagedSession, type MessageDirection, type NetworkEntry, type ParsedPipeline, PlaybackEngine, type PlaybackOptions, type PlaybackResult, PluginInstaller, type PluginLoaderOptions, type RecordedEvent, RecorderController, type RecorderStatus, type Recording, type RecordingControlFile, type RecordingData, type RecordingEvent, type RecordingSession, type RecordingStep, type RecordingSummary, type RegisteredCommand, ResultAnalyzer, type RuleContext, type RuleEngine, type ScopeDefinition, type ScopeLevel, ScreencastCapturer, type ScreencastFrame, type ScreencastMessage, type ScreencastOptions, type SearchResult, type ManagedSession as SessionInfo, SessionRecorder, type StatusMessage, type StorageConfig, type UserAction, type ViolationSeverity, type WSInboundMessage, type WSMessage, WSServer, type WSServerConfig, type WaitForHumanOptions, type WaitForHumanResult, WebhookNotifier, type WebhookPayload, XBrowserPluginLoader, advise, allBuiltins, assertPageScope, attachWaitForHuman, automationSignalsRule, checkBrowserScope, routeCommand as cliRoute, closeAllSessions as closeAllBrowserSessions, closeAllSessions, closeSession, closeSessionByName, createCDPInterceptor, createRuleEngine, createSession, destroyBrowser, destroyBrowser as destroySessionManager, domMutationRule, emulationOverrideRule, eventSimulationRule, executeChain, executeCommand, extractAndSave, extractRecording, filterRecording, findSession, findSession as findSessionInfo, fingerprintingRule, generateBashScript, generateJSScript, generatePythonScript, getAllSessions as getAllBrowserSessions, getAllCommands, getBrowser, getBuiltin, getCaptchaConfig, getCommand, getCommandNames, getCompanyType, getDaemonProcessStatus, getPlatformName, getSessionPage, getWSServerFromCache, inputKeystrokeRule, isChainInput, getAllSessions as listAllBrowserSessions, listSessions, mouseTrajectoryRule, networkAnomalyRule, normalizeSelector, openSession, pageLifecycleRule, parseCommandArgs, parseCommandChain, parseExcludeTypes, printExtractSummary, readCommandFile, readStdin, registerCommand, registerCommandDefinition, resetForTesting, setWSServer, setWSServerCache, splitCommand, startDaemonProcess, stopDaemonProcess, version };
|