agent-browser 0.4.4 → 0.5.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/browser.d.ts CHANGED
@@ -1,6 +1,26 @@
1
- import { devices, type Browser, type Page, type Frame, type Locator } from 'playwright-core';
1
+ import { devices, type Browser, type Page, type Frame, type Locator, type CDPSession } from 'playwright-core';
2
2
  import type { LaunchCommand } from './types.js';
3
3
  import { type RefMap, type EnhancedSnapshot } from './snapshot.js';
4
+ export interface ScreencastFrame {
5
+ data: string;
6
+ metadata: {
7
+ offsetTop: number;
8
+ pageScaleFactor: number;
9
+ deviceWidth: number;
10
+ deviceHeight: number;
11
+ scrollOffsetX: number;
12
+ scrollOffsetY: number;
13
+ timestamp?: number;
14
+ };
15
+ sessionId: number;
16
+ }
17
+ export interface ScreencastOptions {
18
+ format?: 'jpeg' | 'png';
19
+ quality?: number;
20
+ maxWidth?: number;
21
+ maxHeight?: number;
22
+ everyNthFrame?: number;
23
+ }
4
24
  interface TrackedRequest {
5
25
  url: string;
6
26
  method: string;
@@ -22,6 +42,8 @@ interface PageError {
22
42
  */
23
43
  export declare class BrowserManager {
24
44
  private browser;
45
+ private cdpPort;
46
+ private isPersistentContext;
25
47
  private contexts;
26
48
  private pages;
27
49
  private activePageIndex;
@@ -35,6 +57,11 @@ export declare class BrowserManager {
35
57
  private refMap;
36
58
  private lastSnapshot;
37
59
  private scopedHeaderRoutes;
60
+ private cdpSession;
61
+ private screencastActive;
62
+ private screencastSessionId;
63
+ private frameCallback;
64
+ private screencastFrameHandler;
38
65
  /**
39
66
  * Check if browser is launched
40
67
  */
@@ -217,15 +244,32 @@ export declare class BrowserManager {
217
244
  * Get the current browser instance
218
245
  */
219
246
  getBrowser(): Browser | null;
247
+ /**
248
+ * Check if an existing CDP connection is still alive
249
+ * by verifying we can access browser contexts and that at least one has pages
250
+ */
251
+ private isCdpConnectionAlive;
252
+ /**
253
+ * Check if CDP connection needs to be re-established
254
+ */
255
+ private needsCdpReconnect;
220
256
  /**
221
257
  * Launch the browser with the specified options
222
258
  * If already launched, this is a no-op (browser stays open)
223
259
  */
224
260
  launch(options: LaunchCommand): Promise<void>;
225
261
  /**
226
- * Set up console and error tracking for a page
262
+ * Connect to a running browser via CDP (Chrome DevTools Protocol)
263
+ */
264
+ private connectViaCDP;
265
+ /**
266
+ * Set up console, error, and close tracking for a page
227
267
  */
228
268
  private setupPageTracking;
269
+ /**
270
+ * Set up tracking for new pages in a context (for CDP connections)
271
+ */
272
+ private setupContextTracking;
229
273
  /**
230
274
  * Create a new tab in the current context
231
275
  */
@@ -243,14 +287,19 @@ export declare class BrowserManager {
243
287
  index: number;
244
288
  total: number;
245
289
  }>;
290
+ /**
291
+ * Invalidate the current CDP session (must be called before switching pages)
292
+ * This ensures screencast and input injection work correctly after tab switch
293
+ */
294
+ private invalidateCDPSession;
246
295
  /**
247
296
  * Switch to a specific tab/page by index
248
297
  */
249
- switchTo(index: number): {
298
+ switchTo(index: number): Promise<{
250
299
  index: number;
251
300
  url: string;
252
301
  title: string;
253
- };
302
+ }>;
254
303
  /**
255
304
  * Close a specific tab/page
256
305
  */
@@ -267,6 +316,60 @@ export declare class BrowserManager {
267
316
  title: string;
268
317
  active: boolean;
269
318
  }>>;
319
+ /**
320
+ * Get or create a CDP session for the current page
321
+ * Only works with Chromium-based browsers
322
+ */
323
+ getCDPSession(): Promise<CDPSession>;
324
+ /**
325
+ * Check if screencast is currently active
326
+ */
327
+ isScreencasting(): boolean;
328
+ /**
329
+ * Start screencast - streams viewport frames via CDP
330
+ * @param callback Function called for each frame
331
+ * @param options Screencast options
332
+ */
333
+ startScreencast(callback: (frame: ScreencastFrame) => void, options?: ScreencastOptions): Promise<void>;
334
+ /**
335
+ * Stop screencast
336
+ */
337
+ stopScreencast(): Promise<void>;
338
+ /**
339
+ * Inject a mouse event via CDP
340
+ */
341
+ injectMouseEvent(params: {
342
+ type: 'mousePressed' | 'mouseReleased' | 'mouseMoved' | 'mouseWheel';
343
+ x: number;
344
+ y: number;
345
+ button?: 'left' | 'right' | 'middle' | 'none';
346
+ clickCount?: number;
347
+ deltaX?: number;
348
+ deltaY?: number;
349
+ modifiers?: number;
350
+ }): Promise<void>;
351
+ /**
352
+ * Inject a keyboard event via CDP
353
+ */
354
+ injectKeyboardEvent(params: {
355
+ type: 'keyDown' | 'keyUp' | 'char';
356
+ key?: string;
357
+ code?: string;
358
+ text?: string;
359
+ modifiers?: number;
360
+ }): Promise<void>;
361
+ /**
362
+ * Inject touch event via CDP (for mobile emulation)
363
+ */
364
+ injectTouchEvent(params: {
365
+ type: 'touchStart' | 'touchEnd' | 'touchMove' | 'touchCancel';
366
+ touchPoints: Array<{
367
+ x: number;
368
+ y: number;
369
+ id?: number;
370
+ }>;
371
+ modifiers?: number;
372
+ }): Promise<void>;
270
373
  /**
271
374
  * Close the browser and clean up
272
375
  */
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,OAAO,EACP,KAAK,OAAO,EAEZ,KAAK,IAAI,EACT,KAAK,KAAK,EAIV,KAAK,OAAO,EACZ,KAAK,UAAU,EAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,gBAAgB,EAAiC,MAAM,eAAe,CAAC;AAGlG,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,aAAa,CAAoD;IACzE,OAAO,CAAC,eAAe,CAAwB;IAC/C,OAAO,CAAC,MAAM,CAA2D;IACzE,OAAO,CAAC,eAAe,CAAwB;IAC/C,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,kBAAkB,CAA2D;IAGrF,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,mBAAmB,CAAa;IACxC,OAAO,CAAC,aAAa,CAAmD;IACxE,OAAO,CAAC,sBAAsB,CAAwC;IAEtE;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACG,WAAW,CAAC,OAAO,CAAC,EAAE;QAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAQ7B;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;;OAGG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAyBjD;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO;IAU1C;;OAEG;IACH,OAAO,IAAI,IAAI;IAOf;;OAEG;IACH,QAAQ,IAAI,KAAK;IAOjB;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B/F;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAIzB;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAmB3E;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAQ1B;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAa5B;;OAEG;IACH,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE;IAO9C;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;OAEG;IACG,QAAQ,CACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACP,QAAQ,CAAC,EAAE;YACT,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;SAClC,CAAC;QACF,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,GACA,OAAO,CAAC,IAAI,CAAC;IAsBhB;;OAEG;IACG,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB9C;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO3F;;OAEG;IACG,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1E;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/D;;OAEG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,GAAG,SAAS;IAIjF;;OAEG;IACH,WAAW,IAAI,MAAM,EAAE;IAIvB;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAW5B;;OAEG;IACH,kBAAkB,IAAI,cAAc,EAAE;IAItC;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAI5B;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAU1B;;OAEG;IACH,aAAa,IAAI,SAAS,EAAE;IAI5B;;OAEG;IACH,eAAe,IAAI,IAAI;IAIvB;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxC;;OAEG;IACH,cAAc,IAAI,OAAO;IAIzB;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjD;;OAEG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrE;;;OAGG;IACG,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqCtF;;OAEG;IACG,kBAAkB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BxD;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAU1F;;OAEG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9C;;OAEG;IACG,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOnD;;OAEG;IACH,QAAQ,IAAI,IAAI,EAAE;IAIlB;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;OAEG;IACH,UAAU,IAAI,OAAO,GAAG,IAAI;IAI5B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAW5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;OAGG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAiEnD;;OAEG;YACW,aAAa;IA8C3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA2BzB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAO5B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAmBzD;;OAEG;IACG,SAAS,CAAC,QAAQ,CAAC,EAAE;QACzB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAqB7C;;;OAGG;YACW,oBAAoB;IAalC;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAoBrF;;OAEG;IACG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IA8B9E;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAYhG;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAa1C;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;;;OAIG;IACG,eAAe,CACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,EAC1C,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC;IAuChB;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBrC;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE;QAC7B,IAAI,EAAE,cAAc,GAAG,eAAe,GAAG,YAAY,GAAG,YAAY,CAAC;QACrE,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;QAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBjB;;OAEG;IACG,mBAAmB,CAAC,MAAM,EAAE;QAChC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;QACnC,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjB;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE;QAC7B,IAAI,EAAE,YAAY,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;QAC9D,WAAW,EAAE,KAAK,CAAC;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,IAAI,CAAC;IAcjB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAyC7B"}
package/dist/browser.js CHANGED
@@ -1,10 +1,14 @@
1
1
  import { chromium, firefox, webkit, devices, } from 'playwright-core';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
2
4
  import { getEnhancedSnapshot, parseRef } from './snapshot.js';
3
5
  /**
4
6
  * Manages the Playwright browser lifecycle with multiple tabs/windows
5
7
  */
6
8
  export class BrowserManager {
7
9
  browser = null;
10
+ cdpPort = null;
11
+ isPersistentContext = false;
8
12
  contexts = [];
9
13
  pages = [];
10
14
  activePageIndex = 0;
@@ -18,11 +22,17 @@ export class BrowserManager {
18
22
  refMap = {};
19
23
  lastSnapshot = '';
20
24
  scopedHeaderRoutes = new Map();
25
+ // CDP session for screencast and input injection
26
+ cdpSession = null;
27
+ screencastActive = false;
28
+ screencastSessionId = 0;
29
+ frameCallback = null;
30
+ screencastFrameHandler = null;
21
31
  /**
22
32
  * Check if browser is launched
23
33
  */
24
34
  isLaunched() {
25
- return this.browser !== null;
35
+ return this.browser !== null || this.isPersistentContext;
26
36
  }
27
37
  /**
28
38
  * Get enhanced snapshot with refs and cache the ref map
@@ -477,40 +487,134 @@ export class BrowserManager {
477
487
  getBrowser() {
478
488
  return this.browser;
479
489
  }
490
+ /**
491
+ * Check if an existing CDP connection is still alive
492
+ * by verifying we can access browser contexts and that at least one has pages
493
+ */
494
+ isCdpConnectionAlive() {
495
+ if (!this.browser)
496
+ return false;
497
+ try {
498
+ const contexts = this.browser.contexts();
499
+ if (contexts.length === 0)
500
+ return false;
501
+ return contexts.some((context) => context.pages().length > 0);
502
+ }
503
+ catch {
504
+ return false;
505
+ }
506
+ }
507
+ /**
508
+ * Check if CDP connection needs to be re-established
509
+ */
510
+ needsCdpReconnect(cdpPort) {
511
+ if (!this.browser?.isConnected())
512
+ return true;
513
+ if (this.cdpPort !== cdpPort)
514
+ return true;
515
+ if (!this.isCdpConnectionAlive())
516
+ return true;
517
+ return false;
518
+ }
480
519
  /**
481
520
  * Launch the browser with the specified options
482
521
  * If already launched, this is a no-op (browser stays open)
483
522
  */
484
523
  async launch(options) {
485
- // If already launched, don't relaunch
486
- if (this.browser) {
524
+ const cdpPort = options.cdpPort;
525
+ const hasExtensions = !!options.extensions?.length;
526
+ if (hasExtensions && cdpPort) {
527
+ throw new Error('Extensions cannot be used with CDP connection');
528
+ }
529
+ if (this.isLaunched()) {
530
+ const needsRelaunch = (!cdpPort && this.cdpPort !== null) || (!!cdpPort && this.needsCdpReconnect(cdpPort));
531
+ if (needsRelaunch) {
532
+ await this.close();
533
+ }
534
+ else {
535
+ return;
536
+ }
537
+ }
538
+ if (cdpPort) {
539
+ await this.connectViaCDP(cdpPort);
487
540
  return;
488
541
  }
489
- // Select browser type
490
542
  const browserType = options.browser ?? 'chromium';
543
+ if (hasExtensions && browserType !== 'chromium') {
544
+ throw new Error('Extensions are only supported in Chromium');
545
+ }
491
546
  const launcher = browserType === 'firefox' ? firefox : browserType === 'webkit' ? webkit : chromium;
492
- // Launch browser
493
- this.browser = await launcher.launch({
494
- headless: options.headless ?? true,
495
- executablePath: options.executablePath,
496
- });
497
- // Create context with viewport and optional headers
498
- const context = await this.browser.newContext({
499
- viewport: options.viewport ?? { width: 1280, height: 720 },
500
- extraHTTPHeaders: options.headers,
501
- });
502
- // Set default timeout to 10 seconds (Playwright default is 30s)
547
+ const viewport = options.viewport ?? { width: 1280, height: 720 };
548
+ let context;
549
+ if (hasExtensions) {
550
+ const extPaths = options.extensions.join(',');
551
+ const session = process.env.AGENT_BROWSER_SESSION || 'default';
552
+ context = await launcher.launchPersistentContext(path.join(os.tmpdir(), `agent-browser-ext-${session}`), {
553
+ headless: false,
554
+ executablePath: options.executablePath,
555
+ args: [`--disable-extensions-except=${extPaths}`, `--load-extension=${extPaths}`],
556
+ viewport,
557
+ extraHTTPHeaders: options.headers,
558
+ });
559
+ this.isPersistentContext = true;
560
+ }
561
+ else {
562
+ this.browser = await launcher.launch({
563
+ headless: options.headless ?? true,
564
+ executablePath: options.executablePath,
565
+ });
566
+ this.cdpPort = null;
567
+ context = await this.browser.newContext({ viewport, extraHTTPHeaders: options.headers });
568
+ }
503
569
  context.setDefaultTimeout(10000);
504
570
  this.contexts.push(context);
505
- // Create initial page
506
- const page = await context.newPage();
571
+ const page = context.pages()[0] ?? (await context.newPage());
507
572
  this.pages.push(page);
508
573
  this.activePageIndex = 0;
509
- // Automatically start console and error tracking
510
574
  this.setupPageTracking(page);
511
575
  }
512
576
  /**
513
- * Set up console and error tracking for a page
577
+ * Connect to a running browser via CDP (Chrome DevTools Protocol)
578
+ */
579
+ async connectViaCDP(cdpPort) {
580
+ if (!cdpPort) {
581
+ throw new Error('cdpPort is required for CDP connection');
582
+ }
583
+ const browser = await chromium.connectOverCDP(`http://localhost:${cdpPort}`).catch(() => {
584
+ throw new Error(`Failed to connect via CDP on port ${cdpPort}. ` +
585
+ `Make sure the app is running with --remote-debugging-port=${cdpPort}`);
586
+ });
587
+ // Validate and set up state, cleaning up browser connection if anything fails
588
+ try {
589
+ const contexts = browser.contexts();
590
+ if (contexts.length === 0) {
591
+ throw new Error('No browser context found. Make sure the app has an open window.');
592
+ }
593
+ const allPages = contexts.flatMap((context) => context.pages());
594
+ if (allPages.length === 0) {
595
+ throw new Error('No page found. Make sure the app has loaded content.');
596
+ }
597
+ // All validation passed - commit state
598
+ this.browser = browser;
599
+ this.cdpPort = cdpPort;
600
+ for (const context of contexts) {
601
+ this.contexts.push(context);
602
+ this.setupContextTracking(context);
603
+ }
604
+ for (const page of allPages) {
605
+ this.pages.push(page);
606
+ this.setupPageTracking(page);
607
+ }
608
+ this.activePageIndex = 0;
609
+ }
610
+ catch (error) {
611
+ // Clean up browser connection if validation or setup failed
612
+ await browser.close().catch(() => { });
613
+ throw error;
614
+ }
615
+ }
616
+ /**
617
+ * Set up console, error, and close tracking for a page
514
618
  */
515
619
  setupPageTracking(page) {
516
620
  page.on('console', (msg) => {
@@ -526,6 +630,24 @@ export class BrowserManager {
526
630
  timestamp: Date.now(),
527
631
  });
528
632
  });
633
+ page.on('close', () => {
634
+ const index = this.pages.indexOf(page);
635
+ if (index !== -1) {
636
+ this.pages.splice(index, 1);
637
+ if (this.activePageIndex >= this.pages.length) {
638
+ this.activePageIndex = Math.max(0, this.pages.length - 1);
639
+ }
640
+ }
641
+ });
642
+ }
643
+ /**
644
+ * Set up tracking for new pages in a context (for CDP connections)
645
+ */
646
+ setupContextTracking(context) {
647
+ context.on('page', (page) => {
648
+ this.pages.push(page);
649
+ this.setupPageTracking(page);
650
+ });
529
651
  }
530
652
  /**
531
653
  * Create a new tab in the current context
@@ -534,6 +656,8 @@ export class BrowserManager {
534
656
  if (!this.browser || this.contexts.length === 0) {
535
657
  throw new Error('Browser not launched');
536
658
  }
659
+ // Invalidate CDP session since we're switching to a new page
660
+ await this.invalidateCDPSession();
537
661
  const context = this.contexts[0]; // Use first context for tabs
538
662
  const page = await context.newPage();
539
663
  this.pages.push(page);
@@ -561,13 +685,32 @@ export class BrowserManager {
561
685
  this.setupPageTracking(page);
562
686
  return { index: this.activePageIndex, total: this.pages.length };
563
687
  }
688
+ /**
689
+ * Invalidate the current CDP session (must be called before switching pages)
690
+ * This ensures screencast and input injection work correctly after tab switch
691
+ */
692
+ async invalidateCDPSession() {
693
+ // Stop screencast if active (it's tied to the current page's CDP session)
694
+ if (this.screencastActive) {
695
+ await this.stopScreencast();
696
+ }
697
+ // Detach and clear the CDP session
698
+ if (this.cdpSession) {
699
+ await this.cdpSession.detach().catch(() => { });
700
+ this.cdpSession = null;
701
+ }
702
+ }
564
703
  /**
565
704
  * Switch to a specific tab/page by index
566
705
  */
567
- switchTo(index) {
706
+ async switchTo(index) {
568
707
  if (index < 0 || index >= this.pages.length) {
569
708
  throw new Error(`Invalid tab index: ${index}. Available: 0-${this.pages.length - 1}`);
570
709
  }
710
+ // Invalidate CDP session before switching (it's page-specific)
711
+ if (index !== this.activePageIndex) {
712
+ await this.invalidateCDPSession();
713
+ }
571
714
  this.activePageIndex = index;
572
715
  const page = this.pages[index];
573
716
  return {
@@ -587,6 +730,10 @@ export class BrowserManager {
587
730
  if (this.pages.length === 1) {
588
731
  throw new Error('Cannot close the last tab. Use "close" to close the browser.');
589
732
  }
733
+ // If closing the active tab, invalidate CDP session first
734
+ if (targetIndex === this.activePageIndex) {
735
+ await this.invalidateCDPSession();
736
+ }
590
737
  const page = this.pages[targetIndex];
591
738
  await page.close();
592
739
  this.pages.splice(targetIndex, 1);
@@ -611,25 +758,177 @@ export class BrowserManager {
611
758
  })));
612
759
  return tabs;
613
760
  }
761
+ /**
762
+ * Get or create a CDP session for the current page
763
+ * Only works with Chromium-based browsers
764
+ */
765
+ async getCDPSession() {
766
+ if (this.cdpSession) {
767
+ return this.cdpSession;
768
+ }
769
+ const page = this.getPage();
770
+ const context = page.context();
771
+ // Create a new CDP session attached to the page
772
+ this.cdpSession = await context.newCDPSession(page);
773
+ return this.cdpSession;
774
+ }
775
+ /**
776
+ * Check if screencast is currently active
777
+ */
778
+ isScreencasting() {
779
+ return this.screencastActive;
780
+ }
781
+ /**
782
+ * Start screencast - streams viewport frames via CDP
783
+ * @param callback Function called for each frame
784
+ * @param options Screencast options
785
+ */
786
+ async startScreencast(callback, options) {
787
+ if (this.screencastActive) {
788
+ throw new Error('Screencast already active');
789
+ }
790
+ const cdp = await this.getCDPSession();
791
+ this.frameCallback = callback;
792
+ this.screencastActive = true;
793
+ // Create and store the frame handler so we can remove it later
794
+ this.screencastFrameHandler = async (params) => {
795
+ const frame = {
796
+ data: params.data,
797
+ metadata: params.metadata,
798
+ sessionId: params.sessionId,
799
+ };
800
+ // Acknowledge the frame to receive the next one
801
+ await cdp.send('Page.screencastFrameAck', { sessionId: params.sessionId });
802
+ // Call the callback with the frame
803
+ if (this.frameCallback) {
804
+ this.frameCallback(frame);
805
+ }
806
+ };
807
+ // Listen for screencast frames
808
+ cdp.on('Page.screencastFrame', this.screencastFrameHandler);
809
+ // Start the screencast
810
+ await cdp.send('Page.startScreencast', {
811
+ format: options?.format ?? 'jpeg',
812
+ quality: options?.quality ?? 80,
813
+ maxWidth: options?.maxWidth ?? 1280,
814
+ maxHeight: options?.maxHeight ?? 720,
815
+ everyNthFrame: options?.everyNthFrame ?? 1,
816
+ });
817
+ }
818
+ /**
819
+ * Stop screencast
820
+ */
821
+ async stopScreencast() {
822
+ if (!this.screencastActive) {
823
+ return;
824
+ }
825
+ try {
826
+ const cdp = await this.getCDPSession();
827
+ await cdp.send('Page.stopScreencast');
828
+ // Remove the event listener to prevent accumulation
829
+ if (this.screencastFrameHandler) {
830
+ cdp.off('Page.screencastFrame', this.screencastFrameHandler);
831
+ }
832
+ }
833
+ catch {
834
+ // Ignore errors when stopping
835
+ }
836
+ this.screencastActive = false;
837
+ this.frameCallback = null;
838
+ this.screencastFrameHandler = null;
839
+ }
840
+ /**
841
+ * Inject a mouse event via CDP
842
+ */
843
+ async injectMouseEvent(params) {
844
+ const cdp = await this.getCDPSession();
845
+ const cdpButton = params.button === 'left'
846
+ ? 'left'
847
+ : params.button === 'right'
848
+ ? 'right'
849
+ : params.button === 'middle'
850
+ ? 'middle'
851
+ : 'none';
852
+ await cdp.send('Input.dispatchMouseEvent', {
853
+ type: params.type,
854
+ x: params.x,
855
+ y: params.y,
856
+ button: cdpButton,
857
+ clickCount: params.clickCount ?? 1,
858
+ deltaX: params.deltaX ?? 0,
859
+ deltaY: params.deltaY ?? 0,
860
+ modifiers: params.modifiers ?? 0,
861
+ });
862
+ }
863
+ /**
864
+ * Inject a keyboard event via CDP
865
+ */
866
+ async injectKeyboardEvent(params) {
867
+ const cdp = await this.getCDPSession();
868
+ await cdp.send('Input.dispatchKeyEvent', {
869
+ type: params.type,
870
+ key: params.key,
871
+ code: params.code,
872
+ text: params.text,
873
+ modifiers: params.modifiers ?? 0,
874
+ });
875
+ }
876
+ /**
877
+ * Inject touch event via CDP (for mobile emulation)
878
+ */
879
+ async injectTouchEvent(params) {
880
+ const cdp = await this.getCDPSession();
881
+ await cdp.send('Input.dispatchTouchEvent', {
882
+ type: params.type,
883
+ touchPoints: params.touchPoints.map((tp, i) => ({
884
+ x: tp.x,
885
+ y: tp.y,
886
+ id: tp.id ?? i,
887
+ })),
888
+ modifiers: params.modifiers ?? 0,
889
+ });
890
+ }
614
891
  /**
615
892
  * Close the browser and clean up
616
893
  */
617
894
  async close() {
618
- for (const page of this.pages) {
619
- await page.close().catch(() => { });
895
+ // Stop screencast if active
896
+ if (this.screencastActive) {
897
+ await this.stopScreencast();
898
+ }
899
+ // Clean up CDP session
900
+ if (this.cdpSession) {
901
+ await this.cdpSession.detach().catch(() => { });
902
+ this.cdpSession = null;
903
+ }
904
+ // CDP: only disconnect, don't close external app's pages
905
+ if (this.cdpPort !== null) {
906
+ if (this.browser) {
907
+ await this.browser.close().catch(() => { });
908
+ this.browser = null;
909
+ }
620
910
  }
621
- this.pages = [];
622
- for (const context of this.contexts) {
623
- await context.close().catch(() => { });
911
+ else {
912
+ // Regular browser: close everything
913
+ for (const page of this.pages) {
914
+ await page.close().catch(() => { });
915
+ }
916
+ for (const context of this.contexts) {
917
+ await context.close().catch(() => { });
918
+ }
919
+ if (this.browser) {
920
+ await this.browser.close().catch(() => { });
921
+ this.browser = null;
922
+ }
624
923
  }
924
+ this.pages = [];
625
925
  this.contexts = [];
626
- if (this.browser) {
627
- await this.browser.close().catch(() => { });
628
- this.browser = null;
629
- }
926
+ this.cdpPort = null;
927
+ this.isPersistentContext = false;
630
928
  this.activePageIndex = 0;
631
929
  this.refMap = {};
632
930
  this.lastSnapshot = '';
931
+ this.frameCallback = null;
633
932
  }
634
933
  }
635
934
  //# sourceMappingURL=browser.js.map