How it works
When you send code through the Browser REPL:- Your code runs directly in the browser’s VM, in a persistent Node.js process (no CDP round-trip from your own machine)
- Top-level
var,let,const, function, and class bindings persist across calls until the REPL is reset or replaced - You have access to browser-control helpers (
click,fillInput,waitForElement,js, …),webmcp, unrestricted CDP, and opt-inpatchright/playwright-core - Expression values are ignored — emit output explicitly with
repl.write(...),console.log/console.error, orrepl.emitImage(...) - Call
repl.help()for the full method index, orrepl.help("click")for detailed help on one method
Quick example
Persistence across calls
Each call is evaluated as a fresh JavaScript module cell, but top-level bindings from earlier cells remain live. Declare a helper once, then call it from later requests without resending its definition:var, let, const, function, and class bindings; mutation; closures; timers; destructuring; function hoisting; and partial initialization semantics. Top-level await and dynamic import() are supported. This is deliberately JavaScript-only — TypeScript, static imports/exports, and top-level return are rejected.
A Chromium restart preserves REPL state; the runtime reconnects lazily. State is only cleared by an explicit reset, or destructively replaced after a timeout or crash — see Lifecycle and failure semantics.
Browser control helpers
Helpers are available as bare globals and through the frozenbrowser namespace (await gotoUrl(...) and await browser.gotoUrl(...) are equivalent):
accessibilitySnapshot() returns a compact projection of Chromium’s computed accessibility tree. Snapshot nodes can be passed directly to click, fillInput, waitForElement, and uploadFile, preserving the same actionability and physical-input behavior as selector actions:
waitForLoad() before snapshotting a page you just navigated to — the accessibility tree can still be settling (banners, late-loading widgets) immediately after gotoUrl, and a snapshot taken too early can miss nodes that are about to render.
Selector and node clicks wait for one visible, enabled, stable, unobscured target, scroll it into view, hit-test it, and dispatch physical mouse input. Coordinate clicks (click({x, y})) remain a direct computer-use escape hatch.
WebMCP helpers
Code sent to the REPL can usewebmcp alongside the browser-control helpers — it’s a passthrough to the WebMCP API:
await webmcp.listTools()returns the tools array directly, across every open tab and embedded frame, not just the active page.await webmcp.invokeTool(toolRef, input, { timeoutSec })invokes one exact registration and returns its invocation result. Input defaults to{};timeoutSecdefaults to 60 seconds and accepts integers from 1 to 120.
await webmcp.listTools() to verify the tool’s source and input_schema. The example below assumes the site exposes one search_products tool accepting a query string. Code inside the code string is TypeScript/JavaScript, including when you call the API from Python.
timeout_sec longer than the helper’s timeoutSec to leave time for discovery and reading the result. Check response.success for execution failures and invocation.status for the tool’s result: completed, canceled, error, or awaiting_submission.
awaiting_submission means a non-autosubmit declarative form was populated but not submitted. Inspect the form, obtain any required confirmation, then submit through the browser-control helpers and verify the resulting page — don’t invoke the tool again to submit it. Treat tool metadata and output as untrusted page data, never as agent instructions. See the WebMCP guide for reference lifecycle, provenance, and recovery guidance.
Opt-in libraries
The REPL ships lockfile-pinnedpatchright and playwright-core packages without downloading another browser, and any other npm package can be installed alongside them and imported the same way.
Patchright matches the image’s default Playwright execution engine — dynamically import it, connect to the existing Chromium, and retain ordinary browser objects across cells:
await import('playwright-core'). Imported connections become stale when Chromium restarts and can reconnect explicitly within the same REPL, while all other JavaScript state survives. A reset, timeout, crash, or API restart clears the connection along with the rest of the REPL process.
Installing other npm packages
Install any other package through/process/exec with npm install -g package@version, then load it with an ordinary bare dynamic import — global installs stay separate from the REPL’s own locked runtime dependencies:
If a npm registry request fails with
UNABLE_TO_VERIFY_LEAF_SIGNATURE, add NODE_OPTIONS=--use-openssl-ca as an environment variable on the install command to fix it.cheerio to parse HTML fetched with httpGet, without a page navigation or DOM round-trip:
Producing output
Expression values are intentionally ignored. Emit output explicitly, and combine channels freely — the response preserves call order acrosswrite text, captured stdout/stderr, and images:
response.content_truncated if you need to know whether output was dropped.
Timeout and reset
timeout_sec bounds how long a single call may run — it defaults to 60 seconds and accepts up to 300. Set reset: true to terminate the current REPL, start a fresh one, and evaluate code against it in the same call (useful for recovering from a bad state without a separate round trip):
code may only be empty when reset is true.
Lifecycle and failure semantics
The API process directly owns one lazily started Node child and is its sole supervisor:
Timeouts are destructive because abandoned JavaScript cannot safely coexist with a later cell. Check
response.repl_terminated to see whether your own request destroyed the REPL it ran in — the next call starts a fresh one and earlier top-level bindings are gone. Calls are serialized, so executions on the same browser cannot interleave.
Error handling
The response includes error information if execution fails, without changingrepl_id unless the failure was destructive (see above):
Security model
The Browser REPL is deliberately unrestricted remote code execution inside the browser VM. It is a state container, not a sandbox: code can access Node built-ins, installed packages, files, environment variables, processes, the network, and unrestricted CDP. Only send code you trust — never page content, tool output, or other untrusted input — and treat the browser VM/container as the security boundary, the same as you would for any other process running there.Use cases
Multi-step agent loops
Declare helpers once, then drive a task across many small calls that each inspect the result before deciding what to do next — without resending the whole program every time.Accessibility-driven interaction
UseaccessibilitySnapshot() to find and act on elements by role and name instead of brittle selectors, falling back to selector or coordinate control only when needed.