pi-browser-use 0.11.0 → 0.11.1

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.
Files changed (55) hide show
  1. package/dist/annotate.d.ts +0 -14
  2. package/dist/annotate.js +0 -14
  3. package/dist/artifacts.d.ts +0 -2
  4. package/dist/artifacts.js +0 -2
  5. package/dist/auth-verifiers.d.ts +0 -29
  6. package/dist/auth-verifiers.js +0 -31
  7. package/dist/chrome-launcher.d.ts +0 -59
  8. package/dist/chrome-launcher.js +0 -54
  9. package/dist/client.d.ts +0 -4
  10. package/dist/client.js +0 -17
  11. package/dist/config.d.ts +0 -25
  12. package/dist/config.js +0 -23
  13. package/dist/doctor.d.ts +0 -8
  14. package/dist/doctor.js +0 -5
  15. package/dist/existing-flow.d.ts +0 -31
  16. package/dist/existing-flow.js +0 -29
  17. package/dist/focus-policy.d.ts +0 -13
  18. package/dist/focus-policy.js +0 -13
  19. package/dist/index.d.ts +0 -1
  20. package/dist/index.js +0 -1
  21. package/dist/mcp-server.d.ts +0 -2
  22. package/dist/mcp-server.js +0 -10
  23. package/dist/named-profile.d.ts +0 -36
  24. package/dist/named-profile.js +0 -37
  25. package/dist/persistent-backend.d.ts +2 -56
  26. package/dist/persistent-backend.js +30 -65
  27. package/dist/persistent-store.d.ts +0 -24
  28. package/dist/persistent-store.js +0 -24
  29. package/dist/profile-lock.d.ts +0 -21
  30. package/dist/profile-lock.js +0 -27
  31. package/dist/profile.d.ts +0 -10
  32. package/dist/profile.js +0 -14
  33. package/dist/runtime.d.ts +0 -3
  34. package/dist/runtime.js +3 -92
  35. package/dist/session-manager.d.ts +0 -48
  36. package/dist/session-manager.js +0 -46
  37. package/dist/session.d.ts +0 -42
  38. package/dist/session.js +0 -18
  39. package/dist/settings.d.ts +0 -5
  40. package/dist/settings.js +0 -5
  41. package/dist/setup-flow.d.ts +0 -41
  42. package/dist/setup-flow.js +0 -37
  43. package/dist/shared-backend.d.ts +0 -30
  44. package/dist/shared-backend.js +0 -31
  45. package/dist/tab-bridge.d.ts +0 -27
  46. package/dist/tab-bridge.js +0 -27
  47. package/dist/tool-augment.d.ts +0 -9
  48. package/dist/tool-augment.js +0 -14
  49. package/dist/vision.d.ts +0 -17
  50. package/dist/vision.js +0 -17
  51. package/docs/performance.md +2 -0
  52. package/extension/README.md +2 -0
  53. package/extension/background.js +4 -9
  54. package/package.json +1 -1
  55. package/plugin.json +1 -1
@@ -1,12 +1,3 @@
1
- /**
2
- * Numbered-badge annotation for screenshots (adapted from the technique
3
- * popularized by vercel-labs/agent-browser).
4
- *
5
- * Badges are injected around visible interactive elements, screenshotted,
6
- * then removed. The returned coordinate map pairs each badge number with a
7
- * viewport center point for coordinate click tools, plus a short label so
8
- * vision models can resolve "click the blue submit button" to (x, y).
9
- */
10
1
  export interface AnnotatedElement {
11
2
  n: number;
12
3
  x: number;
@@ -17,11 +8,6 @@ export interface AnnotatedElement {
17
8
  export declare const ANNOTATE_MARKER = "data-pi-annotate";
18
9
  export declare const INJECT_ANNOTATIONS = "() => {\n document.querySelectorAll('[data-pi-annotate]').forEach((el) => el.remove());\n const els = [...document.querySelectorAll('a, button, input, select, textarea, [role=\"button\"], [role=\"link\"], [role=\"checkbox\"], [role=\"switch\"]')].filter((el) => {\n const r = el.getBoundingClientRect();\n return r.width > 0 && r.height > 0 && r.top >= 0 && r.left >= 0 && r.top < window.innerHeight && r.left < window.innerWidth;\n }).slice(0, 50);\n return els.map((el, i) => {\n const r = el.getBoundingClientRect();\n const badge = document.createElement('div');\n badge.setAttribute('data-pi-annotate', '1');\n badge.textContent = String(i + 1);\n badge.style.cssText = 'position:fixed;left:' + r.left + 'px;top:' + r.top + 'px;z-index:2147483647;background:#7c3aed;color:#fff;font:12px/1.4 monospace;padding:1px 5px;border-radius:3px;pointer-events:none;';\n document.body.appendChild(badge);\n const label = el.innerText || el.value || el.getAttribute('aria-label') || el.getAttribute('placeholder') || '';\n return { n: i + 1, x: Math.round(r.left + r.width / 2), y: Math.round(r.top + r.height / 2), tag: el.tagName.toLowerCase(), text: String(label).replace(/\\s+/g, ' ').slice(0, 60) };\n });\n}";
19
10
  export declare const CLEANUP_ANNOTATIONS = "() => {\n const badges = document.querySelectorAll('[data-pi-annotate]');\n const count = badges.length;\n badges.forEach((el) => el.remove());\n return count;\n}";
20
- /**
21
- * Upstream evaluate_script wraps results as prose around a ```json fence.
22
- * Extract the payload array robustly.
23
- */
24
11
  export declare function parseAnnotatedElements(text: string): AnnotatedElement[];
25
- /** Render the coordinate map as compact text for tool results. */
26
12
  export declare function formatAnnotatedMap(elements: AnnotatedElement[]): string;
27
13
  //# sourceMappingURL=annotate.d.ts.map
package/dist/annotate.js CHANGED
@@ -1,12 +1,3 @@
1
- /**
2
- * Numbered-badge annotation for screenshots (adapted from the technique
3
- * popularized by vercel-labs/agent-browser).
4
- *
5
- * Badges are injected around visible interactive elements, screenshotted,
6
- * then removed. The returned coordinate map pairs each badge number with a
7
- * viewport center point for coordinate click tools, plus a short label so
8
- * vision models can resolve "click the blue submit button" to (x, y).
9
- */
10
1
  export const ANNOTATE_MARKER = 'data-pi-annotate';
11
2
  export const INJECT_ANNOTATIONS = `() => {
12
3
  document.querySelectorAll('[${ANNOTATE_MARKER}]').forEach((el) => el.remove());
@@ -31,10 +22,6 @@ export const CLEANUP_ANNOTATIONS = `() => {
31
22
  badges.forEach((el) => el.remove());
32
23
  return count;
33
24
  }`;
34
- /**
35
- * Upstream evaluate_script wraps results as prose around a ```json fence.
36
- * Extract the payload array robustly.
37
- */
38
25
  export function parseAnnotatedElements(text) {
39
26
  const start = text.indexOf('[');
40
27
  const end = text.lastIndexOf(']');
@@ -62,7 +49,6 @@ export function parseAnnotatedElements(text) {
62
49
  return [];
63
50
  }
64
51
  }
65
- /** Render the coordinate map as compact text for tool results. */
66
52
  export function formatAnnotatedMap(elements) {
67
53
  if (elements.length === 0)
68
54
  return 'No interactive elements annotated.';
@@ -1,8 +1,6 @@
1
1
  export type ArtifactKind = 'screenshot' | 'html';
2
2
  export declare function defaultArtifactDir(): string;
3
- /** Resolve the destination file: explicit path wins, otherwise a timestamped file in the default dir. */
4
3
  export declare function resolveArtifactTarget(kind: ArtifactKind, path?: unknown, directory?: string): string;
5
- /** Pick the first image payload out of MCP content, if any. */
6
4
  export declare function pickImageData(content: unknown): {
7
5
  data: string;
8
6
  mimeType: string;
package/dist/artifacts.js CHANGED
@@ -3,13 +3,11 @@ import { join } from 'node:path';
3
3
  export function defaultArtifactDir() {
4
4
  return join(homedir(), '.pi', 'browser-artifacts');
5
5
  }
6
- /** Resolve the destination file: explicit path wins, otherwise a timestamped file in the default dir. */
7
6
  export function resolveArtifactTarget(kind, path, directory = defaultArtifactDir()) {
8
7
  if (typeof path === 'string' && path.length > 0)
9
8
  return path;
10
9
  return join(directory, `page-${Date.now()}.${kind === 'html' ? 'html' : 'png'}`);
11
10
  }
12
- /** Pick the first image payload out of MCP content, if any. */
13
11
  export function pickImageData(content) {
14
12
  if (!Array.isArray(content))
15
13
  return undefined;
@@ -1,26 +1,7 @@
1
- /**
2
- * Skill-level site authentication verifiers (spec section 6).
3
- *
4
- * There is intentionally no generic `isAuthenticated()` heuristic: each site
5
- * presents different authenticated and unauthenticated states, so detection
6
- * lives here per site. The browser subsystem only handles the resulting
7
- * BrowserAuthRequired by switching Persistent into reauthentication.
8
- *
9
- * A verifier declares:
10
- * - destination URL (where to check),
11
- * - authenticated-state check,
12
- * - login/challenge-state check.
13
- * `ensureSiteAuthenticated` runs them against snapshot text and throws
14
- * BrowserAuthRequired when a login/challenge is detected.
15
- */
16
1
  export interface SiteAuthVerifier {
17
- /** Stable provider id used in BrowserAuthRequired + status UX. */
18
2
  provider: string;
19
- /** Page to open for the auth check. */
20
3
  destinationUrl: string;
21
- /** True when the snapshot shows the authenticated state. */
22
4
  isAuthenticated: (snapshotText: string, url: string) => boolean;
23
- /** True when the snapshot shows a login/challenge wall. */
24
5
  isLoginOrChallenge: (snapshotText: string, url: string) => boolean;
25
6
  }
26
7
  export interface AuthCheckPage {
@@ -36,14 +17,9 @@ export type AuthCheckResult = {
36
17
  } | {
37
18
  status: 'unknown';
38
19
  };
39
- /** Classify one snapshot without side effects. */
40
20
  export declare function checkSiteAuth(verifier: SiteAuthVerifier, page: AuthCheckPage): AuthCheckResult;
41
- /** Gmail: inbox DOM vs Google login/challenge (never conflate Chrome-profile
42
- * sign-in with a mail.google.com session — they are different states). */
43
21
  export declare const gmailVerifier: SiteAuthVerifier;
44
- /** GitHub: settings/profile page shows the username when signed in. */
45
22
  export declare const githubVerifier: SiteAuthVerifier;
46
- /** Registry of built-in verifiers, keyed by provider id. */
47
23
  export declare const BUILTIN_VERIFIERS: Record<string, SiteAuthVerifier>;
48
24
  export interface AuthBrowser {
49
25
  openPage(url: string): Promise<{
@@ -51,10 +27,5 @@ export interface AuthBrowser {
51
27
  snapshotText: string;
52
28
  }>;
53
29
  }
54
- /**
55
- * Open the verifier destination, classify, and either return or throw
56
- * BrowserAuthRequired. `unknown` is returned (not thrown) so callers can
57
- * decide — an unrecognized page is not proof of a login wall.
58
- */
59
30
  export declare function ensureSiteAuthenticated(browser: AuthBrowser, verifier: SiteAuthVerifier): Promise<AuthCheckResult>;
60
31
  //# sourceMappingURL=auth-verifiers.d.ts.map
@@ -1,20 +1,4 @@
1
- /**
2
- * Skill-level site authentication verifiers (spec section 6).
3
- *
4
- * There is intentionally no generic `isAuthenticated()` heuristic: each site
5
- * presents different authenticated and unauthenticated states, so detection
6
- * lives here per site. The browser subsystem only handles the resulting
7
- * BrowserAuthRequired by switching Persistent into reauthentication.
8
- *
9
- * A verifier declares:
10
- * - destination URL (where to check),
11
- * - authenticated-state check,
12
- * - login/challenge-state check.
13
- * `ensureSiteAuthenticated` runs them against snapshot text and throws
14
- * BrowserAuthRequired when a login/challenge is detected.
15
- */
16
1
  import { BrowserAuthRequired } from './session.js';
17
- /** Classify one snapshot without side effects. */
18
2
  export function checkSiteAuth(verifier, page) {
19
3
  if (verifier.isAuthenticated(page.snapshotText, page.url))
20
4
  return { status: 'authenticated' };
@@ -26,13 +10,7 @@ export function checkSiteAuth(verifier, page) {
26
10
  const GOOGLE_LOGIN_URL = /accounts\.google\.com\/(v\d+\/)?(signin|ServiceLogin|challenge|password|otp|verification)/i;
27
11
  const GOOGLE_LOGIN_QUERY = /[?&](flowEntry=ServiceLogin|flowName=WebLiteSignIn|service=mail)/i;
28
12
  const GOOGLE_CHALLENGE_COPY = /verify it'?s you|2-step verification|two-factor|enter your password|choose an account to continue|sign in to continue to gmail|to continue to gmail/i;
29
- /** Raw-DOM login redirect: Gmail serves a JS hop to accounts.google.com. */
30
13
  const GMAIL_LOGIN_BASE = /<base\s+href="https:\/\/accounts\.google\.com/i;
31
- /**
32
- * Inbox markers. Deliberately excludes bare "primary": Google login pages
33
- * embed it in CSS custom properties (--gm3-sys-color-primary), which caused
34
- * a false authenticated verdict on real sign-in HTML (live-tested 2026-09).
35
- */
36
14
  const GMAIL_INBOX_MARKERS = [
37
15
  /inbox/i,
38
16
  /compose/i,
@@ -41,8 +19,6 @@ const GMAIL_INBOX_MARKERS = [
41
19
  /sent mail/i,
42
20
  /\bdrafts\b/i,
43
21
  ];
44
- /** Gmail: inbox DOM vs Google login/challenge (never conflate Chrome-profile
45
- * sign-in with a mail.google.com session — they are different states). */
46
22
  export const gmailVerifier = {
47
23
  provider: 'google',
48
24
  destinationUrl: 'https://mail.google.com/',
@@ -61,7 +37,6 @@ export const gmailVerifier = {
61
37
  GOOGLE_CHALLENGE_COPY.test(text),
62
38
  };
63
39
  const GITHUB_LOGIN_URL = /(^|\/)((login|session|auth)(\/|$|[?#]))/i;
64
- /** GitHub: settings/profile page shows the username when signed in. */
65
40
  export const githubVerifier = {
66
41
  provider: 'github',
67
42
  destinationUrl: 'https://github.com/settings/profile',
@@ -71,16 +46,10 @@ export const githubVerifier = {
71
46
  GITHUB_LOGIN_URL.test(url) ||
72
47
  (/sign in to github/i.test(text) && /username or email|password/i.test(text)),
73
48
  };
74
- /** Registry of built-in verifiers, keyed by provider id. */
75
49
  export const BUILTIN_VERIFIERS = {
76
50
  google: gmailVerifier,
77
51
  github: githubVerifier,
78
52
  };
79
- /**
80
- * Open the verifier destination, classify, and either return or throw
81
- * BrowserAuthRequired. `unknown` is returned (not thrown) so callers can
82
- * decide — an unrecognized page is not proof of a login wall.
83
- */
84
53
  export async function ensureSiteAuthenticated(browser, verifier) {
85
54
  const page = await browser.openPage(verifier.destinationUrl);
86
55
  const result = checkSiteAuth(verifier, page);
@@ -1,32 +1,10 @@
1
- /**
2
- * Pi-owned Chrome process management (spec sections 3 and 4).
3
- *
4
- * Normal automation launches Chrome directly with the Pi profile and an
5
- * ephemeral loopback remote-debugging port, then MCP attaches via
6
- * `--browser-url`. This keeps one Chrome process per profile and avoids the
7
- * default WebDriver launch path where authentication often breaks:
8
- *
9
- * ```text
10
- * Google Chrome --user-data-dir="<pi-profile>"
11
- * --remote-debugging-port=<ephemeral> [--headless]
12
- * ↕
13
- * chrome-devtools-mcp --browser-url=http://127.0.0.1:<port>
14
- * ```
15
- *
16
- * Bootstrap (first-run auth) launches headed Chrome *without* MCP/Puppeteer
17
- * so it looks like an ordinary manually launched browser.
18
- */
19
1
  export interface ChromeLaunchOptions {
20
2
  userDataDir: string;
21
- /** Named profile directory inside userDataDir (e.g. pi-browser-use). */
22
3
  profileDirectory?: string;
23
- /** Ephemeral loopback port. Allocated automatically when omitted. */
24
4
  port?: number;
25
5
  headless?: boolean;
26
- /** Extra Chrome flags appended after the managed ones. */
27
6
  chromeArgs?: string[];
28
7
  executablePath?: string;
29
- /** How long to wait for the DevTools endpoint. Default 15s. */
30
8
  readyTimeoutMs?: number;
31
9
  signal?: AbortSignal;
32
10
  }
@@ -35,29 +13,14 @@ export interface ChromeProcess {
35
13
  readonly port: number;
36
14
  readonly browserUrl: string;
37
15
  readonly userDataDir: string;
38
- /** True once the process has exited. */
39
16
  readonly exited: boolean;
40
- /** Resolves when the process exits (bootstrap uses this: close → READY). */
41
17
  waitForExit(): Promise<number | null>;
42
- /** SIGTERM, then SIGKILL after `graceMs` if still alive. */
43
18
  shutdown(graceMs?: number): Promise<void>;
44
19
  }
45
- /** Chrome/Chromium executable candidates by platform (stable first). */
46
20
  export declare function chromeExecutableCandidates(): string[];
47
- /** Resolve the Chrome executable: explicit path wins, else first candidate. */
48
21
  export declare function findChromeExecutable(executablePath?: string): string;
49
- /**
50
- * Parse `ps -eo pid,command` output for Pi-managed Chromes on a profile
51
- * root: processes carrying our user-data-dir AND the named-profile marker.
52
- * Pure (testable): the ps text is injected.
53
- */
54
22
  export declare function findManagedChromePids(psOutput: string, userDataDir: string, keepPid?: number): number[];
55
- /** Allocate a free loopback port (never hardcode 9222). */
56
23
  export declare function allocateEphemeralPort(): Promise<number>;
57
- /**
58
- * Managed Chrome flags. Headed bootstrap omits `--headless` and debugging
59
- * entirely when `debugPort` is 0 (plain manual browser, spec section 3).
60
- */
61
24
  export declare function buildChromeArgs(options: {
62
25
  userDataDir: string;
63
26
  profileDirectory?: string;
@@ -65,7 +28,6 @@ export declare function buildChromeArgs(options: {
65
28
  headless?: boolean;
66
29
  chromeArgs?: string[];
67
30
  }): string[];
68
- /** Poll the DevTools `/json/version` endpoint until it answers or times out. */
69
31
  export declare function waitForDevToolsEndpoint(port: number, options?: {
70
32
  timeoutMs?: number;
71
33
  fetchImpl?: typeof fetch;
@@ -74,31 +36,10 @@ export declare function waitForDevToolsEndpoint(port: number, options?: {
74
36
  browserUrl: string;
75
37
  webSocketDebuggerUrl: string;
76
38
  }>;
77
- /**
78
- * Launch Pi-owned Chrome. The caller must hold the profile lock
79
- * (see `profile-lock.ts`) for `userDataDir` before calling.
80
- */
81
39
  export declare function launchChrome(options: ChromeLaunchOptions): Promise<ChromeProcess>;
82
- /**
83
- * AppleScript that raises the exact Chrome process by pid. Unlike app-level
84
- * `activate` (which may front the user's daily windows instead), this targets
85
- * Pi-owned Chrome only. Foreground is reserved for explicit user-requested
86
- * views and auth handoffs — never automation.
87
- */
88
40
  export declare function buildFrontProcessScript(pid: number): string;
89
- /** Best-effort fronting of Pi-owned Chrome (macOS only). Returns success. */
90
41
  export declare function frontProcessByPid(pid: number | undefined, runner?: (cmd: string, args: string[]) => void): boolean;
91
- /**
92
- * AppleScript that raises the Chrome window holding the marker URL.
93
- * Pure (testable): execution lives with the caller. Foreground is reserved
94
- * for explicit user-requested views and auth handoffs — never automation.
95
- */
96
42
  export declare function buildFocusWindowScript(markerUrl: string): string;
97
- /**
98
- * Launch the headed first-run/setup browser (spec section 3): same Pi
99
- * profile, no MCP, no Puppeteer, no remote debugging — an ordinary manually
100
- * launched Chrome. Resolves when the user closes the window.
101
- */
102
43
  export declare function launchSetupBrowser(options: {
103
44
  userDataDir: string;
104
45
  profileDirectory?: string;
@@ -1,26 +1,7 @@
1
- /**
2
- * Pi-owned Chrome process management (spec sections 3 and 4).
3
- *
4
- * Normal automation launches Chrome directly with the Pi profile and an
5
- * ephemeral loopback remote-debugging port, then MCP attaches via
6
- * `--browser-url`. This keeps one Chrome process per profile and avoids the
7
- * default WebDriver launch path where authentication often breaks:
8
- *
9
- * ```text
10
- * Google Chrome --user-data-dir="<pi-profile>"
11
- * --remote-debugging-port=<ephemeral> [--headless]
12
- * ↕
13
- * chrome-devtools-mcp --browser-url=http://127.0.0.1:<port>
14
- * ```
15
- *
16
- * Bootstrap (first-run auth) launches headed Chrome *without* MCP/Puppeteer
17
- * so it looks like an ordinary manually launched browser.
18
- */
19
1
  import { execFileSync, spawn } from 'node:child_process';
20
2
  import { existsSync } from 'node:fs';
21
3
  import { createServer } from 'node:net';
22
4
  import { setTimeout as delay } from 'node:timers/promises';
23
- /** Chrome/Chromium executable candidates by platform (stable first). */
24
5
  export function chromeExecutableCandidates() {
25
6
  if (process.platform === 'darwin') {
26
7
  return [
@@ -44,7 +25,6 @@ export function chromeExecutableCandidates() {
44
25
  '/snap/bin/chromium',
45
26
  ];
46
27
  }
47
- /** Resolve the Chrome executable: explicit path wins, else first candidate. */
48
28
  export function findChromeExecutable(executablePath) {
49
29
  if (executablePath && executablePath.length > 0) {
50
30
  if (!existsSync(executablePath)) {
@@ -61,11 +41,6 @@ export function findChromeExecutable(executablePath) {
61
41
  }
62
42
  throw new Error('Chrome executable was not found. Install Google Chrome Stable or set executablePath.');
63
43
  }
64
- /**
65
- * Parse `ps -eo pid,command` output for Pi-managed Chromes on a profile
66
- * root: processes carrying our user-data-dir AND the named-profile marker.
67
- * Pure (testable): the ps text is injected.
68
- */
69
44
  export function findManagedChromePids(psOutput, userDataDir, keepPid) {
70
45
  const pids = [];
71
46
  for (const line of psOutput.split('\n')) {
@@ -86,7 +61,6 @@ export function findManagedChromePids(psOutput, userDataDir, keepPid) {
86
61
  }
87
62
  return pids;
88
63
  }
89
- /** Allocate a free loopback port (never hardcode 9222). */
90
64
  export function allocateEphemeralPort() {
91
65
  return new Promise((resolve, reject) => {
92
66
  const server = createServer();
@@ -103,10 +77,6 @@ export function allocateEphemeralPort() {
103
77
  });
104
78
  });
105
79
  }
106
- /**
107
- * Managed Chrome flags. Headed bootstrap omits `--headless` and debugging
108
- * entirely when `debugPort` is 0 (plain manual browser, spec section 3).
109
- */
110
80
  export function buildChromeArgs(options) {
111
81
  const args = [
112
82
  `--user-data-dir=${options.userDataDir}`,
@@ -124,7 +94,6 @@ export function buildChromeArgs(options) {
124
94
  args.push(...(options.chromeArgs ?? []));
125
95
  return args;
126
96
  }
127
- /** Poll the DevTools `/json/version` endpoint until it answers or times out. */
128
97
  export async function waitForDevToolsEndpoint(port, options) {
129
98
  const timeoutMs = options?.timeoutMs ?? 15_000;
130
99
  const fetchImpl = options?.fetchImpl ?? fetch;
@@ -199,10 +168,6 @@ class OwnedChromeProcess {
199
168
  }
200
169
  }
201
170
  }
202
- /**
203
- * Launch Pi-owned Chrome. The caller must hold the profile lock
204
- * (see `profile-lock.ts`) for `userDataDir` before calling.
205
- */
206
171
  export async function launchChrome(options) {
207
172
  options.signal?.throwIfAborted();
208
173
  const executable = findChromeExecutable(options.executablePath);
@@ -217,7 +182,6 @@ export async function launchChrome(options) {
217
182
  const child = spawn(executable, args, { stdio: 'ignore', detached: false });
218
183
  await new Promise((resolve, reject) => {
219
184
  child.on('error', reject);
220
- // Give spawn a tick to surface ENOENT-style failures before probing.
221
185
  setTimeout(resolve, 50);
222
186
  });
223
187
  if (child.exitCode !== null) {
@@ -234,18 +198,11 @@ export async function launchChrome(options) {
234
198
  child.kill('SIGKILL');
235
199
  }
236
200
  catch {
237
- // Already gone; the endpoint error below is what matters.
238
201
  }
239
202
  throw error;
240
203
  }
241
204
  return new OwnedChromeProcess(child, port, `http://127.0.0.1:${port}`, options.userDataDir);
242
205
  }
243
- /**
244
- * AppleScript that raises the exact Chrome process by pid. Unlike app-level
245
- * `activate` (which may front the user's daily windows instead), this targets
246
- * Pi-owned Chrome only. Foreground is reserved for explicit user-requested
247
- * views and auth handoffs — never automation.
248
- */
249
206
  export function buildFrontProcessScript(pid) {
250
207
  return [
251
208
  'tell application "System Events"',
@@ -253,7 +210,6 @@ export function buildFrontProcessScript(pid) {
253
210
  'end tell',
254
211
  ].join('\n');
255
212
  }
256
- /** Best-effort fronting of Pi-owned Chrome (macOS only). Returns success. */
257
213
  export function frontProcessByPid(pid, runner) {
258
214
  if (pid === undefined || process.platform !== 'darwin')
259
215
  return false;
@@ -266,11 +222,6 @@ export function frontProcessByPid(pid, runner) {
266
222
  return false;
267
223
  }
268
224
  }
269
- /**
270
- * AppleScript that raises the Chrome window holding the marker URL.
271
- * Pure (testable): execution lives with the caller. Foreground is reserved
272
- * for explicit user-requested views and auth handoffs — never automation.
273
- */
274
225
  export function buildFocusWindowScript(markerUrl) {
275
226
  const needle = markerUrl.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
276
227
  return [
@@ -287,11 +238,6 @@ export function buildFocusWindowScript(markerUrl) {
287
238
  'end tell',
288
239
  ].join('\n');
289
240
  }
290
- /**
291
- * Launch the headed first-run/setup browser (spec section 3): same Pi
292
- * profile, no MCP, no Puppeteer, no remote debugging — an ordinary manually
293
- * launched Chrome. Resolves when the user closes the window.
294
- */
295
241
  export async function launchSetupBrowser(options) {
296
242
  options.signal?.throwIfAborted();
297
243
  const executable = findChromeExecutable(options.executablePath);
package/dist/client.d.ts CHANGED
@@ -1,8 +1,4 @@
1
1
  import { type BrowserUseConfig } from './config.js';
2
- /**
3
- * MCP client that spawns chrome-devtools-mcp as a per-session subprocess over
4
- * stdio. Nothing runs persistently: connect() starts it, close() kills it.
5
- */
6
2
  export declare class DevToolsClient {
7
3
  private client;
8
4
  private config;
package/dist/client.js CHANGED
@@ -8,7 +8,6 @@ const MCP_STDERR_LIMIT = 4_096;
8
8
  const MCP_SYSTEM_ERROR_CODE_PATTERN = /^(?:EACCES|EADDRINUSE|ECONNREFUSED|ECONNRESET|EHOSTUNREACH|ENOENT|ENOTEMPTY|ENOTFOUND|EPERM|ETIMEDOUT)$/;
9
9
  const require = createRequire(import.meta.url);
10
10
  const chromeDevToolsMcpPackagePath = require.resolve('chrome-devtools-mcp/package.json');
11
- // oxlint-disable-next-line no-unsafe-read -- package.json of a pinned dependency
12
11
  const chromeDevToolsMcpPackage = require(chromeDevToolsMcpPackagePath);
13
12
  const chromeDevToolsMcpBin = chromeDevToolsMcpPackage.bin?.['chrome-devtools-mcp'];
14
13
  if (!chromeDevToolsMcpBin) {
@@ -40,10 +39,6 @@ function summarizeFailure(stderr, errorName, errorCode) {
40
39
  const safeName = /^[A-Za-z][A-Za-z0-9]{0,63}$/.test(errorName) ? errorName : 'UnknownError';
41
40
  return `MCP transport failed (${safeName}).`;
42
41
  }
43
- /**
44
- * MCP client that spawns chrome-devtools-mcp as a per-session subprocess over
45
- * stdio. Nothing runs persistently: connect() starts it, close() kills it.
46
- */
47
42
  export class DevToolsClient {
48
43
  client = null;
49
44
  config;
@@ -72,8 +67,6 @@ export class DevToolsClient {
72
67
  }
73
68
  async openConnection(signal) {
74
69
  this.state = this.hasConnected ? 'reconnecting' : 'connecting';
75
- // Keep the MCP SDK off the extension's eager module-evaluation path and
76
- // pay its cost only when session initialization opens a connection.
77
70
  const { Client, StdioClientTransport } = await loadMcpRuntime();
78
71
  const args = configToArgs(this.config);
79
72
  const generation = ++this.generation;
@@ -89,8 +82,6 @@ export class DevToolsClient {
89
82
  });
90
83
  const client = new Client({ name: 'pi-browser-use', version: '0.1.0' }, { capabilities: {} });
91
84
  this.client = client;
92
- // MCP transport implements callback properties, not EventTarget.
93
- // oxlint-disable-next-line unicorn/prefer-add-event-listener
94
85
  transport.onerror = (error) => {
95
86
  if (generation !== this.generation)
96
87
  return;
@@ -98,7 +89,6 @@ export class DevToolsClient {
98
89
  console.error(`[pi-browser-use] chrome-devtools-mcp transport error (${transportErrorCode ?? error.name})`);
99
90
  void this.disconnectUnhealthyClient(generation);
100
91
  };
101
- // oxlint-disable-next-line unicorn/prefer-add-event-listener
102
92
  transport.onclose = () => this.markDisconnected(generation);
103
93
  try {
104
94
  await client.connect(transport, signal ? { signal, timeout: MCP_TIMEOUT_MS } : { timeout: MCP_TIMEOUT_MS, signal });
@@ -118,7 +108,6 @@ export class DevToolsClient {
118
108
  await client.close();
119
109
  }
120
110
  catch {
121
- // The failed transport may already be closed.
122
111
  }
123
112
  if (signal?.aborted)
124
113
  throw error;
@@ -128,8 +117,6 @@ export class DevToolsClient {
128
117
  : undefined;
129
118
  const diagnostic = summarizeFailure(stderr, errorName, transportErrorCode ?? errorCode);
130
119
  console.error(`[pi-browser-use] browser connection failed: ${diagnostic}`);
131
- // Raw upstream causes can contain credentials; retain only the sanitized diagnostic.
132
- // oxlint-disable-next-line preserve-caught-error
133
120
  throw new Error(`Browser connection failed. ${diagnostic}`);
134
121
  }
135
122
  }
@@ -209,17 +196,13 @@ export class DevToolsClient {
209
196
  if (signal?.aborted)
210
197
  throw error;
211
198
  if (this.state !== 'ready' || this.client !== client) {
212
- // oxlint-disable-next-line preserve-caught-error -- upstream causes may contain secrets
213
199
  throw new Error('Browser connection lost; retry the tool.');
214
200
  }
215
201
  const errorName = error instanceof Error ? error.name : 'UnknownError';
216
202
  console.error(`[pi-browser-use] upstream tool call failed (${errorName})`);
217
- // Existing mode fails most often on the consent gate: say so plainly.
218
203
  if (this.config.sessionMode === 'existing') {
219
- // oxlint-disable-next-line preserve-caught-error -- upstream causes may contain secrets
220
204
  throw new Error('Browser tool call failed. If Chrome is showing an "Allow remote debugging?" prompt, click Allow and retry.');
221
205
  }
222
- // oxlint-disable-next-line preserve-caught-error -- upstream causes may contain secrets
223
206
  throw new Error('Browser tool call failed.');
224
207
  }
225
208
  }
package/dist/config.d.ts CHANGED
@@ -1,24 +1,12 @@
1
1
  export declare const DEFAULT_PROFILE_DIR: string;
2
2
  export type BrowserSessionMode = 'persistent' | 'isolated' | 'existing';
3
- /** Vision model used by the optional analyze_screenshot tool. Must already exist in Pi's model registry. */
4
3
  export interface VisionModelConfig {
5
4
  provider: string;
6
5
  model: string;
7
6
  }
8
- /** Simple mode selector. Takes precedence over sessionMode/headless when set. */
9
7
  export type BrowserModeOption = 'fresh' | 'persistent' | 'existing';
10
- /** Configuration for the chrome-devtools-mcp subprocess. Same settings key as before: "pi-browser-use". */
11
8
  export interface BrowserUseConfig {
12
- /**
13
- * Simple facade: fresh (isolated clean room), persistent (saved profile
14
- * with your logins), existing (attach to your running Chrome). Overrides
15
- * sessionMode and headless/headed below when present.
16
- */
17
9
  mode?: BrowserModeOption;
18
- /**
19
- * Show the browser window. Default false (headless) for fresh and persistent;
20
- * existing is always headed. Overrides headless below when present.
21
- */
22
10
  headed?: boolean;
23
11
  sessionMode?: BrowserSessionMode;
24
12
  headless?: boolean;
@@ -47,26 +35,13 @@ export interface BrowserUseConfig {
47
35
  allowedUrlPattern?: string[];
48
36
  blockedUrlPattern?: string[];
49
37
  slim?: boolean;
50
- /** Loopback port for the Existing-mode tab-broker bridge. Default 31973; set 0 to disable the bridge. */
51
38
  tabBridgePort?: number;
52
- /** First-class Chrome flags, forwarded as --chrome-arg=<flag>. Only applies when Chrome is launched by chrome-devtools-mcp (not with autoConnect/browserUrl). */
53
39
  chromeArgs?: string[];
54
- /** Raw escape hatch: extra CLI flags forwarded verbatim to chrome-devtools-mcp. */
55
40
  extraArgs?: string[];
56
41
  }
57
- /** In-session backend target for browser_switch_mode. */
58
42
  export type BrowserMode = 'fresh' | 'persistent' | 'existing';
59
- /**
60
- * Build the config for a mode switch from the session base config.
61
- * fresh means an isolated clean room; persistent means the saved profile
62
- * (headless unless headed is requested, so logins work without popups).
63
- * Attach fields never carry across modes.
64
- */
65
43
  export declare function resolveModeTarget(base: BrowserUseConfig, mode: BrowserMode, headed?: boolean, defaultProfileDir?: string): BrowserUseConfig;
66
- /** Expand a leading ~/ in user-supplied paths (env interpolation covers ${} only). */
67
44
  export declare function expandHome(path: string): string;
68
- /** Merge user config over fresh-headless defaults. */
69
45
  export declare function resolveConfig(config?: BrowserUseConfig, defaultProfileDir?: string): BrowserUseConfig;
70
- /** Convert config into CLI flags for the chrome-devtools-mcp subprocess. */
71
46
  export declare function configToArgs(config: BrowserUseConfig): string[];
72
47
  //# sourceMappingURL=config.d.ts.map