browser-pilot 0.0.9 → 0.0.11

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.
@@ -1,7 +1,7 @@
1
1
  import { C as CDPClient } from './client-7Nqka5MV.cjs';
2
2
  import { b as ConnectOptions } from './types--wXNHUwt.cjs';
3
- import { y as Page } from './types-CYw-7vx1.cjs';
4
- export { k as ActionOptions, l as ActionResult, m as ConsoleHandler, n as ConsoleMessage, o as ConsoleMessageType, p as CustomSelectConfig, D as Dialog, q as DialogHandler, r as DialogType, s as Download, E as ElementInfo, t as ElementNotFoundError, u as EmulationState, v as ErrorHandler, ae as FailureHint, F as FileInput, w as FillOptions, G as GeolocationOptions, I as InteractiveElement, N as NavigationError, x as NetworkIdleOptions, z as PageError, H as PageSnapshot, J as SnapshotNode, K as SubmitOptions, T as TimeoutError, L as TypeOptions, U as UserAgentMetadata, M as UserAgentOptions, V as ViewportOptions, W as WaitForOptions } from './types-CYw-7vx1.cjs';
3
+ import { y as Page } from './types-GWuQJs_e.cjs';
4
+ export { k as ActionOptions, l as ActionResult, m as ConsoleHandler, n as ConsoleMessage, o as ConsoleMessageType, p as CustomSelectConfig, D as Dialog, q as DialogHandler, r as DialogType, s as Download, E as ElementInfo, t as ElementNotFoundError, u as EmulationState, v as ErrorHandler, ae as FailureHint, F as FileInput, w as FillOptions, G as GeolocationOptions, I as InteractiveElement, N as NavigationError, x as NetworkIdleOptions, z as PageError, H as PageSnapshot, J as SnapshotNode, K as SubmitOptions, T as TimeoutError, L as TypeOptions, U as UserAgentMetadata, M as UserAgentOptions, V as ViewportOptions, W as WaitForOptions } from './types-GWuQJs_e.cjs';
5
5
 
6
6
  /**
7
7
  * Browser class - manages CDP connection and pages
@@ -14,6 +14,18 @@ interface BrowserOptions extends ConnectOptions {
14
14
  interface PageOptions {
15
15
  /** Specific target ID to attach to */
16
16
  targetId?: string;
17
+ /** Filter targets to those whose URL contains this string */
18
+ targetUrl?: string;
19
+ /**
20
+ * Minimum acceptable viewport dimensions.
21
+ * If the attached target's viewport is smaller, it will be overridden.
22
+ * Defaults to { width: 200, height: 200 }.
23
+ * Set to false to disable viewport validation.
24
+ */
25
+ minViewport?: {
26
+ width: number;
27
+ height: number;
28
+ } | false;
17
29
  }
18
30
  declare class Browser {
19
31
  private cdp;
@@ -25,8 +37,13 @@ declare class Browser {
25
37
  */
26
38
  static connect(options: BrowserOptions): Promise<Browser>;
27
39
  /**
28
- * Get or create a page by name
29
- * If no name is provided, returns the first available page or creates a new one
40
+ * Get or create a page by name.
41
+ * If no name is provided, returns the first available page or creates a new one.
42
+ *
43
+ * Target selection heuristics (when no targetId is specified):
44
+ * - Prefer http/https URLs over chrome://, devtools://, about:blank
45
+ * - Prefer unattached targets (not already controlled by another client)
46
+ * - Filter by targetUrl if provided
30
47
  */
31
48
  page(name?: string, options?: PageOptions): Promise<Page>;
32
49
  /**
@@ -72,4 +89,4 @@ declare class Browser {
72
89
  */
73
90
  declare function connect(options: BrowserOptions): Promise<Browser>;
74
91
 
75
- export { Browser, type BrowserOptions, Page, connect };
92
+ export { Browser, type BrowserOptions, Page, type PageOptions, connect };
package/dist/browser.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { C as CDPClient } from './client-7Nqka5MV.js';
2
2
  import { b as ConnectOptions } from './types--wXNHUwt.js';
3
- import { y as Page } from './types-DOGsEYQa.js';
4
- export { k as ActionOptions, l as ActionResult, m as ConsoleHandler, n as ConsoleMessage, o as ConsoleMessageType, p as CustomSelectConfig, D as Dialog, q as DialogHandler, r as DialogType, s as Download, E as ElementInfo, t as ElementNotFoundError, u as EmulationState, v as ErrorHandler, ae as FailureHint, F as FileInput, w as FillOptions, G as GeolocationOptions, I as InteractiveElement, N as NavigationError, x as NetworkIdleOptions, z as PageError, H as PageSnapshot, J as SnapshotNode, K as SubmitOptions, T as TimeoutError, L as TypeOptions, U as UserAgentMetadata, M as UserAgentOptions, V as ViewportOptions, W as WaitForOptions } from './types-DOGsEYQa.js';
3
+ import { y as Page } from './types-DtGF3yGl.js';
4
+ export { k as ActionOptions, l as ActionResult, m as ConsoleHandler, n as ConsoleMessage, o as ConsoleMessageType, p as CustomSelectConfig, D as Dialog, q as DialogHandler, r as DialogType, s as Download, E as ElementInfo, t as ElementNotFoundError, u as EmulationState, v as ErrorHandler, ae as FailureHint, F as FileInput, w as FillOptions, G as GeolocationOptions, I as InteractiveElement, N as NavigationError, x as NetworkIdleOptions, z as PageError, H as PageSnapshot, J as SnapshotNode, K as SubmitOptions, T as TimeoutError, L as TypeOptions, U as UserAgentMetadata, M as UserAgentOptions, V as ViewportOptions, W as WaitForOptions } from './types-DtGF3yGl.js';
5
5
 
6
6
  /**
7
7
  * Browser class - manages CDP connection and pages
@@ -14,6 +14,18 @@ interface BrowserOptions extends ConnectOptions {
14
14
  interface PageOptions {
15
15
  /** Specific target ID to attach to */
16
16
  targetId?: string;
17
+ /** Filter targets to those whose URL contains this string */
18
+ targetUrl?: string;
19
+ /**
20
+ * Minimum acceptable viewport dimensions.
21
+ * If the attached target's viewport is smaller, it will be overridden.
22
+ * Defaults to { width: 200, height: 200 }.
23
+ * Set to false to disable viewport validation.
24
+ */
25
+ minViewport?: {
26
+ width: number;
27
+ height: number;
28
+ } | false;
17
29
  }
18
30
  declare class Browser {
19
31
  private cdp;
@@ -25,8 +37,13 @@ declare class Browser {
25
37
  */
26
38
  static connect(options: BrowserOptions): Promise<Browser>;
27
39
  /**
28
- * Get or create a page by name
29
- * If no name is provided, returns the first available page or creates a new one
40
+ * Get or create a page by name.
41
+ * If no name is provided, returns the first available page or creates a new one.
42
+ *
43
+ * Target selection heuristics (when no targetId is specified):
44
+ * - Prefer http/https URLs over chrome://, devtools://, about:blank
45
+ * - Prefer unattached targets (not already controlled by another client)
46
+ * - Filter by targetUrl if provided
30
47
  */
31
48
  page(name?: string, options?: PageOptions): Promise<Page>;
32
49
  /**
@@ -72,4 +89,4 @@ declare class Browser {
72
89
  */
73
90
  declare function connect(options: BrowserOptions): Promise<Browser>;
74
91
 
75
- export { Browser, type BrowserOptions, Page, connect };
92
+ export { Browser, type BrowserOptions, Page, type PageOptions, connect };
package/dist/browser.mjs CHANGED
@@ -2,14 +2,14 @@ import {
2
2
  Browser,
3
3
  Page,
4
4
  connect
5
- } from "./chunk-7OSR2CAE.mjs";
5
+ } from "./chunk-JHAF52FA.mjs";
6
6
  import "./chunk-BCOZUKWS.mjs";
7
- import "./chunk-R3PS4PCM.mjs";
7
+ import "./chunk-BRAFQUMG.mjs";
8
8
  import {
9
9
  ElementNotFoundError,
10
10
  NavigationError,
11
11
  TimeoutError
12
- } from "./chunk-KKW2SZLV.mjs";
12
+ } from "./chunk-FAUNIZR7.mjs";
13
13
  export {
14
14
  Browser,
15
15
  ElementNotFoundError,
@@ -129,6 +129,34 @@ var BrowserlessProvider = class {
129
129
  };
130
130
 
131
131
  // src/providers/generic.ts
132
+ function sleep(ms) {
133
+ return new Promise((resolve) => setTimeout(resolve, ms));
134
+ }
135
+ async function fetchDevToolsJson(host, path, errorPrefix, options = {}) {
136
+ const protocol = host.includes("://") ? "" : "http://";
137
+ const attempts = options.attempts ?? 1;
138
+ let delayMs = options.initialDelayMs ?? 50;
139
+ const maxDelayMs = options.maxDelayMs ?? 250;
140
+ let lastError;
141
+ for (let attempt = 1; attempt <= attempts; attempt++) {
142
+ try {
143
+ const response = await fetch(`${protocol}${host}${path}`);
144
+ if (response.ok) {
145
+ return await response.json();
146
+ }
147
+ lastError = new Error(`${errorPrefix}: ${response.status}`);
148
+ } catch (error) {
149
+ lastError = new Error(
150
+ `${errorPrefix}: ${error instanceof Error ? error.message : String(error)}`
151
+ );
152
+ }
153
+ if (attempt < attempts) {
154
+ await sleep(delayMs);
155
+ delayMs = Math.min(delayMs * 2, maxDelayMs);
156
+ }
157
+ }
158
+ throw lastError ?? new Error(errorPrefix);
159
+ }
132
160
  var GenericProvider = class {
133
161
  name = "generic";
134
162
  wsUrl;
@@ -147,20 +175,14 @@ var GenericProvider = class {
147
175
  }
148
176
  };
149
177
  async function discoverTargets(host = "localhost:9222") {
150
- const protocol = host.includes("://") ? "" : "http://";
151
- const response = await fetch(`${protocol}${host}/json/list`);
152
- if (!response.ok) {
153
- throw new Error(`Failed to discover targets: ${response.status}`);
154
- }
155
- return await response.json();
178
+ return fetchDevToolsJson(host, "/json/list", "Failed to discover targets");
156
179
  }
157
180
  async function getBrowserWebSocketUrl(host = "localhost:9222") {
158
- const protocol = host.includes("://") ? "" : "http://";
159
- const response = await fetch(`${protocol}${host}/json/version`);
160
- if (!response.ok) {
161
- throw new Error(`Failed to get browser info: ${response.status}`);
162
- }
163
- const info = await response.json();
181
+ const info = await fetchDevToolsJson(host, "/json/version", "Failed to get browser info", {
182
+ attempts: 10,
183
+ initialDelayMs: 50,
184
+ maxDelayMs: 250
185
+ });
164
186
  return info.webSocketDebuggerUrl;
165
187
  }
166
188
 
@@ -4,7 +4,12 @@ var ElementNotFoundError = class extends Error {
4
4
  hints;
5
5
  constructor(selectors, hints) {
6
6
  const selectorList = Array.isArray(selectors) ? selectors : [selectors];
7
- super(`Element not found: ${selectorList.join(", ")}`);
7
+ let msg = `Element not found: ${selectorList.join(", ")}`;
8
+ if (hints?.length) {
9
+ msg += `. Did you mean: ${hints.slice(0, 3).map((h) => `${h.element.ref} (${h.element.role} "${h.element.name}")`).join(", ")}`;
10
+ }
11
+ msg += `. Run 'bp snapshot' to see available elements.`;
12
+ super(msg);
8
13
  this.name = "ElementNotFoundError";
9
14
  this.selectors = selectorList;
10
15
  this.hints = hints;
@@ -12,7 +17,8 @@ var ElementNotFoundError = class extends Error {
12
17
  };
13
18
  var TimeoutError = class extends Error {
14
19
  constructor(message = "Operation timed out") {
15
- super(message);
20
+ const msg = message.includes("bp snapshot") ? message : `${message}. Run 'bp snapshot' to check current page state.`;
21
+ super(msg);
16
22
  this.name = "TimeoutError";
17
23
  }
18
24
  };
@@ -95,7 +101,7 @@ var BatchExecutor = class {
95
101
  }
96
102
  case "click": {
97
103
  if (!step.selector) throw new Error("click requires selector");
98
- if (step.waitForNavigation) {
104
+ if (step.waitForNavigation === true) {
99
105
  const navPromise = this.page.waitForNavigation({ timeout, optional });
100
106
  await this.page.click(step.selector, { timeout, optional });
101
107
  await navPromise;
@@ -110,7 +116,6 @@ var BatchExecutor = class {
110
116
  await this.page.fill(step.selector, step.value, {
111
117
  timeout,
112
118
  optional,
113
- clear: step.clear ?? true,
114
119
  blur: step.blur
115
120
  });
116
121
  return { selectorUsed: this.getUsedSelector(step.selector) };
@@ -158,7 +163,8 @@ var BatchExecutor = class {
158
163
  await this.page.submit(step.selector, {
159
164
  timeout,
160
165
  optional,
161
- method: step.method ?? "enter+click"
166
+ method: step.method ?? "enter+click",
167
+ waitForNavigation: step.waitForNavigation
162
168
  });
163
169
  return { selectorUsed: this.getUsedSelector(step.selector) };
164
170
  }
@@ -380,7 +386,6 @@ var ACTION_RULES = {
380
386
  fill: {
381
387
  required: { selector: { type: "string|string[]" }, value: { type: "string" } },
382
388
  optional: {
383
- clear: { type: "boolean" },
384
389
  blur: { type: "boolean" }
385
390
  }
386
391
  },
@@ -411,7 +416,8 @@ var ACTION_RULES = {
411
416
  submit: {
412
417
  required: { selector: { type: "string|string[]" } },
413
418
  optional: {
414
- method: { type: "string", enum: ["enter", "click", "enter+click"] }
419
+ method: { type: "string", enum: ["enter", "click", "enter+click"] },
420
+ waitForNavigation: { type: "boolean|auto" }
415
421
  }
416
422
  },
417
423
  press: {
@@ -489,7 +495,6 @@ var KNOWN_STEP_FIELDS = /* @__PURE__ */ new Set([
489
495
  "timeout",
490
496
  "optional",
491
497
  "method",
492
- "clear",
493
498
  "blur",
494
499
  "delay",
495
500
  "waitForNavigation",
@@ -570,6 +575,11 @@ function checkFieldType(value, rule) {
570
575
  case "boolean":
571
576
  if (typeof value !== "boolean") return `expected boolean, got ${typeof value}`;
572
577
  return null;
578
+ case "boolean|auto":
579
+ if (typeof value !== "boolean" && value !== "auto") {
580
+ return `expected boolean or "auto", got ${typeof value}`;
581
+ }
582
+ return null;
573
583
  }
574
584
  }
575
585
  function validateSteps(steps) {