Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-browserbase-url-accessors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@browserbasehq/stagehand": patch
---

Add Browserbase session URL and debug URL accessors
44 changes: 31 additions & 13 deletions packages/core/lib/v3/v3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,17 @@ export class V3 {
private readonly domSettleTimeoutMs?: number;
private _isClosing = false;
public browserbaseSessionId?: string;
private browserbaseSessionUrl?: string;
private browserbaseDebugUrl?: string;
public get browserbaseSessionID(): string | undefined {
return this.browserbaseSessionId;
}
public get browserbaseSessionURL(): string | undefined {
return this.browserbaseSessionUrl;
}
public get browserbaseDebugURL(): string | undefined {
return this.browserbaseDebugUrl;
}
private _onCdpClosed = (why: string) => {
// Single place to react to the transport closing
this._immediateShutdown(`CDP transport closed: ${why}`).catch(() => {});
Expand Down Expand Up @@ -679,6 +687,7 @@ export class V3 {
} as unknown as import("chrome-launcher").LaunchedChrome,
ws: lbo.cdpUrl,
};
this.resetBrowserbaseSessionMetadata();
// Post-connect settings (downloads and viewport) if provided
await this._applyPostConnectLocalOptions(lbo);
return;
Expand Down Expand Up @@ -767,7 +776,7 @@ export class V3 {
createdTempProfile: createdTemp,
preserveUserDataDir: !!lbo.preserveUserDataDir,
};
this.browserbaseSessionId = undefined;
this.resetBrowserbaseSessionMetadata();

// Post-connect settings (downloads and viewport) if provided
await this._applyPostConnectLocalOptions(lbo);
Expand Down Expand Up @@ -841,18 +850,21 @@ export class V3 {

await this._ensureBrowserbaseDownloadsEnabled();

const resumed = !!this.opts.browserbaseSessionID;
let debugUrl: string | undefined;
try {
const dbg = (await bb.sessions.debug(sessionId)) as unknown as {
debuggerUrl?: string;
};
debugUrl = dbg?.debuggerUrl;
} catch {
// Ignore debug fetch failures; continue with sessionUrl only
}
const sessionUrl = `https://www.browserbase.com/sessions/${sessionId}`;
this.browserbaseSessionUrl = sessionUrl;
this.browserbaseDebugUrl = debugUrl;

try {
const resumed = !!this.opts.browserbaseSessionID;
let debugUrl: string | undefined;
try {
const dbg = (await bb.sessions.debug(sessionId)) as unknown as {
debuggerUrl?: string;
};
debugUrl = dbg?.debuggerUrl;
} catch {
// Ignore debug fetch failures; continue with sessionUrl only
}
const sessionUrl = `https://www.browserbase.com/sessions/${sessionId}`;
this.logger({
category: "init",
message: resumed
Expand Down Expand Up @@ -926,6 +938,12 @@ export class V3 {
}
}

private resetBrowserbaseSessionMetadata(): void {
this.browserbaseSessionId = undefined;
this.browserbaseSessionUrl = undefined;
this.browserbaseDebugUrl = undefined;
}

/**
* Run an "act" instruction through the ActHandler.
*
Expand Down Expand Up @@ -1277,7 +1295,7 @@ export class V3 {
this.state = { kind: "UNINITIALIZED" };
this.ctx = null;
this._isClosing = false;
this.browserbaseSessionId = undefined;
this.resetBrowserbaseSessionMetadata();
try {
unbindInstanceLogger(this.instanceId);
} catch {
Expand Down
116 changes: 116 additions & 0 deletions packages/core/tests/browserbase-session-accessors.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { V3 } from "../lib/v3/v3";

const MOCK_SESSION_ID = "session-123";
const MOCK_SESSION_URL = `https://www.browserbase.com/sessions/${MOCK_SESSION_ID}`;
const MOCK_DEBUG_URL = `https://debug.browserbase.com/${MOCK_SESSION_ID}`;

vi.mock("../lib/v3/understudy/context", () => {
class MockConnection {
onTransportClosed = vi.fn();
offTransportClosed = vi.fn();
send = vi.fn(async () => {});
}

class MockV3Context {
static async create(): Promise<MockV3Context> {
return new MockV3Context();
}

conn = new MockConnection();

pages(): never[] {
return [];
}

async close(): Promise<void> {
// noop
}
}

return { V3Context: MockV3Context };
});

vi.mock("../lib/v3/launch/browserbase", () => ({
createBrowserbaseSession: vi.fn(async () => ({
ws: "wss://mock-browserbase",
sessionId: MOCK_SESSION_ID,
bb: {
sessions: {
debug: vi.fn(async () => ({ debuggerUrl: MOCK_DEBUG_URL })),
},
},
})),
}));

vi.mock("../lib/v3/launch/local", () => ({
launchLocalChrome: vi.fn(async () => ({
ws: "ws://local-cdp",
chrome: { kill: vi.fn(async () => {}) },
})),
}));

describe("browserbase accessors", () => {
beforeEach(() => {
process.env.BROWSERBASE_API_KEY = "fake-key";
process.env.BROWSERBASE_PROJECT_ID = "fake-project";
});

afterEach(() => {
delete process.env.BROWSERBASE_API_KEY;
delete process.env.BROWSERBASE_PROJECT_ID;
vi.clearAllMocks();
});

it("exposes Browserbase session and debug URLs after init", async () => {
const v3 = new V3({
env: "BROWSERBASE",
disableAPI: true,
verbose: 0,
});

try {
await v3.init();

expect(v3.browserbaseSessionURL).toBe(MOCK_SESSION_URL);
expect(v3.browserbaseDebugURL).toBe(MOCK_DEBUG_URL);
} finally {
await v3.close().catch(() => {});
}
});

it("clears stored URLs after close", async () => {
const v3 = new V3({
env: "BROWSERBASE",
disableAPI: true,
verbose: 0,
});

await v3.init();
await v3.close();

expect(v3.browserbaseSessionURL).toBeUndefined();
expect(v3.browserbaseDebugURL).toBeUndefined();
});
});

describe("local accessors", () => {
it("stay empty for LOCAL environments", async () => {
const v3 = new V3({
env: "LOCAL",
disableAPI: true,
verbose: 0,
localBrowserLaunchOptions: {
cdpUrl: "ws://local-existing-session",
},
});

try {
await v3.init();
expect(v3.browserbaseSessionURL).toBeUndefined();
expect(v3.browserbaseDebugURL).toBeUndefined();
} finally {
await v3.close().catch(() => {});
}
});
});
30 changes: 30 additions & 0 deletions packages/docs/v3/references/stagehand.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,36 @@ interface HistoryEntry {
}
```

### browserbaseSessionID

Browserbase session identifier for the active Browserbase run.

```typescript
const sessionId = stagehand.browserbaseSessionID;
```

**Type:** `string | undefined` — undefined for LOCAL runs or before `init()`.

### browserbaseSessionURL

Shareable link to the active Browserbase session dashboard.

```typescript
const sessionUrl = stagehand.browserbaseSessionURL;
```

**Type:** `string | undefined` — undefined until a Browserbase session is active.

### browserbaseDebugURL

Debugger URL returned by Browserbase for direct CDP inspection.

```typescript
const debugUrl = stagehand.browserbaseDebugURL;
```

**Type:** `string | undefined` — undefined for LOCAL runs or if Browserbase doesn’t provide one.

## Code Examples

<Tabs>
Expand Down