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/early-brooms-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@browserbasehq/stagehand": patch
---

Add a page.sendCDP method
57 changes: 57 additions & 0 deletions packages/core/lib/v3/tests/page-send-cdp.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { test, expect } from "@playwright/test";
import { V3 } from "../v3";
import { v3TestConfig } from "./v3.config";

test.describe("Page sendCDP method", () => {
let v3: V3;

test.beforeEach(async () => {
v3 = new V3(v3TestConfig);
await v3.init();
});

test.afterEach(async () => {
await v3?.close?.().catch(() => {});
});

test("sends CDP commands and requires domain to be enabled first", async () => {
const page = v3.context.pages()[0];
await page.goto("https://example.com");

// Try to add a virtual authenticator without enabling WebAuthn first
// This should fail because the domain needs to be enabled
await expect(
page.sendCDP("WebAuthn.addVirtualAuthenticator", {
options: {
protocol: "ctap2",
transport: "usb",
hasResidentKey: false,
hasUserVerification: false,
isUserVerified: false,
},
}),
).rejects.toThrow();

// Enable the WebAuthn domain
await page.sendCDP("WebAuthn.enable");

// Now adding a virtual authenticator should succeed
const result = await page.sendCDP<{ authenticatorId: string }>(
"WebAuthn.addVirtualAuthenticator",
{
options: {
protocol: "ctap2",
transport: "usb",
hasResidentKey: false,
hasUserVerification: false,
isUserVerified: false,
},
},
);

// Verify we got an authenticator ID back
expect(result).toHaveProperty("authenticatorId");
expect(typeof result.authenticatorId).toBe("string");
expect(result.authenticatorId.length).toBeGreaterThan(0);
});
});
26 changes: 26 additions & 0 deletions packages/core/lib/v3/understudy/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,32 @@ export class Page {
return this._targetId;
}

/**
* Send a CDP command through the main session.
* Allows external consumers to execute arbitrary Chrome DevTools Protocol commands.
*
* @param method - The CDP method name (e.g., "Page.enable", "Runtime.evaluate")
* @param params - Optional parameters for the CDP command
* @returns Promise resolving to the typed CDP response
*
* @example
* // Enable the Runtime domain
* await page.sendCDP("Runtime.enable");
*
* @example
* // Evaluate JavaScript with typed response
* const result = await page.sendCDP<Protocol.Runtime.EvaluateResponse>(
* "Runtime.evaluate",
* { expression: "1 + 1" }
* );
*/
public async sendCDP<T = unknown>(
method: string,
params?: object,
): Promise<T> {
return this.mainSession.send<T>(method, params);
}

/** Seed the cached URL before navigation events converge. */
public seedCurrentUrl(url: string | undefined | null): void {
if (!url) return;
Expand Down