@browser_use/pi 0.1.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/.env.example +6 -0
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/dist/agent.d.ts +15 -0
- package/dist/agent.js +381 -0
- package/dist/agent.js.map +1 -0
- package/dist/browser.d.ts +52 -0
- package/dist/browser.js +264 -0
- package/dist/browser.js.map +1 -0
- package/dist/cdp.d.ts +39 -0
- package/dist/cdp.js +291 -0
- package/dist/cdp.js.map +1 -0
- package/dist/context.d.ts +24 -0
- package/dist/context.js +143 -0
- package/dist/context.js.map +1 -0
- package/dist/control.d.ts +18 -0
- package/dist/control.js +84 -0
- package/dist/control.js.map +1 -0
- package/dist/events.d.ts +40 -0
- package/dist/events.js +79 -0
- package/dist/events.js.map +1 -0
- package/dist/highlight.d.ts +3 -0
- package/dist/highlight.js +102 -0
- package/dist/highlight.js.map +1 -0
- package/dist/history.d.ts +22 -0
- package/dist/history.js +126 -0
- package/dist/history.js.map +1 -0
- package/dist/images.d.ts +14 -0
- package/dist/images.js +104 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +422 -0
- package/dist/index.js.map +1 -0
- package/dist/model-stream.d.ts +3 -0
- package/dist/model-stream.js +78 -0
- package/dist/model-stream.js.map +1 -0
- package/dist/observer.d.ts +16 -0
- package/dist/observer.js +56 -0
- package/dist/observer.js.map +1 -0
- package/dist/page.d.ts +58 -0
- package/dist/page.js +189 -0
- package/dist/page.js.map +1 -0
- package/dist/policy.d.ts +18 -0
- package/dist/policy.js +195 -0
- package/dist/policy.js.map +1 -0
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.js +41 -0
- package/dist/prompt.js.map +1 -0
- package/dist/protocol.d.ts +70 -0
- package/dist/protocol.js +7 -0
- package/dist/protocol.js.map +1 -0
- package/dist/recording.d.ts +44 -0
- package/dist/recording.js +120 -0
- package/dist/recording.js.map +1 -0
- package/dist/research-tools.d.ts +3 -0
- package/dist/research-tools.js +67 -0
- package/dist/research-tools.js.map +1 -0
- package/dist/runtime.d.ts +39 -0
- package/dist/runtime.js +268 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +251 -0
- package/dist/server.js.map +1 -0
- package/dist/telemetry.d.ts +3 -0
- package/dist/telemetry.js +41 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/video.d.ts +18 -0
- package/dist/video.js +177 -0
- package/dist/video.js.map +1 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +329 -0
- package/dist/worker.js.map +1 -0
- package/examples/README.md +58 -0
- package/examples/apply-to-job.ts +57 -0
- package/examples/ehr.ts +53 -0
- package/examples/extract.ts +50 -0
- package/examples/form.ts +36 -0
- package/examples/onepassword.ts +66 -0
- package/examples/qa.ts +72 -0
- package/examples/research.ts +35 -0
- package/examples/stripe-link.ts +166 -0
- package/package.json +66 -0
package/dist/browser.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { spawn, execFile } from 'node:child_process';
|
|
2
|
+
import { access, mkdir, mkdtemp, open, readFile, realpath, rm, stat } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir, homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
6
|
+
/** Declarative browser choices. BrowserUse owns the resulting connection lifecycle. */
|
|
7
|
+
export const Browser = {
|
|
8
|
+
cloud: (options) => ({ ...options, kind: 'cloud' }),
|
|
9
|
+
chromium: (options = {}) => ({
|
|
10
|
+
...options,
|
|
11
|
+
kind: 'chromium',
|
|
12
|
+
}),
|
|
13
|
+
chrome: (options = {}) => ({ ...options, kind: 'chrome' }),
|
|
14
|
+
};
|
|
15
|
+
/** Same profile discovery convention as Browser Harness on macOS, Linux and Windows. */
|
|
16
|
+
export function chromeProfileDirs(platform = process.platform, home = homedir(), local = process.env.LOCALAPPDATA) {
|
|
17
|
+
if (platform === 'darwin')
|
|
18
|
+
return [join(home, 'Library/Application Support/Google/Chrome')];
|
|
19
|
+
if (platform === 'win32')
|
|
20
|
+
return [join(local || join(home, 'AppData/Local'), 'Google/Chrome/User Data')];
|
|
21
|
+
return [join(home, '.config/google-chrome'), join(home, '.config/chromium')];
|
|
22
|
+
}
|
|
23
|
+
async function discoverChrome(options) {
|
|
24
|
+
for (const profile of options.profileDir ? [options.profileDir] : chromeProfileDirs()) {
|
|
25
|
+
try {
|
|
26
|
+
const [port, path] = (await readFile(join(profile, 'DevToolsActivePort'), 'utf8'))
|
|
27
|
+
.trim()
|
|
28
|
+
.split('\n');
|
|
29
|
+
if (!port ||
|
|
30
|
+
!/^\d+$/.test(port) ||
|
|
31
|
+
+port < 1 ||
|
|
32
|
+
+port > 65535 ||
|
|
33
|
+
!path?.startsWith('/devtools/browser/'))
|
|
34
|
+
continue;
|
|
35
|
+
const response = await fetch(`http://127.0.0.1:${port}/json/version`, {
|
|
36
|
+
signal: AbortSignal.timeout(1500),
|
|
37
|
+
});
|
|
38
|
+
// Chrome 147 can disable HTTP discovery for its default profile.
|
|
39
|
+
if (response.status === 404)
|
|
40
|
+
return `ws://127.0.0.1:${port}${path}`;
|
|
41
|
+
if (!response.ok)
|
|
42
|
+
continue;
|
|
43
|
+
const info = (await response.json());
|
|
44
|
+
if (info.webSocketDebuggerUrl)
|
|
45
|
+
return info.webSocketDebuggerUrl;
|
|
46
|
+
}
|
|
47
|
+
catch { }
|
|
48
|
+
}
|
|
49
|
+
throw new Error('No running Chrome debugging endpoint found. Enable chrome://inspect/#remote-debugging and accept Chrome’s connection prompt, or pass Browser.chrome({ cdpUrl }). No browser was launched or profile copied.');
|
|
50
|
+
}
|
|
51
|
+
async function openCloud(options) {
|
|
52
|
+
if (typeof options.apiKey !== 'string' || !options.apiKey.trim())
|
|
53
|
+
throw new Error('Browser.cloud requires apiKey.');
|
|
54
|
+
const timeout = options.timeoutMinutes ?? 30;
|
|
55
|
+
if (!Number.isInteger(timeout) || timeout <= 0 || timeout > 240)
|
|
56
|
+
throw new Error('timeoutMinutes must be an integer from 1 to 240.');
|
|
57
|
+
const request = async (path, method, body) => {
|
|
58
|
+
const response = await fetch(`https://api.browser-use.com/api/v3${path}`, {
|
|
59
|
+
method,
|
|
60
|
+
headers: { 'X-Browser-Use-API-Key': options.apiKey, 'Content-Type': 'application/json' },
|
|
61
|
+
body: JSON.stringify(body),
|
|
62
|
+
signal: AbortSignal.timeout(30_000),
|
|
63
|
+
redirect: 'error',
|
|
64
|
+
});
|
|
65
|
+
if (!response.ok)
|
|
66
|
+
throw new Error(`Browser Use Cloud ${method} failed (${response.status}).`);
|
|
67
|
+
return response;
|
|
68
|
+
};
|
|
69
|
+
// Never retry provisioning: an ambiguous POST can already have created a billable browser.
|
|
70
|
+
const data = (await (await request('/browsers', 'POST', {
|
|
71
|
+
timeout,
|
|
72
|
+
enableRecording: false,
|
|
73
|
+
...(options.profileId ? { profileId: options.profileId } : {}),
|
|
74
|
+
...(options.proxyCountryCode ? { proxyCountryCode: options.proxyCountryCode } : {}),
|
|
75
|
+
})).json());
|
|
76
|
+
if (typeof data.id !== 'string' || !data.id)
|
|
77
|
+
throw new Error('Cloud response missing browser id; check the Cloud dashboard for an orphaned browser.');
|
|
78
|
+
let closing;
|
|
79
|
+
const close = () => (closing ??= request(`/browsers/${encodeURIComponent(data.id)}`, 'PATCH', { action: 'stop' })
|
|
80
|
+
.then(() => { })
|
|
81
|
+
.catch((error) => {
|
|
82
|
+
closing = undefined;
|
|
83
|
+
throw error;
|
|
84
|
+
}));
|
|
85
|
+
try {
|
|
86
|
+
if (typeof data.cdpUrl !== 'string' ||
|
|
87
|
+
!['ws:', 'wss:', 'http:', 'https:'].includes(new URL(data.cdpUrl).protocol))
|
|
88
|
+
throw new Error('Cloud response missing valid cdpUrl.');
|
|
89
|
+
return { endpoint: data.cdpUrl, close };
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
try {
|
|
93
|
+
await close();
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
throw new Error(`Invalid Cloud response and cleanup failed for browser ${data.id}; stop it in the Cloud dashboard.`);
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function executable(options) {
|
|
102
|
+
if (options.executablePath) {
|
|
103
|
+
await access(options.executablePath);
|
|
104
|
+
return options.executablePath;
|
|
105
|
+
}
|
|
106
|
+
const candidates = process.platform === 'darwin'
|
|
107
|
+
? [
|
|
108
|
+
options.channel === 'msedge'
|
|
109
|
+
? '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'
|
|
110
|
+
: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
111
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
112
|
+
]
|
|
113
|
+
: process.platform === 'win32'
|
|
114
|
+
? [
|
|
115
|
+
join(process.env.PROGRAMFILES ?? 'C:\\Program Files', options.channel === 'msedge'
|
|
116
|
+
? 'Microsoft/Edge/Application/msedge.exe'
|
|
117
|
+
: 'Google/Chrome/Application/chrome.exe'),
|
|
118
|
+
]
|
|
119
|
+
: options.channel === 'msedge'
|
|
120
|
+
? ['/usr/bin/microsoft-edge']
|
|
121
|
+
: ['/usr/bin/google-chrome', '/usr/bin/chromium', '/usr/bin/chromium-browser'];
|
|
122
|
+
for (const path of candidates) {
|
|
123
|
+
try {
|
|
124
|
+
await access(path);
|
|
125
|
+
return path;
|
|
126
|
+
}
|
|
127
|
+
catch { }
|
|
128
|
+
}
|
|
129
|
+
throw new Error('Chrome not found. Install Chrome or set browser.executablePath / browser.cdpUrl.');
|
|
130
|
+
}
|
|
131
|
+
/** Local Chrome has an isolated temporary profile; external Chrome always belongs to the caller. */
|
|
132
|
+
export async function openBrowser(options = {}) {
|
|
133
|
+
if (options.kind !== undefined && !['cloud', 'chrome', 'chromium'].includes(options.kind))
|
|
134
|
+
throw new Error('Unknown browser kind. Use Browser.cloud, Browser.chromium or Browser.chrome.');
|
|
135
|
+
if (options.kind === 'cloud')
|
|
136
|
+
return openCloud(options);
|
|
137
|
+
if (options.kind === 'chrome') {
|
|
138
|
+
if (options.approveConnection !== undefined && typeof options.approveConnection !== 'boolean')
|
|
139
|
+
throw new Error('approveConnection must be boolean.');
|
|
140
|
+
if (options.approveConnection && process.platform !== 'darwin')
|
|
141
|
+
throw new Error('approveConnection is supported only on macOS.');
|
|
142
|
+
const endpoint = options.cdpUrl ?? (await discoverChrome(options));
|
|
143
|
+
if (!['http:', 'https:', 'ws:', 'wss:'].includes(new URL(endpoint).protocol))
|
|
144
|
+
throw new Error('Invalid Chrome CDP endpoint.');
|
|
145
|
+
return { endpoint, close: async () => { } };
|
|
146
|
+
}
|
|
147
|
+
if ('cdpUrl' in options && options.cdpUrl) {
|
|
148
|
+
if (['headless', 'channel', 'executablePath', 'profileDir'].some((key) => key in options))
|
|
149
|
+
throw new Error('cdpUrl cannot be combined with local browser options.');
|
|
150
|
+
if (!['http:', 'https:', 'ws:', 'wss:'].includes(new URL(options.cdpUrl).protocol))
|
|
151
|
+
throw new Error('cdpUrl must be an HTTP(S) or WebSocket endpoint.');
|
|
152
|
+
return { endpoint: options.cdpUrl, close: async () => { } };
|
|
153
|
+
}
|
|
154
|
+
const path = await executable(options);
|
|
155
|
+
const persistent = !!options.profileDir;
|
|
156
|
+
if (options.profileDir)
|
|
157
|
+
await mkdir(options.profileDir, { recursive: true, mode: 0o700 });
|
|
158
|
+
const profile = options.profileDir
|
|
159
|
+
? await realpath(options.profileDir)
|
|
160
|
+
: await mkdtemp(join(tmpdir(), 'browser-use-'));
|
|
161
|
+
const lockPath = join(profile, '.bu-pi.lock');
|
|
162
|
+
const lock = await open(lockPath, 'wx', 0o600).catch(() => {
|
|
163
|
+
throw new Error(`Browser profile is locked: ${profile}. Close its owner. After a crash, verify Chrome and the SDK have exited before removing .bu-pi.lock.`);
|
|
164
|
+
});
|
|
165
|
+
await lock.writeFile(JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }));
|
|
166
|
+
const launchedAt = Date.now();
|
|
167
|
+
const child = spawn(path, [
|
|
168
|
+
`--user-data-dir=${profile}`,
|
|
169
|
+
'--remote-debugging-port=0',
|
|
170
|
+
'--remote-debugging-address=127.0.0.1',
|
|
171
|
+
'--no-first-run',
|
|
172
|
+
'--no-default-browser-check',
|
|
173
|
+
'--window-size=1440,900',
|
|
174
|
+
...(options.headless === false ? [] : ['--headless=new']),
|
|
175
|
+
'about:blank',
|
|
176
|
+
], { stdio: 'ignore' });
|
|
177
|
+
let launchError;
|
|
178
|
+
child.on('error', (error) => {
|
|
179
|
+
launchError = error;
|
|
180
|
+
});
|
|
181
|
+
let closing;
|
|
182
|
+
const close = () => (closing ??= (async () => {
|
|
183
|
+
if (child.exitCode === null && child.signalCode === null && child.pid) {
|
|
184
|
+
const exited = new Promise((resolve) => child.once('exit', () => resolve()));
|
|
185
|
+
child.kill('SIGTERM');
|
|
186
|
+
const timer = setTimeout(() => child.kill('SIGKILL'), 2000);
|
|
187
|
+
await exited;
|
|
188
|
+
clearTimeout(timer);
|
|
189
|
+
}
|
|
190
|
+
await lock.close();
|
|
191
|
+
await rm(lockPath, { force: true });
|
|
192
|
+
if (!persistent)
|
|
193
|
+
await rm(profile, { recursive: true, force: true });
|
|
194
|
+
})());
|
|
195
|
+
try {
|
|
196
|
+
const deadline = Date.now() + 15_000;
|
|
197
|
+
while (Date.now() < deadline) {
|
|
198
|
+
if (launchError)
|
|
199
|
+
throw launchError;
|
|
200
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
201
|
+
throw new Error('Chrome exited before exposing CDP.');
|
|
202
|
+
try {
|
|
203
|
+
if ((await stat(join(profile, 'DevToolsActivePort'))).mtimeMs < launchedAt - 1)
|
|
204
|
+
throw new Error('Waiting for a fresh DevTools endpoint.');
|
|
205
|
+
const [port, path] = (await readFile(join(profile, 'DevToolsActivePort'), 'utf8'))
|
|
206
|
+
.trim()
|
|
207
|
+
.split('\n');
|
|
208
|
+
if (port && path)
|
|
209
|
+
return { endpoint: `ws://127.0.0.1:${port}${path}`, close };
|
|
210
|
+
}
|
|
211
|
+
catch { }
|
|
212
|
+
await delay(50);
|
|
213
|
+
}
|
|
214
|
+
throw new Error('Chrome did not expose CDP within 15000 ms.');
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
await close();
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/** Narrow AX action from Browser Harness. Never enables debugging or grants Accessibility. */
|
|
222
|
+
export function approveChromeConnection(signal) {
|
|
223
|
+
const script = `using terms from application "System Events"
|
|
224
|
+
on clickAllow(nodeRef)
|
|
225
|
+
try
|
|
226
|
+
if (role of nodeRef as text) is "AXButton" and (description of nodeRef as text) is "Allow" then
|
|
227
|
+
perform action "AXPress" of nodeRef
|
|
228
|
+
return true
|
|
229
|
+
end if
|
|
230
|
+
end try
|
|
231
|
+
try
|
|
232
|
+
repeat with childRef in UI elements of nodeRef
|
|
233
|
+
if my clickAllow(childRef) then return true
|
|
234
|
+
end repeat
|
|
235
|
+
end try
|
|
236
|
+
return false
|
|
237
|
+
end clickAllow
|
|
238
|
+
end using terms from
|
|
239
|
+
tell application "System Events"
|
|
240
|
+
if exists process "Google Chrome" then
|
|
241
|
+
tell process "Google Chrome"
|
|
242
|
+
repeat with w in windows
|
|
243
|
+
try
|
|
244
|
+
repeat with s in sheets of w
|
|
245
|
+
if (name of s as text) is "Allow remote debugging?" then
|
|
246
|
+
if my clickAllow(s) then return "ready"
|
|
247
|
+
end if
|
|
248
|
+
end repeat
|
|
249
|
+
end try
|
|
250
|
+
end repeat
|
|
251
|
+
end tell
|
|
252
|
+
end if
|
|
253
|
+
end tell
|
|
254
|
+
return "not-found"`;
|
|
255
|
+
return new Promise((resolve, reject) => {
|
|
256
|
+
execFile('/usr/bin/osascript', ['-e', script], { timeout: 5000, signal }, (error, stdout) => {
|
|
257
|
+
if (error)
|
|
258
|
+
reject(new Error('Chrome approval needs macOS Accessibility permission for the app running Browser Use Pi. Accept Chrome’s prompt manually, or grant that permission.'));
|
|
259
|
+
else
|
|
260
|
+
resolve(stdout.trim());
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, execFile } from 'node:child_process';\nimport { access, mkdir, mkdtemp, open, readFile, realpath, rm, stat } from 'node:fs/promises';\nimport { tmpdir, homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { setTimeout as delay } from 'node:timers/promises';\n\nexport interface LocalBrowserOptions {\n headless?: boolean;\n channel?: 'chrome' | 'msedge';\n executablePath?: string;\n profileDir?: string;\n}\nexport interface CloudBrowserOptions {\n apiKey: string;\n profileId?: string;\n timeoutMinutes?: number;\n proxyCountryCode?: string;\n}\nexport interface ChromeBrowserOptions {\n cdpUrl?: string;\n /** User-data root containing DevToolsActivePort, not its Default subdirectory. */\n profileDir?: string;\n targetId?: string;\n /** macOS only: accept Chrome’s exact remote-debugging sheet while connecting. */\n approveConnection?: boolean;\n}\nexport type BrowserOptions =\n | ({ kind: 'cloud' } & CloudBrowserOptions)\n | ({ kind: 'chromium' } & LocalBrowserOptions)\n | ({ kind: 'chrome' } & ChromeBrowserOptions)\n | ({ kind?: never; cdpUrl: string; targetId?: string } & {\n [K in keyof LocalBrowserOptions]?: never;\n })\n | ({ kind?: never; cdpUrl?: never; targetId?: never } & LocalBrowserOptions);\n\n/** Declarative browser choices. BrowserUse owns the resulting connection lifecycle. */\nexport const Browser = {\n cloud: (options: CloudBrowserOptions): BrowserOptions => ({ ...options, kind: 'cloud' }),\n chromium: (options: LocalBrowserOptions = {}): BrowserOptions => ({\n ...options,\n kind: 'chromium',\n }),\n chrome: (options: ChromeBrowserOptions = {}): BrowserOptions => ({ ...options, kind: 'chrome' }),\n};\n\n/** Same profile discovery convention as Browser Harness on macOS, Linux and Windows. */\nexport function chromeProfileDirs(\n platform = process.platform,\n home = homedir(),\n local = process.env.LOCALAPPDATA,\n) {\n if (platform === 'darwin') return [join(home, 'Library/Application Support/Google/Chrome')];\n if (platform === 'win32')\n return [join(local || join(home, 'AppData/Local'), 'Google/Chrome/User Data')];\n return [join(home, '.config/google-chrome'), join(home, '.config/chromium')];\n}\nasync function discoverChrome(options: ChromeBrowserOptions) {\n for (const profile of options.profileDir ? [options.profileDir] : chromeProfileDirs()) {\n try {\n const [port, path] = (await readFile(join(profile, 'DevToolsActivePort'), 'utf8'))\n .trim()\n .split('\\n');\n if (\n !port ||\n !/^\\d+$/.test(port) ||\n +port < 1 ||\n +port > 65535 ||\n !path?.startsWith('/devtools/browser/')\n )\n continue;\n const response = await fetch(`http://127.0.0.1:${port}/json/version`, {\n signal: AbortSignal.timeout(1500),\n });\n // Chrome 147 can disable HTTP discovery for its default profile.\n if (response.status === 404) return `ws://127.0.0.1:${port}${path}`;\n if (!response.ok) continue;\n const info = (await response.json()) as { webSocketDebuggerUrl?: string };\n if (info.webSocketDebuggerUrl) return info.webSocketDebuggerUrl;\n } catch {}\n }\n throw new Error(\n 'No running Chrome debugging endpoint found. Enable chrome://inspect/#remote-debugging and accept Chrome’s connection prompt, or pass Browser.chrome({ cdpUrl }). No browser was launched or profile copied.',\n );\n}\n\nasync function openCloud(options: CloudBrowserOptions) {\n if (typeof options.apiKey !== 'string' || !options.apiKey.trim())\n throw new Error('Browser.cloud requires apiKey.');\n const timeout = options.timeoutMinutes ?? 30;\n if (!Number.isInteger(timeout) || timeout <= 0 || timeout > 240)\n throw new Error('timeoutMinutes must be an integer from 1 to 240.');\n const request = async (path: string, method: string, body: object) => {\n const response = await fetch(`https://api.browser-use.com/api/v3${path}`, {\n method,\n headers: { 'X-Browser-Use-API-Key': options.apiKey, 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(30_000),\n redirect: 'error',\n });\n if (!response.ok) throw new Error(`Browser Use Cloud ${method} failed (${response.status}).`);\n return response;\n };\n // Never retry provisioning: an ambiguous POST can already have created a billable browser.\n const data = (await (\n await request('/browsers', 'POST', {\n timeout,\n enableRecording: false,\n ...(options.profileId ? { profileId: options.profileId } : {}),\n ...(options.proxyCountryCode ? { proxyCountryCode: options.proxyCountryCode } : {}),\n })\n ).json()) as { id?: string; cdpUrl?: string; liveUrl?: string };\n if (typeof data.id !== 'string' || !data.id)\n throw new Error(\n 'Cloud response missing browser id; check the Cloud dashboard for an orphaned browser.',\n );\n let closing: Promise<void> | undefined;\n const close = () =>\n (closing ??= request(`/browsers/${encodeURIComponent(data.id!)}`, 'PATCH', { action: 'stop' })\n .then(() => {})\n .catch((error) => {\n closing = undefined;\n throw error;\n }));\n try {\n if (\n typeof data.cdpUrl !== 'string' ||\n !['ws:', 'wss:', 'http:', 'https:'].includes(new URL(data.cdpUrl).protocol)\n )\n throw new Error('Cloud response missing valid cdpUrl.');\n return { endpoint: data.cdpUrl, close };\n } catch (error) {\n try {\n await close();\n } catch {\n throw new Error(\n `Invalid Cloud response and cleanup failed for browser ${data.id}; stop it in the Cloud dashboard.`,\n );\n }\n throw error;\n }\n}\n\nasync function executable(options: LocalBrowserOptions) {\n if (options.executablePath) {\n await access(options.executablePath);\n return options.executablePath;\n }\n const candidates =\n process.platform === 'darwin'\n ? [\n options.channel === 'msedge'\n ? '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'\n : '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n '/Applications/Chromium.app/Contents/MacOS/Chromium',\n ]\n : process.platform === 'win32'\n ? [\n join(\n process.env.PROGRAMFILES ?? 'C:\\\\Program Files',\n options.channel === 'msedge'\n ? 'Microsoft/Edge/Application/msedge.exe'\n : 'Google/Chrome/Application/chrome.exe',\n ),\n ]\n : options.channel === 'msedge'\n ? ['/usr/bin/microsoft-edge']\n : ['/usr/bin/google-chrome', '/usr/bin/chromium', '/usr/bin/chromium-browser'];\n for (const path of candidates) {\n try {\n await access(path);\n return path;\n } catch {}\n }\n throw new Error(\n 'Chrome not found. Install Chrome or set browser.executablePath / browser.cdpUrl.',\n );\n}\n\n/** Local Chrome has an isolated temporary profile; external Chrome always belongs to the caller. */\nexport async function openBrowser(options: BrowserOptions = {}) {\n if (options.kind !== undefined && !['cloud', 'chrome', 'chromium'].includes(options.kind))\n throw new Error('Unknown browser kind. Use Browser.cloud, Browser.chromium or Browser.chrome.');\n if (options.kind === 'cloud') return openCloud(options);\n if (options.kind === 'chrome') {\n if (options.approveConnection !== undefined && typeof options.approveConnection !== 'boolean')\n throw new Error('approveConnection must be boolean.');\n if (options.approveConnection && process.platform !== 'darwin')\n throw new Error('approveConnection is supported only on macOS.');\n const endpoint = options.cdpUrl ?? (await discoverChrome(options));\n if (!['http:', 'https:', 'ws:', 'wss:'].includes(new URL(endpoint).protocol))\n throw new Error('Invalid Chrome CDP endpoint.');\n return { endpoint, close: async () => {} };\n }\n if ('cdpUrl' in options && options.cdpUrl) {\n if (['headless', 'channel', 'executablePath', 'profileDir'].some((key) => key in options))\n throw new Error('cdpUrl cannot be combined with local browser options.');\n if (!['http:', 'https:', 'ws:', 'wss:'].includes(new URL(options.cdpUrl).protocol))\n throw new Error('cdpUrl must be an HTTP(S) or WebSocket endpoint.');\n return { endpoint: options.cdpUrl, close: async () => {} };\n }\n const path = await executable(options as LocalBrowserOptions);\n const persistent = !!options.profileDir;\n if (options.profileDir) await mkdir(options.profileDir, { recursive: true, mode: 0o700 });\n const profile = options.profileDir\n ? await realpath(options.profileDir)\n : await mkdtemp(join(tmpdir(), 'browser-use-'));\n const lockPath = join(profile, '.bu-pi.lock');\n const lock = await open(lockPath, 'wx', 0o600).catch(() => {\n throw new Error(\n `Browser profile is locked: ${profile}. Close its owner. After a crash, verify Chrome and the SDK have exited before removing .bu-pi.lock.`,\n );\n });\n await lock.writeFile(JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }));\n const launchedAt = Date.now();\n const child = spawn(\n path,\n [\n `--user-data-dir=${profile}`,\n '--remote-debugging-port=0',\n '--remote-debugging-address=127.0.0.1',\n '--no-first-run',\n '--no-default-browser-check',\n '--window-size=1440,900',\n ...(options.headless === false ? [] : ['--headless=new']),\n 'about:blank',\n ],\n { stdio: 'ignore' },\n );\n let launchError: Error | undefined;\n child.on('error', (error) => {\n launchError = error;\n });\n let closing: Promise<void> | undefined;\n const close = () =>\n (closing ??= (async () => {\n if (child.exitCode === null && child.signalCode === null && child.pid) {\n const exited = new Promise<void>((resolve) => child.once('exit', () => resolve()));\n child.kill('SIGTERM');\n const timer = setTimeout(() => child.kill('SIGKILL'), 2000);\n await exited;\n clearTimeout(timer);\n }\n await lock.close();\n await rm(lockPath, { force: true });\n if (!persistent) await rm(profile, { recursive: true, force: true });\n })());\n try {\n const deadline = Date.now() + 15_000;\n while (Date.now() < deadline) {\n if (launchError) throw launchError;\n if (child.exitCode !== null || child.signalCode !== null)\n throw new Error('Chrome exited before exposing CDP.');\n try {\n if ((await stat(join(profile, 'DevToolsActivePort'))).mtimeMs < launchedAt - 1)\n throw new Error('Waiting for a fresh DevTools endpoint.');\n const [port, path] = (await readFile(join(profile, 'DevToolsActivePort'), 'utf8'))\n .trim()\n .split('\\n');\n if (port && path) return { endpoint: `ws://127.0.0.1:${port}${path}`, close };\n } catch {}\n await delay(50);\n }\n throw new Error('Chrome did not expose CDP within 15000 ms.');\n } catch (error) {\n await close();\n throw error;\n }\n}\n\n/** Narrow AX action from Browser Harness. Never enables debugging or grants Accessibility. */\nexport function approveChromeConnection(signal: AbortSignal): Promise<string> {\n const script = `using terms from application \"System Events\"\n on clickAllow(nodeRef)\n try\n if (role of nodeRef as text) is \"AXButton\" and (description of nodeRef as text) is \"Allow\" then\n perform action \"AXPress\" of nodeRef\n return true\n end if\n end try\n try\n repeat with childRef in UI elements of nodeRef\n if my clickAllow(childRef) then return true\n end repeat\n end try\n return false\n end clickAllow\n end using terms from\n tell application \"System Events\"\n if exists process \"Google Chrome\" then\n tell process \"Google Chrome\"\n repeat with w in windows\n try\n repeat with s in sheets of w\n if (name of s as text) is \"Allow remote debugging?\" then\n if my clickAllow(s) then return \"ready\"\n end if\n end repeat\n end try\n end repeat\n end tell\n end if\n end tell\n return \"not-found\"`;\n return new Promise((resolve, reject) => {\n execFile('/usr/bin/osascript', ['-e', script], { timeout: 5000, signal }, (error, stdout) => {\n if (error)\n reject(\n new Error(\n 'Chrome approval needs macOS Accessibility permission for the app running Browser Use Pi. Accept Chrome’s prompt manually, or grant that permission.',\n ),\n );\n else resolve(stdout.trim());\n });\n });\n}\n"]}
|
package/dist/cdp.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js';
|
|
2
|
+
type Commands = ProtocolMapping.Commands;
|
|
3
|
+
type Events = ProtocolMapping.Events;
|
|
4
|
+
/** Explicit commands and one-shot events over one flattened CDP WebSocket. No proxies. */
|
|
5
|
+
export declare class CDP {
|
|
6
|
+
private socket;
|
|
7
|
+
readonly timeoutMs: number;
|
|
8
|
+
private nextId;
|
|
9
|
+
private activity;
|
|
10
|
+
get observationTargetId(): string | undefined;
|
|
11
|
+
targetForSession(sessionId: string): string | undefined;
|
|
12
|
+
/** Optional metadata observer; errors cannot change command delivery. Never receives responses. */
|
|
13
|
+
observeCommand: ((method: string, params: unknown, sessionId?: string) => void) | undefined;
|
|
14
|
+
/** Passive result tap. Exceptions cannot change command delivery. May contain page data. */
|
|
15
|
+
observeResponse: ((method: string, params: unknown, result: unknown, sessionId?: string) => void) | undefined;
|
|
16
|
+
observeEvent: ((method: string, params: unknown, sessionId?: string) => void) | undefined;
|
|
17
|
+
private pending;
|
|
18
|
+
private listeners;
|
|
19
|
+
private constructor();
|
|
20
|
+
private endpoint;
|
|
21
|
+
private approveConnection;
|
|
22
|
+
private delegate;
|
|
23
|
+
private closed;
|
|
24
|
+
/** Defer network access until the first browser operation. Never replay a command. */
|
|
25
|
+
static lazy(endpoint: string, timeoutMs?: number, approveConnection?: boolean): CDP;
|
|
26
|
+
private connected;
|
|
27
|
+
static connect(endpoint: string, timeoutMs?: number, approveConnection?: boolean): Promise<CDP>;
|
|
28
|
+
send<M extends keyof Commands>(method: M, params?: Commands[M]['paramsType'][0], sessionId?: string): Promise<Commands[M]['returnType']>;
|
|
29
|
+
private sendMessage;
|
|
30
|
+
waitFor<M extends keyof Events>(method: M, options?: {
|
|
31
|
+
sessionId?: string;
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
predicate?: (event: Events[M][0]) => boolean;
|
|
35
|
+
}): Promise<Events[M][0]>;
|
|
36
|
+
private fail;
|
|
37
|
+
close(): void;
|
|
38
|
+
}
|
|
39
|
+
export {};
|
package/dist/cdp.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { approveChromeConnection } from './browser.js';
|
|
2
|
+
import { positiveInteger } from './protocol.js';
|
|
3
|
+
/** Explicit commands and one-shot events over one flattened CDP WebSocket. No proxies. */
|
|
4
|
+
export class CDP {
|
|
5
|
+
socket;
|
|
6
|
+
timeoutMs;
|
|
7
|
+
nextId = 0;
|
|
8
|
+
// Shared with the lazy connection. Observation follows protocol sessions, not JS variable names.
|
|
9
|
+
activity = {
|
|
10
|
+
targets: new Map(),
|
|
11
|
+
parents: new Map(),
|
|
12
|
+
targetId: undefined,
|
|
13
|
+
};
|
|
14
|
+
get observationTargetId() {
|
|
15
|
+
let targetId = this.activity.targetId;
|
|
16
|
+
const visited = new Set();
|
|
17
|
+
while (targetId && this.activity.parents.has(targetId) && !visited.has(targetId)) {
|
|
18
|
+
visited.add(targetId);
|
|
19
|
+
targetId = this.activity.parents.get(targetId);
|
|
20
|
+
}
|
|
21
|
+
return targetId;
|
|
22
|
+
}
|
|
23
|
+
targetForSession(sessionId) {
|
|
24
|
+
return this.activity.targets.get(sessionId);
|
|
25
|
+
}
|
|
26
|
+
/** Optional metadata observer; errors cannot change command delivery. Never receives responses. */
|
|
27
|
+
observeCommand;
|
|
28
|
+
/** Passive result tap. Exceptions cannot change command delivery. May contain page data. */
|
|
29
|
+
observeResponse;
|
|
30
|
+
observeEvent;
|
|
31
|
+
pending = new Map();
|
|
32
|
+
listeners = new Set();
|
|
33
|
+
constructor(socket, timeoutMs) {
|
|
34
|
+
this.socket = socket;
|
|
35
|
+
this.timeoutMs = timeoutMs;
|
|
36
|
+
if (!socket)
|
|
37
|
+
return;
|
|
38
|
+
socket.addEventListener('message', ({ data }) => {
|
|
39
|
+
try {
|
|
40
|
+
const message = JSON.parse(String(data));
|
|
41
|
+
if (message.id !== undefined) {
|
|
42
|
+
const request = this.pending.get(message.id);
|
|
43
|
+
if (message.error)
|
|
44
|
+
request?.reject(new Error(`CDP ${message.error.code}: ${message.error.message}`));
|
|
45
|
+
else
|
|
46
|
+
request?.resolve(message.result);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.observeEvent?.(message.method, message.params, message.sessionId);
|
|
50
|
+
if (message.method === 'Target.detachedFromTarget')
|
|
51
|
+
this.activity.targets.delete(message.params.sessionId);
|
|
52
|
+
for (const listener of [...this.listeners]) {
|
|
53
|
+
if (listener.method === message.method && listener.sessionId === message.sessionId)
|
|
54
|
+
listener.accept(message.params);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
this.fail(new Error('Malformed CDP message.'));
|
|
60
|
+
socket.close();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
socket.addEventListener('close', () => this.fail(new Error('CDP connection closed. Inspect state before retrying.')));
|
|
64
|
+
socket.addEventListener('error', () => this.fail(new Error('CDP connection failed.')));
|
|
65
|
+
}
|
|
66
|
+
endpoint;
|
|
67
|
+
approveConnection = false;
|
|
68
|
+
delegate;
|
|
69
|
+
closed = false;
|
|
70
|
+
/** Defer network access until the first browser operation. Never replay a command. */
|
|
71
|
+
static lazy(endpoint, timeoutMs = 15_000, approveConnection = false) {
|
|
72
|
+
const connection = new CDP(undefined, timeoutMs);
|
|
73
|
+
connection.endpoint = endpoint;
|
|
74
|
+
connection.approveConnection = approveConnection;
|
|
75
|
+
return connection;
|
|
76
|
+
}
|
|
77
|
+
connected() {
|
|
78
|
+
if (this.closed)
|
|
79
|
+
return Promise.reject(new Error('CDP connection is closed.'));
|
|
80
|
+
this.delegate ??= CDP.connect(this.endpoint, this.timeoutMs, this.approveConnection)
|
|
81
|
+
.then((connection) => {
|
|
82
|
+
if (this.closed) {
|
|
83
|
+
connection.close();
|
|
84
|
+
throw new Error('CDP connection is closed.');
|
|
85
|
+
}
|
|
86
|
+
connection.activity = this.activity;
|
|
87
|
+
connection.observeEvent = (method, params, session) => this.observeEvent?.(method, params, session);
|
|
88
|
+
connection.observeCommand = (method, params, sessionId) => this.observeCommand?.(method, params, sessionId);
|
|
89
|
+
return connection;
|
|
90
|
+
})
|
|
91
|
+
.catch((error) => {
|
|
92
|
+
this.delegate = undefined;
|
|
93
|
+
throw error;
|
|
94
|
+
});
|
|
95
|
+
return this.delegate;
|
|
96
|
+
}
|
|
97
|
+
static async connect(endpoint, timeoutMs = 15_000, approveConnection = false) {
|
|
98
|
+
if (approveConnection && process.platform !== 'darwin')
|
|
99
|
+
throw new Error('Chrome approval is macOS only.');
|
|
100
|
+
positiveInteger('timeoutMs', timeoutMs);
|
|
101
|
+
const url = new URL(endpoint);
|
|
102
|
+
if (url.protocol === 'http:' || url.protocol === 'https:') {
|
|
103
|
+
url.pathname = `${url.pathname.replace(/\/$/, '')}/json/version`;
|
|
104
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
105
|
+
if (!response.ok)
|
|
106
|
+
throw new Error(`CDP discovery failed (${response.status}).`);
|
|
107
|
+
endpoint = (await response.json()).webSocketDebuggerUrl;
|
|
108
|
+
}
|
|
109
|
+
else if (!['ws:', 'wss:'].includes(url.protocol))
|
|
110
|
+
throw new Error('Unsupported CDP endpoint protocol.');
|
|
111
|
+
const socket = new WebSocket(endpoint);
|
|
112
|
+
const connection = new CDP(socket, timeoutMs);
|
|
113
|
+
await new Promise((resolve, reject) => {
|
|
114
|
+
const approval = new AbortController();
|
|
115
|
+
const finish = (error) => {
|
|
116
|
+
approval.abort();
|
|
117
|
+
clearTimeout(timer);
|
|
118
|
+
socket.removeEventListener('open', open);
|
|
119
|
+
socket.removeEventListener('error', failed);
|
|
120
|
+
socket.removeEventListener('close', failed);
|
|
121
|
+
if (error) {
|
|
122
|
+
socket.close();
|
|
123
|
+
reject(error);
|
|
124
|
+
}
|
|
125
|
+
else
|
|
126
|
+
resolve();
|
|
127
|
+
};
|
|
128
|
+
const open = () => finish();
|
|
129
|
+
const failed = () => finish(new Error('Could not connect to CDP endpoint.'));
|
|
130
|
+
const timer = setTimeout(() => finish(new Error('CDP connection timed out.')), timeoutMs);
|
|
131
|
+
socket.addEventListener('open', open, { once: true });
|
|
132
|
+
socket.addEventListener('error', failed, { once: true });
|
|
133
|
+
socket.addEventListener('close', failed, { once: true });
|
|
134
|
+
if (approveConnection) {
|
|
135
|
+
void (async () => {
|
|
136
|
+
while (!approval.signal.aborted) {
|
|
137
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
138
|
+
if (approval.signal.aborted)
|
|
139
|
+
return;
|
|
140
|
+
if ((await approveChromeConnection(approval.signal)) === 'ready')
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
})().catch((error) => {
|
|
144
|
+
if (!approval.signal.aborted)
|
|
145
|
+
finish(error);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return connection;
|
|
150
|
+
}
|
|
151
|
+
async send(method, params = {}, sessionId) {
|
|
152
|
+
// Capture the observer at dispatch, so late responses cannot enter a later cell.
|
|
153
|
+
const observe = this.observeResponse;
|
|
154
|
+
const result = this.endpoint
|
|
155
|
+
? await (await this.connected()).send(method, params, sessionId)
|
|
156
|
+
: await this.sendMessage(method, params, sessionId);
|
|
157
|
+
if (method === 'Target.attachToTarget') {
|
|
158
|
+
const attached = result;
|
|
159
|
+
const target = params.targetId;
|
|
160
|
+
this.activity.targets.set(attached.sessionId, target);
|
|
161
|
+
this.activity.targetId = target;
|
|
162
|
+
}
|
|
163
|
+
else if (method === 'Target.detachFromTarget') {
|
|
164
|
+
const detached = params;
|
|
165
|
+
if (detached?.sessionId)
|
|
166
|
+
this.activity.targets.delete(detached.sessionId);
|
|
167
|
+
}
|
|
168
|
+
else if (method === 'Target.closeTarget' && result.success) {
|
|
169
|
+
const target = params.targetId;
|
|
170
|
+
if (this.observationTargetId === target || this.activity.targetId === target)
|
|
171
|
+
this.activity.targetId = undefined;
|
|
172
|
+
for (const [id, value] of this.activity.targets)
|
|
173
|
+
if (value === target)
|
|
174
|
+
this.activity.targets.delete(id);
|
|
175
|
+
this.activity.parents.delete(target);
|
|
176
|
+
}
|
|
177
|
+
if (method === 'Target.getTargets' || method === 'Target.getTargetInfo') {
|
|
178
|
+
const infos = method === 'Target.getTargets'
|
|
179
|
+
? result.targetInfos
|
|
180
|
+
: [result.targetInfo];
|
|
181
|
+
for (const info of infos)
|
|
182
|
+
if (info.type === 'iframe' && info.parentFrameId)
|
|
183
|
+
this.activity.parents.set(info.targetId, info.parentFrameId);
|
|
184
|
+
}
|
|
185
|
+
if (method === 'Page.getFrameTree' && sessionId) {
|
|
186
|
+
const targetId = this.targetForSession(sessionId);
|
|
187
|
+
const visit = (tree) => {
|
|
188
|
+
if (targetId && tree.frame.id !== targetId)
|
|
189
|
+
this.activity.parents.set(tree.frame.id, targetId);
|
|
190
|
+
tree.childFrames?.forEach(visit);
|
|
191
|
+
};
|
|
192
|
+
visit(result.frameTree);
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
observe?.(method, params, result, sessionId);
|
|
196
|
+
}
|
|
197
|
+
catch { }
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
sendMessage(method, params, sessionId) {
|
|
201
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN)
|
|
202
|
+
return Promise.reject(new Error('CDP connection is closed.'));
|
|
203
|
+
if (this.pending.size >= 256)
|
|
204
|
+
return Promise.reject(new Error('Too many pending CDP commands (256).'));
|
|
205
|
+
if (sessionId)
|
|
206
|
+
this.activity.targetId = this.targetForSession(sessionId);
|
|
207
|
+
try {
|
|
208
|
+
this.observeCommand?.(method, params, sessionId);
|
|
209
|
+
}
|
|
210
|
+
catch { }
|
|
211
|
+
const id = ++this.nextId;
|
|
212
|
+
return new Promise((resolve, reject) => {
|
|
213
|
+
const finish = (error, value) => {
|
|
214
|
+
clearTimeout(timer);
|
|
215
|
+
this.pending.delete(id);
|
|
216
|
+
if (error)
|
|
217
|
+
reject(error);
|
|
218
|
+
else
|
|
219
|
+
resolve(value);
|
|
220
|
+
};
|
|
221
|
+
const timer = setTimeout(() => finish(new Error(`CDP ${method} exceeded ${this.timeoutMs} ms; the action may have happened.`)), this.timeoutMs);
|
|
222
|
+
this.pending.set(id, {
|
|
223
|
+
resolve: (value) => finish(undefined, value),
|
|
224
|
+
reject: (error) => finish(error),
|
|
225
|
+
});
|
|
226
|
+
try {
|
|
227
|
+
this.socket.send(JSON.stringify({ id, method, params, ...(sessionId ? { sessionId } : {}) }));
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
waitFor(method, options = {}) {
|
|
235
|
+
const timeoutMs = positiveInteger('timeoutMs', options.timeoutMs ?? this.timeoutMs);
|
|
236
|
+
if (this.endpoint)
|
|
237
|
+
return this.connected().then((connection) => connection.waitFor(method, options));
|
|
238
|
+
const promise = new Promise((resolve, reject) => {
|
|
239
|
+
const finish = (error, value) => {
|
|
240
|
+
clearTimeout(timer);
|
|
241
|
+
this.listeners.delete(listener);
|
|
242
|
+
options.signal?.removeEventListener('abort', abort);
|
|
243
|
+
if (error)
|
|
244
|
+
reject(error);
|
|
245
|
+
else
|
|
246
|
+
resolve(value);
|
|
247
|
+
};
|
|
248
|
+
const listener = {
|
|
249
|
+
method,
|
|
250
|
+
sessionId: options.sessionId,
|
|
251
|
+
accept: (value) => {
|
|
252
|
+
try {
|
|
253
|
+
if (!options.predicate || options.predicate(value))
|
|
254
|
+
finish(undefined, value);
|
|
255
|
+
}
|
|
256
|
+
catch (e) {
|
|
257
|
+
finish(e instanceof Error ? e : new Error(String(e)));
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
reject: (error) => finish(error),
|
|
261
|
+
};
|
|
262
|
+
const abort = () => finish(new Error(`CDP ${method} wait cancelled.`));
|
|
263
|
+
const timer = setTimeout(() => finish(new Error(`CDP event ${method} exceeded ${timeoutMs} ms.`)), timeoutMs);
|
|
264
|
+
this.listeners.add(listener);
|
|
265
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
266
|
+
if (options.signal?.aborted)
|
|
267
|
+
abort();
|
|
268
|
+
else if (this.socket?.readyState !== WebSocket.OPEN)
|
|
269
|
+
finish(new Error('CDP connection is closed.'));
|
|
270
|
+
});
|
|
271
|
+
// A caller commonly registers a waiter before an action, then awaits it afterward.
|
|
272
|
+
void promise.catch(() => { });
|
|
273
|
+
return promise;
|
|
274
|
+
}
|
|
275
|
+
fail(error) {
|
|
276
|
+
for (const request of [...this.pending.values()])
|
|
277
|
+
request.reject(error);
|
|
278
|
+
for (const listener of [...this.listeners])
|
|
279
|
+
listener.reject(error);
|
|
280
|
+
}
|
|
281
|
+
close() {
|
|
282
|
+
this.closed = true;
|
|
283
|
+
this.activity.targets.clear();
|
|
284
|
+
this.activity.parents.clear();
|
|
285
|
+
this.activity.targetId = undefined;
|
|
286
|
+
void this.delegate?.then((connection) => connection.close()).catch(() => { });
|
|
287
|
+
this.fail(new Error('CDP connection closed.'));
|
|
288
|
+
this.socket?.close();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=cdp.js.map
|