browser-pilot 0.0.13 → 0.0.15
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/README.md +82 -617
- package/dist/actions.cjs +1438 -17
- package/dist/actions.d.cts +21 -3
- package/dist/actions.d.ts +21 -3
- package/dist/actions.mjs +1 -1
- package/dist/browser-MEWT75IB.mjs +11 -0
- package/dist/browser.cjs +1583 -22
- package/dist/browser.d.cts +12 -3
- package/dist/browser.d.ts +12 -3
- package/dist/browser.mjs +3 -3
- package/dist/cdp.cjs +36 -3
- package/dist/cdp.d.cts +1 -1
- package/dist/cdp.d.ts +1 -1
- package/dist/cdp.mjs +3 -1
- package/dist/{chunk-A2ZRAEO3.mjs → chunk-7YVCOL2W.mjs} +1428 -17
- package/dist/chunk-BVZALQT4.mjs +303 -0
- package/dist/chunk-DTVRFXKI.mjs +35 -0
- package/dist/{chunk-HP6R3W32.mjs → chunk-LCNFBXB5.mjs} +33 -6
- package/dist/chunk-LUGLEMVR.mjs +11 -0
- package/dist/chunk-USYSHCI3.mjs +8640 -0
- package/dist/chunk-WPNW23CE.mjs +466 -0
- package/dist/{chunk-VDAMDOS6.mjs → chunk-ZAXQ5OTV.mjs} +151 -7
- package/dist/cli.mjs +3225 -8434
- package/dist/{client-DRqxBdHv.d.ts → client-B5QBRgIy.d.cts} +10 -6
- package/dist/{client-DRqxBdHv.d.cts → client-B5QBRgIy.d.ts} +10 -6
- package/dist/client-JWWZWO6L.mjs +12 -0
- package/dist/index.cjs +1629 -24
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +3 -3
- package/dist/page-XPS6IC6V.mjs +7 -0
- package/dist/transport-WHEBAZUP.mjs +83 -0
- package/dist/{types-CzgQjai9.d.ts → types-C9ySEdOX.d.cts} +78 -4
- package/dist/{types-BXMGFtnB.d.cts → types-Cvvf0oGu.d.ts} +78 -4
- package/package.json +2 -2
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createCDPClient
|
|
3
|
+
} from "./chunk-LCNFBXB5.mjs";
|
|
4
|
+
import {
|
|
5
|
+
Page
|
|
6
|
+
} from "./chunk-USYSHCI3.mjs";
|
|
7
|
+
|
|
8
|
+
// src/providers/browserbase.ts
|
|
9
|
+
var BrowserBaseProvider = class {
|
|
10
|
+
name = "browserbase";
|
|
11
|
+
apiKey;
|
|
12
|
+
projectId;
|
|
13
|
+
baseUrl;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.apiKey = options.apiKey;
|
|
16
|
+
this.projectId = options.projectId;
|
|
17
|
+
this.baseUrl = options.baseUrl ?? "https://api.browserbase.com";
|
|
18
|
+
}
|
|
19
|
+
async createSession(options = {}) {
|
|
20
|
+
const response = await fetch(`${this.baseUrl}/v1/sessions`, {
|
|
21
|
+
method: "POST",
|
|
22
|
+
headers: {
|
|
23
|
+
"X-BB-API-Key": this.apiKey,
|
|
24
|
+
"Content-Type": "application/json"
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({
|
|
27
|
+
projectId: this.projectId,
|
|
28
|
+
browserSettings: {
|
|
29
|
+
viewport: options.width && options.height ? {
|
|
30
|
+
width: options.width,
|
|
31
|
+
height: options.height
|
|
32
|
+
} : void 0
|
|
33
|
+
},
|
|
34
|
+
...options
|
|
35
|
+
})
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
const text = await response.text();
|
|
39
|
+
throw new Error(`BrowserBase createSession failed: ${response.status} ${text}`);
|
|
40
|
+
}
|
|
41
|
+
const session = await response.json();
|
|
42
|
+
const connectResponse = await fetch(`${this.baseUrl}/v1/sessions/${session.id}`, {
|
|
43
|
+
headers: {
|
|
44
|
+
"X-BB-API-Key": this.apiKey
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
if (!connectResponse.ok) {
|
|
48
|
+
throw new Error(`BrowserBase getSession failed: ${connectResponse.status}`);
|
|
49
|
+
}
|
|
50
|
+
const sessionDetails = await connectResponse.json();
|
|
51
|
+
if (!sessionDetails.connectUrl) {
|
|
52
|
+
throw new Error("BrowserBase session does not have a connectUrl");
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
wsUrl: sessionDetails.connectUrl,
|
|
56
|
+
sessionId: session.id,
|
|
57
|
+
metadata: {
|
|
58
|
+
debugUrl: sessionDetails.debugUrl,
|
|
59
|
+
projectId: this.projectId,
|
|
60
|
+
status: sessionDetails.status
|
|
61
|
+
},
|
|
62
|
+
close: async () => {
|
|
63
|
+
await fetch(`${this.baseUrl}/v1/sessions/${session.id}`, {
|
|
64
|
+
method: "DELETE",
|
|
65
|
+
headers: {
|
|
66
|
+
"X-BB-API-Key": this.apiKey
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async resumeSession(sessionId) {
|
|
73
|
+
const response = await fetch(`${this.baseUrl}/v1/sessions/${sessionId}`, {
|
|
74
|
+
headers: {
|
|
75
|
+
"X-BB-API-Key": this.apiKey
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw new Error(`BrowserBase resumeSession failed: ${response.status}`);
|
|
80
|
+
}
|
|
81
|
+
const session = await response.json();
|
|
82
|
+
if (!session.connectUrl) {
|
|
83
|
+
throw new Error("BrowserBase session does not have a connectUrl (may be closed)");
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
wsUrl: session.connectUrl,
|
|
87
|
+
sessionId: session.id,
|
|
88
|
+
metadata: {
|
|
89
|
+
debugUrl: session.debugUrl,
|
|
90
|
+
projectId: this.projectId,
|
|
91
|
+
status: session.status
|
|
92
|
+
},
|
|
93
|
+
close: async () => {
|
|
94
|
+
await fetch(`${this.baseUrl}/v1/sessions/${sessionId}`, {
|
|
95
|
+
method: "DELETE",
|
|
96
|
+
headers: {
|
|
97
|
+
"X-BB-API-Key": this.apiKey
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// src/providers/browserless.ts
|
|
106
|
+
var BrowserlessProvider = class {
|
|
107
|
+
name = "browserless";
|
|
108
|
+
token;
|
|
109
|
+
baseUrl;
|
|
110
|
+
constructor(options) {
|
|
111
|
+
this.token = options.token;
|
|
112
|
+
this.baseUrl = options.baseUrl ?? "wss://chrome.browserless.io";
|
|
113
|
+
}
|
|
114
|
+
async createSession(options = {}) {
|
|
115
|
+
const params = new URLSearchParams({
|
|
116
|
+
token: this.token
|
|
117
|
+
});
|
|
118
|
+
if (options.width && options.height) {
|
|
119
|
+
params.set("--window-size", `${options.width},${options.height}`);
|
|
120
|
+
}
|
|
121
|
+
if (options.proxy?.server) {
|
|
122
|
+
params.set("--proxy-server", options.proxy.server);
|
|
123
|
+
}
|
|
124
|
+
const wsUrl = `${this.baseUrl}?${params.toString()}`;
|
|
125
|
+
return {
|
|
126
|
+
wsUrl,
|
|
127
|
+
metadata: {
|
|
128
|
+
provider: "browserless"
|
|
129
|
+
},
|
|
130
|
+
close: async () => {
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// Browserless doesn't support session resumption in the same way
|
|
135
|
+
// Each connection is a fresh browser instance
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// src/providers/generic.ts
|
|
139
|
+
function sleep(ms) {
|
|
140
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
141
|
+
}
|
|
142
|
+
async function fetchDevToolsJson(host, path, errorPrefix, options = {}) {
|
|
143
|
+
const protocol = host.includes("://") ? "" : "http://";
|
|
144
|
+
const attempts = options.attempts ?? 1;
|
|
145
|
+
let delayMs = options.initialDelayMs ?? 50;
|
|
146
|
+
const maxDelayMs = options.maxDelayMs ?? 250;
|
|
147
|
+
let lastError;
|
|
148
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
149
|
+
try {
|
|
150
|
+
const response = await fetch(`${protocol}${host}${path}`);
|
|
151
|
+
if (response.ok) {
|
|
152
|
+
return await response.json();
|
|
153
|
+
}
|
|
154
|
+
lastError = new Error(`${errorPrefix}: ${response.status}`);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
lastError = new Error(
|
|
157
|
+
`${errorPrefix}: ${error instanceof Error ? error.message : String(error)}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if (attempt < attempts) {
|
|
161
|
+
await sleep(delayMs);
|
|
162
|
+
delayMs = Math.min(delayMs * 2, maxDelayMs);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
throw lastError ?? new Error(errorPrefix);
|
|
166
|
+
}
|
|
167
|
+
var GenericProvider = class {
|
|
168
|
+
name = "generic";
|
|
169
|
+
wsUrl;
|
|
170
|
+
constructor(options) {
|
|
171
|
+
this.wsUrl = options.wsUrl;
|
|
172
|
+
}
|
|
173
|
+
async createSession(_options = {}) {
|
|
174
|
+
return {
|
|
175
|
+
wsUrl: this.wsUrl,
|
|
176
|
+
metadata: {
|
|
177
|
+
provider: "generic"
|
|
178
|
+
},
|
|
179
|
+
close: async () => {
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
async function getBrowserWebSocketUrl(host = "localhost:9222") {
|
|
185
|
+
const info = await fetchDevToolsJson(host, "/json/version", "Failed to get browser info", {
|
|
186
|
+
attempts: 10,
|
|
187
|
+
initialDelayMs: 50,
|
|
188
|
+
maxDelayMs: 250
|
|
189
|
+
});
|
|
190
|
+
return info.webSocketDebuggerUrl;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/providers/index.ts
|
|
194
|
+
function createProvider(options) {
|
|
195
|
+
switch (options.provider) {
|
|
196
|
+
case "browserbase":
|
|
197
|
+
if (!options.apiKey) {
|
|
198
|
+
throw new Error("BrowserBase provider requires apiKey");
|
|
199
|
+
}
|
|
200
|
+
if (!options.projectId) {
|
|
201
|
+
throw new Error("BrowserBase provider requires projectId");
|
|
202
|
+
}
|
|
203
|
+
return new BrowserBaseProvider({
|
|
204
|
+
apiKey: options.apiKey,
|
|
205
|
+
projectId: options.projectId
|
|
206
|
+
});
|
|
207
|
+
case "browserless":
|
|
208
|
+
if (!options.apiKey) {
|
|
209
|
+
throw new Error("Browserless provider requires apiKey (token)");
|
|
210
|
+
}
|
|
211
|
+
return new BrowserlessProvider({
|
|
212
|
+
token: options.apiKey
|
|
213
|
+
});
|
|
214
|
+
case "generic":
|
|
215
|
+
if (!options.wsUrl) {
|
|
216
|
+
throw new Error("Generic provider requires wsUrl");
|
|
217
|
+
}
|
|
218
|
+
return new GenericProvider({
|
|
219
|
+
wsUrl: options.wsUrl
|
|
220
|
+
});
|
|
221
|
+
default:
|
|
222
|
+
throw new Error(`Unknown provider: ${options.provider}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/browser/browser.ts
|
|
227
|
+
function scoreTarget(t) {
|
|
228
|
+
let score = 0;
|
|
229
|
+
if (t.url.startsWith("http://") || t.url.startsWith("https://")) score += 10;
|
|
230
|
+
if (t.url.startsWith("chrome://")) score -= 20;
|
|
231
|
+
if (t.url.startsWith("chrome-extension://")) score -= 15;
|
|
232
|
+
if (t.url.startsWith("devtools://")) score -= 25;
|
|
233
|
+
if (t.url === "about:blank") score -= 5;
|
|
234
|
+
if (!t.attached) score += 3;
|
|
235
|
+
if (t.title && t.title.length > 0) score += 2;
|
|
236
|
+
return score;
|
|
237
|
+
}
|
|
238
|
+
function pickBestTarget(targets) {
|
|
239
|
+
if (targets.length === 0) return void 0;
|
|
240
|
+
const sorted = [...targets].sort((a, b) => scoreTarget(b) - scoreTarget(a));
|
|
241
|
+
return sorted[0].targetId;
|
|
242
|
+
}
|
|
243
|
+
var Browser = class _Browser {
|
|
244
|
+
cdp;
|
|
245
|
+
providerSession;
|
|
246
|
+
pages = /* @__PURE__ */ new Map();
|
|
247
|
+
pageCounter = 0;
|
|
248
|
+
constructor(cdp, _provider, providerSession, _options) {
|
|
249
|
+
this.cdp = cdp;
|
|
250
|
+
this.providerSession = providerSession;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Create a Browser from an existing CDPClient (used by daemon fast-path).
|
|
254
|
+
* The caller is responsible for the CDP connection lifecycle.
|
|
255
|
+
*/
|
|
256
|
+
static fromCDP(cdp, sessionInfo) {
|
|
257
|
+
const providerSession = {
|
|
258
|
+
wsUrl: sessionInfo.wsUrl,
|
|
259
|
+
sessionId: sessionInfo.sessionId,
|
|
260
|
+
async close() {
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const provider = {
|
|
264
|
+
name: sessionInfo.provider ?? "daemon",
|
|
265
|
+
async createSession() {
|
|
266
|
+
return providerSession;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
return new _Browser(cdp, provider, providerSession, { provider: "generic" });
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Connect to a browser instance
|
|
273
|
+
*/
|
|
274
|
+
static async connect(options) {
|
|
275
|
+
const provider = createProvider(options);
|
|
276
|
+
const session = await provider.createSession(options.session);
|
|
277
|
+
const cdp = await createCDPClient(session.wsUrl, {
|
|
278
|
+
debug: options.debug,
|
|
279
|
+
timeout: options.timeout
|
|
280
|
+
});
|
|
281
|
+
return new _Browser(cdp, provider, session, options);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get or create a page by name.
|
|
285
|
+
* If no name is provided, returns the first available page or creates a new one.
|
|
286
|
+
*
|
|
287
|
+
* Target selection heuristics (when no targetId is specified):
|
|
288
|
+
* - Prefer http/https URLs over chrome://, devtools://, about:blank
|
|
289
|
+
* - Prefer unattached targets (not already controlled by another client)
|
|
290
|
+
* - Filter by targetUrl if provided
|
|
291
|
+
*/
|
|
292
|
+
async page(name, options) {
|
|
293
|
+
const pageName = name ?? "default";
|
|
294
|
+
const cached = this.pages.get(pageName);
|
|
295
|
+
if (cached) return cached;
|
|
296
|
+
const targets = await this.cdp.send(
|
|
297
|
+
"Target.getTargets",
|
|
298
|
+
void 0,
|
|
299
|
+
null
|
|
300
|
+
);
|
|
301
|
+
let pageTargets = targets.targetInfos.filter((t) => t.type === "page");
|
|
302
|
+
if (options?.targetUrl) {
|
|
303
|
+
const urlFilter = options.targetUrl;
|
|
304
|
+
const filtered = pageTargets.filter((t) => t.url.includes(urlFilter));
|
|
305
|
+
if (filtered.length > 0) {
|
|
306
|
+
pageTargets = filtered;
|
|
307
|
+
} else {
|
|
308
|
+
console.warn(
|
|
309
|
+
`[browser-pilot] No targets match URL filter "${urlFilter}", falling back to all page targets`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
let targetId;
|
|
314
|
+
if (options?.targetId) {
|
|
315
|
+
const targetExists = targets.targetInfos.some(
|
|
316
|
+
(t) => t.type === "page" && t.targetId === options.targetId
|
|
317
|
+
);
|
|
318
|
+
if (targetExists) {
|
|
319
|
+
targetId = options.targetId;
|
|
320
|
+
} else {
|
|
321
|
+
console.warn(`[browser-pilot] Target ${options.targetId} no longer exists, falling back`);
|
|
322
|
+
targetId = pickBestTarget(pageTargets) ?? (await this.cdp.send(
|
|
323
|
+
"Target.createTarget",
|
|
324
|
+
{
|
|
325
|
+
url: "about:blank"
|
|
326
|
+
},
|
|
327
|
+
null
|
|
328
|
+
)).targetId;
|
|
329
|
+
}
|
|
330
|
+
} else if (pageTargets.length > 0) {
|
|
331
|
+
targetId = pickBestTarget(pageTargets);
|
|
332
|
+
} else {
|
|
333
|
+
const result = await this.cdp.send(
|
|
334
|
+
"Target.createTarget",
|
|
335
|
+
{
|
|
336
|
+
url: "about:blank"
|
|
337
|
+
},
|
|
338
|
+
null
|
|
339
|
+
);
|
|
340
|
+
targetId = result.targetId;
|
|
341
|
+
}
|
|
342
|
+
await this.cdp.attachToTarget(targetId);
|
|
343
|
+
const page = new Page(this.cdp, targetId);
|
|
344
|
+
await page.init();
|
|
345
|
+
const minViewport = options?.minViewport !== void 0 ? options.minViewport : { width: 200, height: 200 };
|
|
346
|
+
if (minViewport !== false) {
|
|
347
|
+
try {
|
|
348
|
+
const viewport = await page.evaluate(
|
|
349
|
+
"({ w: window.innerWidth, h: window.innerHeight })"
|
|
350
|
+
);
|
|
351
|
+
if (viewport.w < minViewport.width || viewport.h < minViewport.height) {
|
|
352
|
+
console.warn(
|
|
353
|
+
`[browser-pilot] Attached target has small viewport (${viewport.w}x${viewport.h}). Applying default viewport override (1280x720). Use { minViewport: false } to disable this check.`
|
|
354
|
+
);
|
|
355
|
+
await page.setViewport({ width: 1280, height: 720 });
|
|
356
|
+
}
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
this.pages.set(pageName, page);
|
|
361
|
+
return page;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Create a new page (tab)
|
|
365
|
+
*/
|
|
366
|
+
async newPage(url = "about:blank") {
|
|
367
|
+
const result = await this.cdp.send(
|
|
368
|
+
"Target.createTarget",
|
|
369
|
+
{
|
|
370
|
+
url
|
|
371
|
+
},
|
|
372
|
+
null
|
|
373
|
+
);
|
|
374
|
+
await this.cdp.attachToTarget(result.targetId);
|
|
375
|
+
const page = new Page(this.cdp, result.targetId);
|
|
376
|
+
await page.init();
|
|
377
|
+
const name = `page-${++this.pageCounter}`;
|
|
378
|
+
this.pages.set(name, page);
|
|
379
|
+
return page;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Close a page by name
|
|
383
|
+
*/
|
|
384
|
+
async closePage(name) {
|
|
385
|
+
const page = this.pages.get(name);
|
|
386
|
+
if (!page) return;
|
|
387
|
+
const targetId = page.targetId;
|
|
388
|
+
await this.cdp.send("Target.closeTarget", { targetId }, null);
|
|
389
|
+
this.pages.delete(name);
|
|
390
|
+
const deadline = Date.now() + 5e3;
|
|
391
|
+
while (Date.now() < deadline) {
|
|
392
|
+
const { targetInfos } = await this.cdp.send(
|
|
393
|
+
"Target.getTargets",
|
|
394
|
+
void 0,
|
|
395
|
+
null
|
|
396
|
+
);
|
|
397
|
+
if (!targetInfos.some((t) => t.targetId === targetId)) return;
|
|
398
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* List all page targets in the connected browser.
|
|
403
|
+
*/
|
|
404
|
+
async listTargets() {
|
|
405
|
+
const { targetInfos } = await this.cdp.send(
|
|
406
|
+
"Target.getTargets",
|
|
407
|
+
void 0,
|
|
408
|
+
null
|
|
409
|
+
);
|
|
410
|
+
return targetInfos.filter((target) => target.type === "page");
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Get the WebSocket URL for this browser connection
|
|
414
|
+
*/
|
|
415
|
+
get wsUrl() {
|
|
416
|
+
return this.providerSession.wsUrl;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Get the provider session ID (for resumption)
|
|
420
|
+
*/
|
|
421
|
+
get sessionId() {
|
|
422
|
+
return this.providerSession.sessionId;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get provider metadata
|
|
426
|
+
*/
|
|
427
|
+
get metadata() {
|
|
428
|
+
return this.providerSession.metadata;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Check if connected
|
|
432
|
+
*/
|
|
433
|
+
get isConnected() {
|
|
434
|
+
return this.cdp.isConnected;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Disconnect from the browser (keeps provider session alive for reconnection)
|
|
438
|
+
*/
|
|
439
|
+
async disconnect() {
|
|
440
|
+
this.pages.clear();
|
|
441
|
+
await this.cdp.close();
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Close the browser session completely
|
|
445
|
+
*/
|
|
446
|
+
async close() {
|
|
447
|
+
this.pages.clear();
|
|
448
|
+
await this.cdp.close();
|
|
449
|
+
await this.providerSession.close();
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Get the underlying CDP client (for advanced usage)
|
|
453
|
+
*/
|
|
454
|
+
get cdpClient() {
|
|
455
|
+
return this.cdp;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
function connect(options) {
|
|
459
|
+
return Browser.connect(options);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export {
|
|
463
|
+
getBrowserWebSocketUrl,
|
|
464
|
+
Browser,
|
|
465
|
+
connect
|
|
466
|
+
};
|