Skip to content
Mayura

Data, safety and extensions

Browsers

Real browsers for agents to read and use web pages in, on the Chrome or Edge installed here or a hosted browser service, kept to the origins you allow, with permission-gated tools.

mayura/browser lets an agent use a real browser: open pages, read them, click, type and choose, take screenshots. It speaks the Chrome DevTools Protocol itself, with no dependency. The Chrome or Edge installed on your machine is built in (mayura/browser/local), any browser with a CDP endpoint can be reached (cdpBrowsers), and hosted browser services come as @mayurajs/browser-* packages. Every browser has a lifetime, and its pages load only from the origins you allow.

ts
import { createBrowsers } from 'mayura/browser';
import { localBrowsers } from 'mayura/browser/local';

const browsers = createBrowsers(localBrowsers(), {
  maxBrowsers: 2,
  maxLifetimeMs: 30 * 60_000,
  origins: ['https://example.com', 'https://*.example.com'],
});

const browser = await browsers.open();
try {
  await browser.goto('https://example.com/');
  const page = await browser.snapshot();
  console.log(page.text);
  // - heading "Example Domain"
  // - link "Learn more" [ref=e1]
  await browser.click('e1');
} finally {
  await browser.release();
}

Limits

createBrowsers(provider, options) holds every browser from a provider to the same limits.

Option Notes
maxBrowsers Required. The most browsers open at once, counting those opening; more fail with LIMIT_EXCEEDED.
maxLifetimeMs Required. The longest a browser may live; at most the provider's.
origins Required. The origins pages may load from, or 'all'. See Origins.
maxTabs The most tabs a browser may have open; 8 by default.
callTimeoutMs How long one call other than a navigation may take; 30 s by default.
navigationTimeoutMs How long a navigation waits for its page to load; 30 s by default. A page still loading is returned with loaded: false.
maxTextBytes The most bytes of a snapshot, of page text or of an evaluated value; 256 KiB by default.
maxScreenshotBytes The largest screenshot; 8 MiB by default.
viewport The window size pages are laid out for; 1280 × 800 by default.
labels Labels given to every browser, to find sessions in the provider's console.
webSocket Opens the CDP WebSocket, for runtimes whose WebSocket cannot send headers.

browsers.close() releases every browser still open and opens no more.

Origins

Nothing is allowed by default: origins lists what pages may load from, as https://example.com (scheme, host and port), https://*.example.com (its subdomains, not example.com itself) or http://127.0.0.1:8080. 'all' allows every http(s) URL. originPolicy(origins) checks a URL against such a list, for tools of your own that fetch pages.

The list is enforced in the browser, not only at goto. Every request is checked before it leaves — navigations, redirects, frames, scripts, images, fetch, and requests from dedicated, shared and service workers — and one to another origin fails as blocked. Each new page, frame or worker is held until its requests are checked; one that cannot be checked never runs. goto to another origin, or a navigation redirected to one, fails with PERMISSION_DENIED. When the browser is this one's alone, requests are checked for the whole browser, so a page another client opens (such as Stagehand, over browser.cdp) is covered from its first request. In a browser shared with others (cdpBrowsers), each page is checked as it is attached, as others' pages must be left alone: a page another CDP client opens there with a URL may make that first request unchecked.

Downloads are refused, file pickers stay closed, and dialogs (alert, confirm, prompt) are dismissed and reported in the next snapshot.

A browser

browsers.open(options) takes lifetimeMs, labels and signal. The browser ends when its lifetime is over, if release() did not end it first; after that every call fails with a BrowserError whose reason is gone. Calls on one browser run one at a time, in order.

Method Notes
goto(url) Opens a page in the current tab. Returns its url, title, status and whether it loaded.
back(), forward(), reload() Move through the tab's history.
snapshot() The page as an outline: one line per meaningful element, indented by depth. Elements to act on carry [ref=eN]; refs from earlier snapshots stop working. Also reports dialogs the page opened.
text({ ref }) The visible text of the page, or of one element.
click(ref, { double }), hover(ref) Point at an element by its ref.
fill(ref, text) Replaces the text of an input or editable element, as typing would.
press(keys) Presses a key on what has focus: Enter, Tab, ArrowDown, a, Control+a, Shift+Tab.
select(ref, option) Chooses an option of a list by its value or label.
scroll(dy, { ref }) Scrolls the page, or an element, by dy pixels.
screenshot({ fullPage }) A PNG of the visible page, or all of it.
evaluate(expression) Runs JavaScript in the page; returns { value } as JSON, or { error } with what it threw.
tabs(), newTab(url), selectTab(tab), closeTab(tab) Tabs, including windows a page opens.
release() Ends the browser.

liveViewUrl is where a person can watch, on providers with a live view. It may carry a token: treat it as a secret.

For a live view of any browser other clients can join, local ones included, @mayurajs/browser-viewer streams the active tab to a page: serveBrowserViewer() then share(browser) gives a link that expires, view only unless interact: true lets a person use the page (within the same origins). browserViewer() is the same as request handling to mount in your own server.

cdp is the browser's CDP endpoint (url, headers, and whether it is isolated), for another client to drive it alongside, such as Stagehand. It may carry a credential. Origins stay enforced for every page while the browser is open. It is absent where another connection would not reach this browser: a provider whose every connection starts a browser of its own (such as Browserless) marks its endpoint joinable: false.

Tools

browserTools(browser, options) gives an agent the browser as tools named after options.name. Navigating and reading are on; the rest are each off until enabled, and each needs its permission.

Tools Permission Option
<name>.goto, <name>.back, <name>.snapshot, <name>.text browser:<name>:read always
<name>.click, .fill, .press, .select, .hover, .scroll, .tab browser:<name>:act act: true
<name>.screenshot browser:<name>:read screenshot: true
<name>.evaluate browser:<name>:evaluate evaluate: true

Navigations and actions return a fresh snapshot (snapshotAfter, true by default), so the model sees what changed. A mistake the model can put right — an origin that is not allowed, a ref from an older snapshot, an option that is not there — comes back as the tool's result, { error, message }, so it can try again; a failure of the browser itself fails the call. callCostMicros charges each navigation and action to the run's budget.

ts
import { browserPerRun, browserTools } from 'mayura/browser';

const perRun = browserPerRun(browsers);
const tools = browserTools(perRun.source, { name: 'web', act: true });
// Release each run's browser when it ends:
// defineHook({ id: 'browser.release', version: '1', stage: 'onFinally', handler: (_event, context) => perRun.release(context.runId) })

A browser you already run

cdpBrowsers({ endpoint, headers }) reaches any browser with a CDP endpoint: a ws(s):// URL, or http://host:port (asked for its WebSocket URL), such as a Chrome started with --remote-debugging-port. Each browser opened there is a new browser context, closed on release; the browser's other tabs are left alone.

Providers

Package Browsers Live view
mayura/browser/local The Chrome, Chromium or Edge installed here, one process with a new profile per browser; Node no
cdpBrowsers (in mayura/browser) Any browser with a CDP endpoint, in a context of its own no
@mayurajs/browser-browserbase Browserbase sessions; captchas, recording and logs off debugger view
@mayurajs/browser-steel Steel sessions, which outlive their connection: release them session viewer, view-only by default
@mayurajs/browser-hyperbrowser Hyperbrowser sessions; proxies, stealth, captchas and recording off live view, view-only by default
@mayurajs/browser-browserless Browserless, hosted or your own: a fresh browser per connection no
@mayurajs/browser-kernel Kernel browsers, deleted soon after nothing is connected; stealth (and its captcha solver) off live view, view-only by default
@mayurajs/browser-browser-use Browser Use Cloud browsers, which outlive their connection; no proxy, no captcha solving only with liveView: true
@mayurajs/browser-anchor Anchor Browser sessions; recording and ad blocking off live view, view-only by default
@mayurajs/browser-browserview BrowserView sessions, not kept alive; stealth off; its CDP connection needs a header (give webSocket on Workers or Deno) watch link by default
@mayurajs/browser-cloudflare Cloudflare Browser Run from outside a Worker: a session per browser, closed on release yes (read only by default)
@mayurajs/browser-firecrawl Firecrawl Interact sessions; the package also has firecrawlTools, Firecrawl's scrape, map, search, crawl and extract as permission-gated tools kept to origins live view, view-only by default

localBrowsers({ channel, executablePath, headless, args }) finds Chrome (channel: 'chrome', the default) or Edge ('edge') in the usual install places, or runs executablePath. Nothing is downloaded. Each browser runs headless with its own profile in a temporary directory, removed on release. args adds switches; ones that would open the browser to others (remote debugging, profiles, extensions, proxies, the sandbox) are refused.

Stagehand and agent-browser

stagehandTools(browser, { model }) in @mayurajs/browser-stagehand adds Stagehand 3's natural-language tools — extract, observe and, when enabled, act — on a Mayura browser, driving it alongside over browser.cdp. The browser's origins, lifetime and limits still hold, including for pages Stagehand opens.

agentBrowserTools(browser) in @mayurajs/browser-agent-browser does the same with Vercel Labs' agent-browser command line: its snapshots with @e refs and its actions, from a short list of commands, each tool behind its permission.

Web data without a browser

When a page only needs reading, not driving, two packages fetch it in the provider's own browser and give back markdown, links, structured data or whole crawls, as permission-gated tools kept to origins like browsers are:

  • firecrawlTools in @mayurajs/browser-firecrawl: Firecrawl's scrape, map, search, crawl and extract.
  • quickActionTools in @mayurajs/cloudflare-quick-action: Cloudflare Browser Run's markdown, links, scrape, screenshot, JSON and crawl, the rendered page also kept to origins.

Writing a provider

A provider is { id, features: { liveView }, maxLifetimeMs, create(spec, { signal }) }. create starts a browser and returns { id, cdp: { url, headers }, liveViewUrl, isolate, release }: cdp.url is the browser's CDP WebSocket, release ends it (a BrowserError with reason gone counts as released), and isolate asks for a browser context of its own in a browser shared with others. browserHttpFailure(status) and browserResponseFailure(response) turn HTTP failures into a BrowserError without the provider's text.

browserConformance from mayura/browser/testing holds the cases every provider must pass, against the pages in browserFixturePages served on two origins — serveBrowserFixtures() from mayura/browser/local serves them on this machine.