@canaryai/cli 0.1.9 → 0.1.12

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.
@@ -4,13 +4,12 @@ var HEARTBEAT_INTERVAL_MS = 3e4;
4
4
  var RECONNECT_DELAY_MS = 1e3;
5
5
  var MAX_RECONNECT_DELAY_MS = 3e4;
6
6
  var MAX_RECONNECT_ATTEMPTS = 10;
7
- var LocalBrowserHost = class {
7
+ var LocalBrowserHost = class _LocalBrowserHost {
8
8
  options;
9
9
  ws = null;
10
10
  browser = null;
11
- context = null;
12
- page = null;
13
- pendingDialogs = [];
11
+ contexts = /* @__PURE__ */ new Map();
12
+ static DEFAULT_CONTEXT_ID = "__default__";
14
13
  heartbeatTimer = null;
15
14
  reconnectAttempts = 0;
16
15
  isShuttingDown = false;
@@ -49,13 +48,13 @@ var LocalBrowserHost = class {
49
48
  }
50
49
  this.ws = null;
51
50
  }
52
- if (this.context) {
51
+ for (const [id, slot] of this.contexts) {
53
52
  try {
54
- await this.context.close();
53
+ await slot.context.close();
55
54
  } catch {
56
55
  }
57
- this.context = null;
58
56
  }
57
+ this.contexts.clear();
59
58
  if (this.browser) {
60
59
  try {
61
60
  await this.browser.close();
@@ -63,7 +62,6 @@ var LocalBrowserHost = class {
63
62
  }
64
63
  this.browser = null;
65
64
  }
66
- this.page = null;
67
65
  this.log("info", "Local browser host stopped");
68
66
  }
69
67
  // =========================================================================
@@ -158,10 +156,13 @@ var LocalBrowserHost = class {
158
156
  if (browserMode === "cdp" && cdpUrl) {
159
157
  this.log("info", "Connecting to existing Chrome via CDP", { cdpUrl });
160
158
  this.browser = await chromium.connectOverCDP(cdpUrl);
161
- const contexts = this.browser.contexts();
162
- this.context = contexts[0] ?? await this.browser.newContext();
163
- const pages = this.context.pages();
164
- this.page = pages[0] ?? await this.context.newPage();
159
+ const existingContexts = this.browser.contexts();
160
+ const ctx = existingContexts[0] ?? await this.browser.newContext();
161
+ const pages = ctx.pages();
162
+ const pg = pages[0] ?? await ctx.newPage();
163
+ const slot = { context: ctx, page: pg, pendingDialogs: [] };
164
+ pg.on("dialog", (dialog) => slot.pendingDialogs.push(dialog));
165
+ this.contexts.set(_LocalBrowserHost.DEFAULT_CONTEXT_ID, slot);
165
166
  } else {
166
167
  this.log("info", "Launching new Playwright browser", { headless });
167
168
  this.browser = await chromium.launch({
@@ -180,12 +181,12 @@ var LocalBrowserHost = class {
180
181
  this.log("debug", "Storage state file not found, starting fresh");
181
182
  }
182
183
  }
183
- this.context = await this.browser.newContext(contextOptions);
184
- this.page = await this.context.newPage();
184
+ const ctx = await this.browser.newContext(contextOptions);
185
+ const pg = await ctx.newPage();
186
+ const slot = { context: ctx, page: pg, pendingDialogs: [] };
187
+ pg.on("dialog", (dialog) => slot.pendingDialogs.push(dialog));
188
+ this.contexts.set(_LocalBrowserHost.DEFAULT_CONTEXT_ID, slot);
185
189
  }
186
- this.page.on("dialog", (dialog) => {
187
- this.pendingDialogs.push(dialog);
188
- });
189
190
  this.log("info", "Browser ready");
190
191
  }
191
192
  // =========================================================================
@@ -215,20 +216,23 @@ var LocalBrowserHost = class {
215
216
  }
216
217
  async handleCommand(command) {
217
218
  const startTime = Date.now();
218
- this.log("debug", `Executing command: ${command.method}`, { id: command.id });
219
+ const contextId = command.contextId;
220
+ this.log("debug", `Executing command: ${command.method}`, { id: command.id, contextId });
219
221
  try {
220
- const result = await this.executeMethod(command.method, command.args);
222
+ const result = await this.executeMethod(command.method, command.args, contextId);
221
223
  const response = {
222
224
  type: "response",
223
225
  id: crypto.randomUUID(),
224
226
  timestamp: Date.now(),
225
227
  requestId: command.id,
226
228
  success: true,
227
- result
229
+ result,
230
+ contextId
228
231
  };
229
232
  this.ws?.send(JSON.stringify(response));
230
233
  this.log("debug", `Command completed: ${command.method}`, {
231
234
  id: command.id,
235
+ contextId,
232
236
  durationMs: Date.now() - startTime
233
237
  });
234
238
  } catch (error) {
@@ -240,11 +244,13 @@ var LocalBrowserHost = class {
240
244
  requestId: command.id,
241
245
  success: false,
242
246
  error: errorMessage,
243
- stack: error instanceof Error ? error.stack : void 0
247
+ stack: error instanceof Error ? error.stack : void 0,
248
+ contextId
244
249
  };
245
250
  this.ws?.send(JSON.stringify(response));
246
251
  this.log("error", `Command failed: ${command.method}`, {
247
252
  id: command.id,
253
+ contextId,
248
254
  error: errorMessage
249
255
  });
250
256
  }
@@ -264,7 +270,51 @@ var LocalBrowserHost = class {
264
270
  // =========================================================================
265
271
  // Method Execution
266
272
  // =========================================================================
267
- async executeMethod(method, args) {
273
+ getSlot(contextId) {
274
+ const id = contextId ?? _LocalBrowserHost.DEFAULT_CONTEXT_ID;
275
+ const slot = this.contexts.get(id);
276
+ if (!slot) throw new Error(`Context not found: ${id}`);
277
+ return slot;
278
+ }
279
+ async createContextSlot(contextId, options) {
280
+ if (!this.browser) throw new Error("No browser available");
281
+ if (this.contexts.has(contextId)) throw new Error(`Context already exists: ${contextId}`);
282
+ const contextOptions = {
283
+ viewport: { width: 1920, height: 1080 }
284
+ };
285
+ if (options?.storageState) {
286
+ const tmpPath = `/tmp/storage-state-${crypto.randomUUID()}.json`;
287
+ await Bun.write(tmpPath, JSON.stringify(options.storageState));
288
+ contextOptions.storageState = tmpPath;
289
+ this.log("info", "Loaded inline storage state for new context", { contextId });
290
+ }
291
+ const ctx = await this.browser.newContext(contextOptions);
292
+ const pg = await ctx.newPage();
293
+ const slot = { context: ctx, page: pg, pendingDialogs: [] };
294
+ pg.on("dialog", (dialog) => slot.pendingDialogs.push(dialog));
295
+ this.contexts.set(contextId, slot);
296
+ this.log("info", "Created new context slot", { contextId });
297
+ }
298
+ async destroyContextSlot(contextId) {
299
+ if (contextId === _LocalBrowserHost.DEFAULT_CONTEXT_ID) {
300
+ throw new Error("Cannot destroy the default context");
301
+ }
302
+ const slot = this.contexts.get(contextId);
303
+ if (!slot) throw new Error(`Context not found: ${contextId}`);
304
+ try {
305
+ await slot.context.close();
306
+ } catch {
307
+ }
308
+ this.contexts.delete(contextId);
309
+ this.log("info", "Destroyed context slot", { contextId });
310
+ }
311
+ async executeMethod(method, args, contextId) {
312
+ switch (method) {
313
+ case "createContext":
314
+ return this.createContextSlot(args[0], args[1]);
315
+ case "destroyContext":
316
+ return this.destroyContextSlot(args[0]);
317
+ }
268
318
  switch (method) {
269
319
  // Lifecycle
270
320
  case "connect":
@@ -273,38 +323,40 @@ var LocalBrowserHost = class {
273
323
  return this.disconnect();
274
324
  // Navigation
275
325
  case "navigate":
276
- return this.navigate(args[0], args[1]);
326
+ return this.navigate(args[0], args[1], contextId);
277
327
  case "navigateBack":
278
- return this.navigateBack(args[0]);
328
+ return this.navigateBack(args[0], contextId);
279
329
  // Page Inspection
280
330
  case "snapshot":
281
- return this.snapshot(args[0]);
331
+ return this.snapshot(args[0], contextId);
282
332
  case "takeScreenshot":
283
- return this.takeScreenshot(args[0]);
333
+ return this.takeScreenshot(args[0], contextId);
284
334
  case "evaluate":
285
- return this.evaluate(args[0], args[1]);
335
+ return this.evaluate(args[0], args[1], contextId);
286
336
  case "runCode":
287
- return this.runCode(args[0], args[1]);
337
+ return this.runCode(args[0], args[1], contextId);
288
338
  case "consoleMessages":
289
339
  return this.consoleMessages(args[0]);
290
340
  case "networkRequests":
291
341
  return this.networkRequests(args[0]);
292
342
  // Interaction
293
343
  case "click":
294
- return this.click(args[0], args[1], args[2]);
344
+ return this.click(args[0], args[1], args[2], contextId);
295
345
  case "clickAtCoordinates":
296
346
  return this.clickAtCoordinates(
297
347
  args[0],
298
348
  args[1],
299
349
  args[2],
300
- args[3]
350
+ args[3],
351
+ contextId
301
352
  );
302
353
  case "moveToCoordinates":
303
354
  return this.moveToCoordinates(
304
355
  args[0],
305
356
  args[1],
306
357
  args[2],
307
- args[3]
358
+ args[3],
359
+ contextId
308
360
  );
309
361
  case "dragCoordinates":
310
362
  return this.dragCoordinates(
@@ -313,17 +365,19 @@ var LocalBrowserHost = class {
313
365
  args[2],
314
366
  args[3],
315
367
  args[4],
316
- args[5]
368
+ args[5],
369
+ contextId
317
370
  );
318
371
  case "hover":
319
- return this.hover(args[0], args[1], args[2]);
372
+ return this.hover(args[0], args[1], args[2], contextId);
320
373
  case "drag":
321
374
  return this.drag(
322
375
  args[0],
323
376
  args[1],
324
377
  args[2],
325
378
  args[3],
326
- args[4]
379
+ args[4],
380
+ contextId
327
381
  );
328
382
  case "type":
329
383
  return this.type(
@@ -331,50 +385,55 @@ var LocalBrowserHost = class {
331
385
  args[1],
332
386
  args[2],
333
387
  args[3],
334
- args[4]
388
+ args[4],
389
+ contextId
335
390
  );
336
391
  case "pressKey":
337
- return this.pressKey(args[0], args[1]);
392
+ return this.pressKey(args[0], args[1], contextId);
338
393
  case "fillForm":
339
- return this.fillForm(args[0], args[1]);
394
+ return this.fillForm(args[0], args[1], contextId);
340
395
  case "selectOption":
341
396
  return this.selectOption(
342
397
  args[0],
343
398
  args[1],
344
399
  args[2],
345
- args[3]
400
+ args[3],
401
+ contextId
346
402
  );
347
403
  case "fileUpload":
348
- return this.fileUpload(args[0], args[1]);
404
+ return this.fileUpload(args[0], args[1], contextId);
349
405
  // Dialogs
350
406
  case "handleDialog":
351
- return this.handleDialog(args[0], args[1], args[2]);
407
+ return this.handleDialog(args[0], args[1], args[2], contextId);
352
408
  // Waiting
353
409
  case "waitFor":
354
- return this.waitFor(args[0]);
410
+ return this.waitFor(args[0], contextId);
355
411
  // Browser Management
356
412
  case "close":
357
- return this.closePage(args[0]);
413
+ return this.closePage(args[0], contextId);
358
414
  case "resize":
359
- return this.resize(args[0], args[1], args[2]);
415
+ return this.resize(args[0], args[1], args[2], contextId);
360
416
  case "tabs":
361
- return this.tabs(args[0], args[1], args[2]);
417
+ return this.tabs(args[0], args[1], args[2], contextId);
418
+ // Context Management
419
+ case "swapContext":
420
+ return this.handleSwapContext(args[0], contextId);
362
421
  // Storage
363
422
  case "getStorageState":
364
- return this.getStorageState(args[0]);
423
+ return this.getStorageState(args[0], contextId);
365
424
  case "getCurrentUrl":
366
- return this.getCurrentUrl(args[0]);
425
+ return this.getCurrentUrl(args[0], contextId);
367
426
  case "getTitle":
368
- return this.getTitle(args[0]);
427
+ return this.getTitle(args[0], contextId);
369
428
  case "getLinks":
370
- return this.getLinks(args[0]);
429
+ return this.getLinks(args[0], contextId);
371
430
  case "getElementBoundingBox":
372
- return this.getElementBoundingBox(args[0], args[1]);
431
+ return this.getElementBoundingBox(args[0], args[1], contextId);
373
432
  // Tracing
374
433
  case "startTracing":
375
- return this.startTracing(args[0]);
434
+ return this.startTracing(args[0], contextId);
376
435
  case "stopTracing":
377
- return this.stopTracing(args[0]);
436
+ return this.stopTracing(args[0], contextId);
378
437
  // Video
379
438
  case "isVideoRecordingEnabled":
380
439
  return false;
@@ -390,12 +449,47 @@ var LocalBrowserHost = class {
390
449
  // =========================================================================
391
450
  // IBrowserClient Method Implementations
392
451
  // =========================================================================
393
- getPage() {
394
- if (!this.page) throw new Error("No page available");
395
- return this.page;
452
+ async handleSwapContext(options, contextId) {
453
+ if (!this.browser) throw new Error("No browser available");
454
+ const slotId = contextId ?? _LocalBrowserHost.DEFAULT_CONTEXT_ID;
455
+ const existing = this.contexts.get(slotId);
456
+ if (existing) {
457
+ await existing.context.close();
458
+ this.contexts.delete(slotId);
459
+ }
460
+ const contextOptions = {
461
+ viewport: { width: 1920, height: 1080 }
462
+ };
463
+ if (options.storageState) {
464
+ const tmpPath = `/tmp/storage-state-${crypto.randomUUID()}.json`;
465
+ await Bun.write(tmpPath, JSON.stringify(options.storageState));
466
+ contextOptions.storageState = tmpPath;
467
+ this.log("info", "Loaded inline storage state for context swap");
468
+ } else if (options.storageStatePath) {
469
+ try {
470
+ const exists = await Bun.file(options.storageStatePath).exists();
471
+ if (exists) {
472
+ contextOptions.storageState = options.storageStatePath;
473
+ this.log("info", "Loading storage state from file for context swap", {
474
+ storageStatePath: options.storageStatePath
475
+ });
476
+ }
477
+ } catch {
478
+ this.log("debug", "Storage state file not found, starting fresh context");
479
+ }
480
+ }
481
+ const ctx = await this.browser.newContext(contextOptions);
482
+ const pg = await ctx.newPage();
483
+ const slot = { context: ctx, page: pg, pendingDialogs: [] };
484
+ pg.on("dialog", (dialog) => slot.pendingDialogs.push(dialog));
485
+ this.contexts.set(slotId, slot);
486
+ this.log("info", "Browser context swapped successfully", { contextId: slotId });
396
487
  }
397
- resolveRef(ref) {
398
- return this.getPage().locator(`aria-ref=${ref}`);
488
+ getPage(contextId) {
489
+ return this.getSlot(contextId).page;
490
+ }
491
+ resolveRef(ref, contextId) {
492
+ return this.getPage(contextId).locator(`aria-ref=${ref}`);
399
493
  }
400
494
  async connect(_options) {
401
495
  return;
@@ -403,27 +497,27 @@ var LocalBrowserHost = class {
403
497
  async disconnect() {
404
498
  await this.stop();
405
499
  }
406
- async navigate(url, _opts) {
407
- const page = this.getPage();
500
+ async navigate(url, _opts, contextId) {
501
+ const page = this.getPage(contextId);
408
502
  await page.goto(url, { waitUntil: "domcontentloaded" });
409
503
  await page.waitForLoadState("load", { timeout: 5e3 }).catch(() => {
410
504
  });
411
- return this.captureSnapshot();
505
+ return this.captureSnapshot(contextId);
412
506
  }
413
- async navigateBack(_opts) {
414
- await this.getPage().goBack();
415
- return this.captureSnapshot();
507
+ async navigateBack(_opts, contextId) {
508
+ await this.getPage(contextId).goBack();
509
+ return this.captureSnapshot(contextId);
416
510
  }
417
- async snapshot(_opts) {
418
- return this.captureSnapshot();
511
+ async snapshot(_opts, contextId) {
512
+ return this.captureSnapshot(contextId);
419
513
  }
420
- async captureSnapshot() {
421
- const page = this.getPage();
514
+ async captureSnapshot(contextId) {
515
+ const page = this.getPage(contextId);
422
516
  this.lastSnapshotYaml = await page._snapshotForAI({ mode: "full" });
423
517
  return this.lastSnapshotYaml;
424
518
  }
425
- async takeScreenshot(opts) {
426
- const page = this.getPage();
519
+ async takeScreenshot(opts, contextId) {
520
+ const page = this.getPage(contextId);
427
521
  const buffer = await page.screenshot({
428
522
  type: opts?.type ?? "jpeg",
429
523
  fullPage: opts?.fullPage ?? false
@@ -431,12 +525,12 @@ var LocalBrowserHost = class {
431
525
  const mime = opts?.type === "png" ? "image/png" : "image/jpeg";
432
526
  return `data:${mime};base64,${buffer.toString("base64")}`;
433
527
  }
434
- async evaluate(fn, _opts) {
435
- const page = this.getPage();
528
+ async evaluate(fn, _opts, contextId) {
529
+ const page = this.getPage(contextId);
436
530
  return page.evaluate(new Function(`return (${fn})()`));
437
531
  }
438
- async runCode(code, _opts) {
439
- const page = this.getPage();
532
+ async runCode(code, _opts, contextId) {
533
+ const page = this.getPage(contextId);
440
534
  const fn = new Function("page", `return (async () => { ${code} })()`);
441
535
  return fn(page);
442
536
  }
@@ -446,15 +540,15 @@ var LocalBrowserHost = class {
446
540
  async networkRequests(_opts) {
447
541
  return "Network request capture not implemented in CLI host";
448
542
  }
449
- async click(ref, _elementDesc, opts) {
450
- const locator = this.resolveRef(ref);
543
+ async click(ref, _elementDesc, opts, contextId) {
544
+ const locator = this.resolveRef(ref, contextId);
451
545
  await locator.scrollIntoViewIfNeeded({ timeout: 5e3 }).catch(() => {
452
546
  });
453
547
  const box = await locator.boundingBox();
454
548
  if (box) {
455
549
  const centerX = box.x + box.width / 2;
456
550
  const centerY = box.y + box.height / 2;
457
- const page = this.getPage();
551
+ const page = this.getPage(contextId);
458
552
  if (opts?.modifiers?.length) {
459
553
  for (const mod of opts.modifiers) {
460
554
  await page.keyboard.down(mod);
@@ -478,34 +572,34 @@ var LocalBrowserHost = class {
478
572
  }
479
573
  }
480
574
  }
481
- async clickAtCoordinates(x, y, _elementDesc, opts) {
482
- const page = this.getPage();
575
+ async clickAtCoordinates(x, y, _elementDesc, opts, contextId) {
576
+ const page = this.getPage(contextId);
483
577
  if (opts?.doubleClick) {
484
578
  await page.mouse.dblclick(x, y);
485
579
  } else {
486
580
  await page.mouse.click(x, y);
487
581
  }
488
582
  }
489
- async moveToCoordinates(x, y, _elementDesc, _opts) {
490
- await this.getPage().mouse.move(x, y);
583
+ async moveToCoordinates(x, y, _elementDesc, _opts, contextId) {
584
+ await this.getPage(contextId).mouse.move(x, y);
491
585
  }
492
- async dragCoordinates(startX, startY, endX, endY, _elementDesc, _opts) {
493
- const page = this.getPage();
586
+ async dragCoordinates(startX, startY, endX, endY, _elementDesc, _opts, contextId) {
587
+ const page = this.getPage(contextId);
494
588
  await page.mouse.move(startX, startY);
495
589
  await page.mouse.down();
496
590
  await page.mouse.move(endX, endY);
497
591
  await page.mouse.up();
498
592
  }
499
- async hover(ref, _elementDesc, opts) {
500
- await this.resolveRef(ref).hover({ timeout: opts?.timeoutMs ?? 3e4 });
593
+ async hover(ref, _elementDesc, opts, contextId) {
594
+ await this.resolveRef(ref, contextId).hover({ timeout: opts?.timeoutMs ?? 3e4 });
501
595
  }
502
- async drag(startRef, _startElement, endRef, _endElement, opts) {
503
- const startLocator = this.resolveRef(startRef);
504
- const endLocator = this.resolveRef(endRef);
596
+ async drag(startRef, _startElement, endRef, _endElement, opts, contextId) {
597
+ const startLocator = this.resolveRef(startRef, contextId);
598
+ const endLocator = this.resolveRef(endRef, contextId);
505
599
  await startLocator.dragTo(endLocator, { timeout: opts?.timeoutMs ?? 6e4 });
506
600
  }
507
- async type(ref, text, _elementDesc, submit, opts) {
508
- const locator = this.resolveRef(ref);
601
+ async type(ref, text, _elementDesc, submit, opts, contextId) {
602
+ const locator = this.resolveRef(ref, contextId);
509
603
  await locator.clear();
510
604
  await locator.pressSequentially(text, {
511
605
  delay: opts?.delay ?? 0,
@@ -515,12 +609,12 @@ var LocalBrowserHost = class {
515
609
  await locator.press("Enter");
516
610
  }
517
611
  }
518
- async pressKey(key, _opts) {
519
- await this.getPage().keyboard.press(key);
612
+ async pressKey(key, _opts, contextId) {
613
+ await this.getPage(contextId).keyboard.press(key);
520
614
  }
521
- async fillForm(fields, opts) {
615
+ async fillForm(fields, opts, contextId) {
522
616
  for (const field of fields) {
523
- const locator = this.resolveRef(field.ref);
617
+ const locator = this.resolveRef(field.ref, contextId);
524
618
  const fieldType = field.type ?? "textbox";
525
619
  switch (fieldType) {
526
620
  case "checkbox": {
@@ -542,17 +636,18 @@ var LocalBrowserHost = class {
542
636
  }
543
637
  }
544
638
  }
545
- async selectOption(ref, value, _elementDesc, opts) {
546
- await this.resolveRef(ref).selectOption(value, { timeout: opts?.timeoutMs ?? 3e4 });
639
+ async selectOption(ref, value, _elementDesc, opts, contextId) {
640
+ await this.resolveRef(ref, contextId).selectOption(value, { timeout: opts?.timeoutMs ?? 3e4 });
547
641
  }
548
- async fileUpload(paths, opts) {
549
- const fileChooser = await this.getPage().waitForEvent("filechooser", {
642
+ async fileUpload(paths, opts, contextId) {
643
+ const fileChooser = await this.getPage(contextId).waitForEvent("filechooser", {
550
644
  timeout: opts?.timeoutMs ?? 3e4
551
645
  });
552
646
  await fileChooser.setFiles(paths);
553
647
  }
554
- async handleDialog(action, promptText, _opts) {
555
- const dialog = this.pendingDialogs.shift();
648
+ async handleDialog(action, promptText, _opts, contextId) {
649
+ const slot = this.getSlot(contextId);
650
+ const dialog = slot.pendingDialogs.shift();
556
651
  if (dialog) {
557
652
  if (action === "accept") {
558
653
  await dialog.accept(promptText);
@@ -561,8 +656,8 @@ var LocalBrowserHost = class {
561
656
  }
562
657
  }
563
658
  }
564
- async waitFor(opts) {
565
- const page = this.getPage();
659
+ async waitFor(opts, contextId) {
660
+ const page = this.getPage(contextId);
566
661
  const timeout = opts?.timeout ?? opts?.timeoutMs ?? 3e4;
567
662
  if (opts?.timeSec) {
568
663
  await page.waitForTimeout(opts.timeSec * 1e3);
@@ -583,16 +678,20 @@ var LocalBrowserHost = class {
583
678
  });
584
679
  }
585
680
  }
586
- async closePage(_opts) {
587
- await this.getPage().close();
588
- this.page = null;
681
+ async closePage(_opts, contextId) {
682
+ const slot = this.getSlot(contextId);
683
+ await slot.page.close();
684
+ const remaining = slot.context.pages();
685
+ if (remaining.length > 0) {
686
+ slot.page = remaining[0];
687
+ }
589
688
  }
590
- async resize(width, height, _opts) {
591
- await this.getPage().setViewportSize({ width, height });
689
+ async resize(width, height, _opts, contextId) {
690
+ await this.getPage(contextId).setViewportSize({ width, height });
592
691
  }
593
- async tabs(action, index, _opts) {
594
- if (!this.context) throw new Error("No context available");
595
- const pages = this.context.pages();
692
+ async tabs(action, index, _opts, contextId) {
693
+ const slot = this.getSlot(contextId);
694
+ const pages = slot.context.pages();
596
695
  switch (action) {
597
696
  case "list":
598
697
  return Promise.all(
@@ -603,58 +702,58 @@ var LocalBrowserHost = class {
603
702
  }))
604
703
  );
605
704
  case "new": {
606
- const newPage = await this.context.newPage();
607
- this.page = newPage;
608
- newPage.on("dialog", (dialog) => this.pendingDialogs.push(dialog));
705
+ const newPage = await slot.context.newPage();
706
+ slot.page = newPage;
707
+ newPage.on("dialog", (dialog) => slot.pendingDialogs.push(dialog));
609
708
  return { index: pages.length };
610
709
  }
611
710
  case "close":
612
711
  if (index !== void 0 && pages[index]) {
613
712
  await pages[index].close();
614
713
  } else {
615
- await this.page?.close();
714
+ await slot.page.close();
616
715
  }
617
- this.page = this.context.pages()[0] ?? null;
716
+ slot.page = slot.context.pages()[0] ?? slot.page;
618
717
  break;
619
718
  case "select":
620
719
  if (index !== void 0 && pages[index]) {
621
- this.page = pages[index];
720
+ slot.page = pages[index];
622
721
  }
623
722
  break;
624
723
  }
625
724
  return null;
626
725
  }
627
- async getStorageState(_opts) {
628
- if (!this.context) throw new Error("No context available");
629
- return this.context.storageState();
726
+ async getStorageState(_opts, contextId) {
727
+ const slot = this.getSlot(contextId);
728
+ return slot.context.storageState();
630
729
  }
631
- async getCurrentUrl(_opts) {
632
- return this.getPage().url();
730
+ async getCurrentUrl(_opts, contextId) {
731
+ return this.getPage(contextId).url();
633
732
  }
634
- async getTitle(_opts) {
635
- return this.getPage().title();
733
+ async getTitle(_opts, contextId) {
734
+ return this.getPage(contextId).title();
636
735
  }
637
- async getLinks(_opts) {
638
- const page = this.getPage();
736
+ async getLinks(_opts, contextId) {
737
+ const page = this.getPage(contextId);
639
738
  return page.$$eval(
640
739
  "a[href]",
641
740
  (links) => links.map((a) => a.href).filter((h) => !!h && (h.startsWith("http://") || h.startsWith("https://")))
642
741
  );
643
742
  }
644
- async getElementBoundingBox(ref, _opts) {
645
- const locator = this.resolveRef(ref);
743
+ async getElementBoundingBox(ref, _opts, contextId) {
744
+ const locator = this.resolveRef(ref, contextId);
646
745
  const box = await locator.boundingBox();
647
746
  if (!box) return null;
648
747
  return { x: box.x, y: box.y, width: box.width, height: box.height };
649
748
  }
650
- async startTracing(_opts) {
651
- if (!this.context) throw new Error("No context available");
652
- await this.context.tracing.start({ screenshots: true, snapshots: true });
749
+ async startTracing(_opts, contextId) {
750
+ const slot = this.getSlot(contextId);
751
+ await slot.context.tracing.start({ screenshots: true, snapshots: true });
653
752
  }
654
- async stopTracing(_opts) {
655
- if (!this.context) throw new Error("No context available");
753
+ async stopTracing(_opts, contextId) {
754
+ const slot = this.getSlot(contextId);
656
755
  const tracePath = `/tmp/trace-${Date.now()}.zip`;
657
- await this.context.tracing.stop({ path: tracePath });
756
+ await slot.context.tracing.stop({ path: tracePath });
658
757
  return {
659
758
  trace: tracePath,
660
759
  network: "",
@@ -668,4 +767,4 @@ var LocalBrowserHost = class {
668
767
  export {
669
768
  LocalBrowserHost
670
769
  };
671
- //# sourceMappingURL=chunk-G2X3H7AM.js.map
770
+ //# sourceMappingURL=chunk-L26U3BST.js.map