Installation
npm install @browseragentprotocol/client
Quick Start
import { BAPClient, role, label } from "@browseragentprotocol/client";
const client = new BAPClient("ws://localhost:9222");
await client.connect();
await client.launch({ browser: "chromium", headless: false });
await client.createPage({ url: "https://example.com" });
// Semantic selectors
await client.fill(label("Email"), "user@example.com");
await client.click(role("button", "Submit"));
// AI-optimized observation
const obs = await client.observe();
console.log(obs.interactiveElements);
await client.close();
Connection
// Constructor
const client = new BAPClient(url: string, options?: BAPClientOptions);
// Connect (handshake + version check)
await client.connect();
// Shutdown and clean up
await client.close();
BAPClientOptions
BAPClientOptions
| Option | Type | Default | Description | |--------|------|---------|-------------| |
token |
string | — | Authentication token | | maxReconnectAttempts | number | 5 | Max
auto-reconnect attempts | | reconnectDelay | number | 1000 | Base delay for reconnect backoff
(ms) | | timeout | number | 30000 | Default request timeout (ms) | | sessionId | string |
— | Session ID for persistence |Factory Function
import { createClient } from "@browseragentprotocol/client";
// Creates + connects in one call
const client = await createClient("ws://localhost:9222");
Browser Control
// Launch browser
await client.launch({
browser: "chromium", // "chromium" | "firefox" | "webkit"
channel: "chrome", // Optional: "chrome", "msedge"
headless: true,
args: ["--disable-gpu"],
cdpUrl: "ws://...", // Connect to existing browser via CDP
});
// Close browser
await client.closeBrowser();
Page Management
// Create page (optionally navigate)
const page = await client.createPage({ url: "https://example.com" });
// Navigate
await client.navigate("https://example.com/login");
// Navigate with fused observation
await client.navigate("https://example.com", {
observe: { includeInteractiveElements: true },
});
// History
await client.goBack();
await client.goForward();
await client.reload();
// Multi-page
const pages = await client.listPages();
await client.activatePage(pageId);
await client.closePage(pageId);
Actions
All action methods accept any selector type.import { role, text, label, testId, ref } from "@browseragentprotocol/client";
// Click
await client.click(role("button", "Submit"));
await client.dblclick(text("Edit"));
// Form input
await client.fill(label("Email"), "user@example.com");
await client.type(label("Search"), "query"); // Character by character
await client.clear(label("Email"));
// Keyboard
await client.press("Enter");
await client.press("Control+a");
// Mouse
await client.hover(text("Menu"));
await client.scroll({ direction: "down", amount: 500 });
// Selection
await client.select(label("Country"), "US");
await client.check(label("Agree to terms"));
await client.uncheck(label("Marketing emails"));
// File upload
await client.upload(testId("file-input"), "/path/to/file.pdf");
Agent Methods
The three composite methods optimized for AI agent workflows.observe()
Get an AI-optimized snapshot of the page with interactive elements, stable refs, and optional screenshot annotation.const obs = await client.observe({
includeInteractiveElements: true,
includeScreenshot: true,
includeAccessibility: true,
maxElements: 50,
responseTier: "interactive", // "full" | "interactive" | "minimal"
incremental: true, // Only changes since last observation
annotateScreenshot: true, // Set-of-Marks badges
includeWebMCPTools: true, // Discover WebMCP tools
});
// Interactive elements with stable refs
for (const el of obs.interactiveElements ?? []) {
console.log(`${el.ref}: ${el.role} "${el.name}" [${el.actionHints}]`);
}
// Incremental changes
if (obs.changes) {
console.log("Added:", obs.changes.added.length);
console.log("Updated:", obs.changes.updated.length);
console.log("Removed:", obs.changes.removed.length);
}
act()
Execute a multi-step action sequence atomically with pre/post observation fusion.const result = await client.act({
steps: [
BAPClient.step("action/fill", {
selector: label("Email"),
value: "user@example.com",
}),
BAPClient.step("action/fill", {
selector: label("Password"),
value: "secret123",
}),
BAPClient.step("action/click", {
selector: role("button", "Sign In"),
}),
],
postObserve: { includeInteractiveElements: true }, // Fusion
});
console.log(`${result.completed}/${result.total} steps completed`);
console.log("Post-observation:", result.postObservation?.interactiveElements);
extract()
Extract structured data from the page using a JSON Schema.const data = await client.extract({
instruction: "Extract all product names and prices",
schema: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
price: { type: "number" },
},
},
},
mode: "list",
});
if (data.success) {
for (const product of data.data) {
console.log(`${product.name}: $${product.price}`);
}
}
Events
// Subscribe to events
client.on("page", (event) => {
console.log(`Page ${event.type}: ${event.url}`);
});
client.on("console", (event) => {
if (event.type === "error") console.error(event.text);
});
client.on("network", (event) => {
if (event.status >= 400) console.warn(`${event.url} -> ${event.status}`);
});
client.on("dialog", (event) => {
console.log(`Dialog: ${event.type} - ${event.message}`);
});
client.on("download", (event) => {
console.log(`Download: ${event.suggestedFilename}`);
});
// Connection events
client.on("close", () => console.log("Disconnected"));
client.on("error", (err) => console.error("Error:", err));
Storage
// Get full storage state (cookies + localStorage)
const state = await client.getStorageState();
// Restore storage state
await client.setStorageState(state);
// Cookie operations
const cookies = await client.getCookies();
await client.setCookies([{ name: "session", value: "abc", domain: ".example.com" }]);
await client.clearCookies();
Contexts and Frames
// Isolated browser contexts
const ctx = await client.createContext({
contextId: "user-session",
options: { viewport: { width: 1920, height: 1080 } },
});
await client.createPage({ url: "https://example.com", contextId: ctx.contextId });
await client.destroyContext(ctx.contextId);
// Frame navigation
const frames = await client.listFrames();
await client.switchFrame({ selector: { type: "css", value: "iframe#payment" } });
await client.fill(label("Card number"), "4242424242424242");
await client.mainFrame();
Emulation
await client.setViewport({ width: 375, height: 812 }); // iPhone viewport
await client.setUserAgent("Mozilla/5.0 (iPhone; ...)");
await client.setGeolocation({ latitude: 37.7749, longitude: -122.4194 });
await client.setOffline(true);
WebMCP Discovery
const tools = await client.discoverTools();
for (const tool of tools.tools) {
console.log(`${tool.name}: ${tool.description} (source: ${tool.source})`);
}
Error Handling
import {
BAPError,
BAPElementNotFoundError,
BAPTimeoutError,
BAPNavigationError,
} from "@browseragentprotocol/client";
try {
await client.click(role("button", "Submit"));
} catch (error) {
if (error instanceof BAPElementNotFoundError) {
// error.retryable === true
// error.recoveryHint === "Run observe() to refresh..."
const obs = await client.observe();
// Retry with updated selector
} else if (error instanceof BAPTimeoutError) {
// Increase timeout and retry
} else if (error instanceof BAPNavigationError) {
// Check URL validity
}
}