Add Browserbase support for remote browser over CDP (#3)

* Add Browserbase support for remote browser over CDP

When BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID env vars are set,
connect to a Browserbase session via CDP instead of launching a local browser.

* Update URLs to browserbase repo

* Add Browserbase support for remote browser over CDP

When BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID env vars are set,
connect to a Browserbase session via CDP instead of launching a local browser.

* Update link to Browserbase Dashboard in README

* bump browserbase sdk to latest version

* remove sdk as a dep

* change name back to vercel labs

* added try catch blocks, functions to close session

* revert package names

* remove extra if statement

---------

Co-authored-by: Kylejeong2 <kylejeong21@gmail.com>
This commit is contained in:
Paul Klein
2026-01-21 17:49:21 -06:00
committed by GitHub
co-authored by Kylejeong2
parent 399fd7a434
commit 7123d46e7f
2 changed files with 109 additions and 2 deletions
+17
View File
@@ -657,6 +657,23 @@ curl -o .claude/skills/agent-browser/SKILL.md \
https://raw.githubusercontent.com/vercel-labs/agent-browser/main/skills/agent-browser/SKILL.md
```
## Integrations
### Browserbase
[Browserbase](https://browserbase.com) provides remote browser infrastructure to make deployment of agentic browsing agents easy. Use it when running the agent-browser CLI in an environment where a local browser isn't feasible.
To enable Browserbase, set these environment variables:
```bash
export BROWSERBASE_API_KEY="your-api-key"
export BROWSERBASE_PROJECT_ID="your-project-id"
```
When both variables are set, agent-browser automatically connects to a Browserbase session instead of launching a local browser. All commands work identically.
Get your API key and project ID from the [Browserbase Dashboard](https://browserbase.com/overview).
## License
Apache-2.0
+92 -2
View File
@@ -70,6 +70,8 @@ export class BrowserManager {
private browser: Browser | null = null;
private cdpPort: number | null = null;
private isPersistentContext: boolean = false;
private browserbaseSessionId: string | null = null;
private browserbaseApiKey: string | null = null;
private contexts: BrowserContext[] = [];
private pages: Page[] = [];
private activePageIndex: number = 0;
@@ -642,6 +644,79 @@ export class BrowserManager {
return false;
}
/**
* Close a Browserbase session via API
*/
private async closeBrowserbaseSession(sessionId: string, apiKey: string): Promise<void> {
await fetch(`https://api.browserbase.com/v1/sessions/${sessionId}`, {
method: 'DELETE',
headers: {
'X-BB-API-Key': apiKey,
},
});
}
/**
* Connect to Browserbase remote browser via CDP.
* Returns true if connected, false if credentials not available.
*/
private async connectToBrowserbase(): Promise<boolean> {
const browserbaseApiKey = process.env.BROWSERBASE_API_KEY;
const browserbaseProjectId = process.env.BROWSERBASE_PROJECT_ID;
if (!browserbaseApiKey || !browserbaseProjectId) {
return false;
}
const response = await fetch('https://api.browserbase.com/v1/sessions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-BB-API-Key': browserbaseApiKey,
},
body: JSON.stringify({
projectId: browserbaseProjectId,
}),
});
if (!response.ok) {
throw new Error(`Failed to create Browserbase session: ${response.statusText}`);
}
const session = await response.json() as { id: string; connectUrl: string };
const browser = await chromium.connectOverCDP(session.connectUrl).catch(() => {
throw new Error('Failed to connect to Browserbase session via CDP');
});
try {
const contexts = browser.contexts();
if (contexts.length === 0) {
throw new Error('No browser context found in Browserbase session');
}
const context = contexts[0];
const pages = context.pages();
const page = pages[0] ?? (await context.newPage());
this.browserbaseSessionId = session.id;
this.browserbaseApiKey = browserbaseApiKey;
this.browser = browser;
context.setDefaultTimeout(10000);
this.contexts.push(context);
this.pages.push(page);
this.activePageIndex = 0;
this.setupPageTracking(page);
return true;
} catch (error) {
await this.closeBrowserbaseSession(session.id, browserbaseApiKey).catch((sessionError) => {
console.error('Failed to close Browserbase session during cleanup:', sessionError);
});
throw error;
}
}
/**
* Launch the browser with the specified options
* If already launched, this is a no-op (browser stays open)
@@ -669,6 +744,12 @@ export class BrowserManager {
return;
}
// Try connecting to Browserbase if credentials are available
if (await this.connectToBrowserbase()) {
return;
}
// Select browser type
const browserType = options.browser ?? 'chromium';
if (hasExtensions && browserType !== 'chromium') {
throw new Error('Extensions are only supported in Chromium');
@@ -1359,8 +1440,15 @@ export class BrowserManager {
this.cdpSession = null;
}
// CDP: only disconnect, don't close external app's pages
if (this.cdpPort !== null) {
if (this.browserbaseSessionId && this.browserbaseApiKey) {
await this.closeBrowserbaseSession(this.browserbaseSessionId, this.browserbaseApiKey).catch(
(error) => {
console.error('Failed to close Browserbase session:', error);
}
);
this.browser = null;
} else if (this.cdpPort !== null) {
// CDP: only disconnect, don't close external app's pages
if (this.browser) {
await this.browser.close().catch(() => {});
this.browser = null;
@@ -1382,6 +1470,8 @@ export class BrowserManager {
this.pages = [];
this.contexts = [];
this.cdpPort = null;
this.browserbaseSessionId = null;
this.browserbaseApiKey = null;
this.isPersistentContext = false;
this.activePageIndex = 0;
this.refMap = {};