npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

remote-agent-browser

v1.2.0

Published

Run agent-browser in a Vercel Sandbox created from a browser-ready container image

Downloads

5,976

Readme

remote-agent-browser

Run agent-browser in the cloud with an isolated Vercel Sandbox.

Install

pnpm add remote-agent-browser

Use

AgentBrowser.create() starts a fresh Vercel Sandbox from the prebuilt browser image. Commands in one client share the same page, cookies, tabs, and element references. close() closes Chromium and stops the Sandbox.

Examples

Authentication

Authentication is automatic when running on Vercel through VERCEL_OIDC_TOKEN.

For local development, link a Vercel project and pull its environment:

vercel link
vercel env pull .env.local
node --env-file=.env.local examples/screenshot/index.mjs

API

AgentBrowser.create()

Starts a fresh browser in a disposable Vercel Sandbox. Always call browser.close() when finished.

Pass agent-browser global options with args. They are placed before every command, which is required for launch settings such as color scheme, profiles, and init scripts:

const browser = await AgentBrowser.create({
  args: ['--color-scheme', 'dark', '--enable', 'react-devtools'],
})

Proxy

Set a proxy for the browser client's lifetime with proxy. Use the object form to configure hosts that should connect directly:

const browser = await AgentBrowser.create({
  proxy: {
    url: 'http://user:[email protected]:8080',
    bypass: ['localhost', '*.internal.example.com'],
  },
})

For a proxy without bypass rules, pass its URL directly:

const browser = await AgentBrowser.create({
  proxy: 'http://proxy.example.com:8080',
})

browser.run(commands)

Run several agent-browser commands in the same session:

const result = await browser.run([
  ['open', 'https://my-preview.vercel.app'],
  ['wait', '--load', 'networkidle'],
  ['snapshot', '-i', '--json'],
  ['click', '@e3'],
])

browser.exec(command, options?)

Run one command with arguments and flags:

await browser.exec('find', {
  args: ['role', 'button', 'click'],
  flags: { name: 'Submit' },
})

browser.shell(command, options?)

Use this when forwarding a Bash-tool command or composing agent-browser with normal shell utilities. The command runs verbatim, so quoting, pipes, redirection, and control operators keep their shell semantics. Prefer exec() or run() for browser commands that do not need a shell. Every agent-browser invocation inherits the client's CLI session through AGENT_BROWSER_SESSION.

const result = await browser.shell(
  'agent-browser read "https://example.com" | grep -io "example" | wc -l',
)
console.log(result.stdout)

Client-wide args are not inserted into a shell string because doing so would require rewriting arbitrary shell syntax. Put global CLI arguments directly in the command when using shell().

Typed JSON

Set output: 'json' for commands that support --json. exec<T>() adds the flag, unwraps the CLI response envelope, and keeps the normal command fields alongside the typed data value:

type UrlResult = { url: string }

const result = await browser.exec<UrlResult>('get', {
  args: ['url'],
  output: 'json',
})
console.log(result.data.url, result.ok)

File transfer

Upload local buffers through a page's file input, or collect a browser download without exposing the Sandbox filesystem:

await browser.upload('#avatar', [
  { name: 'avatar.png', bytes: await readFile('avatar.png') },
])

const { file } = await browser.download('#export', { filename: 'report.csv' })
await writeFile('report.csv', file.bytes)

When no output path is supplied, other file-producing commands collect their artifact in result.file. This includes screenshots, PDFs, traces, profiles, HAR files, saved browser state, and recordings:

await browser.exec('network', { args: ['har', 'start'] })
// ...interact with the page...
const result = await browser.exec('network', { args: ['har', 'stop'] })
await writeFile('capture.har', result.file.bytes)

When a command already contains an explicit remote output path, read it back without changing the command:

await browser.shell(
  'agent-browser screenshot /tmp/verification.png',
)
const file = await browser.readFile('/tmp/verification.png', 'image/png')
await writeFile('verification.png', file.bytes)

Convenience methods

  • browser.snapshot(url?) optionally opens a page, then returns its interactive snapshot. Omit the URL to inspect the current page.
  • browser.screenshot(url?, { fullPage: true }) optionally opens a page, then returns a PNG buffer. Pass the options object first to capture the current page.
  • browser.close() closes the session and stops the Sandbox.

All methods use the same disposable browser session until close() is called.

Stable browser ids

Use AgentBrowser.session() when browser identity must survive process boundaries. It returns a lazy handle: constructing it or starting keepalive does not create a Sandbox. The first browser command finds or creates a runtime derived from the caller-defined id, and another process using the same id finds that runtime again.

const browser = AgentBrowser.session({ id: `environment:${chatId}` })
const stopKeepalive = browser.keepalive()
try {
  await browser.exec('open', { args: ['https://example.com'] })
} finally {
  stopKeepalive()
}

Ids are project-scoped. Include the deployment environment or another namespace when the same application uses one Vercel project for multiple environments. The underlying Sandbox name is private and derived from a hash of the id.

If an expired Sandbox resumes, its Chromium process starts fresh. Subscribe to reset before running commands when the caller needs to surface that page, cookies, refs, tabs, and console history were lost:

browser.on('reset', ({ reason }) => {
  console.log(`Browser runtime reset: ${reason}`)
})

browser.destroy() permanently removes the named runtime. Stopping keepalive only allows its normal idle timeout to resume.

Keepalive

Keep the Sandbox alive across idle gaps during long-running work. Always stop the heartbeat in a finally block; after it stops, the Sandbox expires at its normal timeout unless it is closed earlier:

const stopKeepalive = browser.keepalive()
try {
  await runLongAgentTurn(browser)
} finally {
  stopKeepalive()
}

By default, each heartbeat restores the wall-clock timeout configured by AgentBrowser.create() and runs halfway through that window, capped at five minutes. Override either value with timeoutMs and intervalMs. Renewal is best-effort; pass onError to observe failures.

Container image and development details are in docs.md.