Skip to main content

Browser Automation CLI

If you are an LLM using this tool, read SKILL.md: or run browser install skill to add it to Claude Code / Codex / OpenCode. (AGENTS.md is for agents developing this repo.)

A lightweight, self-hosted browser automation tool with a background daemon and CLI client. Enables authenticated web automation, screenshots, compact page snapshots, and page interactions via simple CLI commands. Share the SKILL.md file with your coding agent harness for seamless integration.

Why This Exists

Coding agents need to interact with authenticated web apps. Existing solutions all have tradeoffs:

  • Chrome DevTools MCP: requires Node.js, per-agent MCP server configuration, Google telemetry by default, and complex setup for each coding agent
  • BrowserMCP and similar tools: require installing Chrome extensions, tie into specific ecosystems, and use MCP which bloats the agent's context window with tool definitions and protocol overhead
  • Playwright/Puppeteer scripts: require writing code for every interaction, no persistent auth state
  • AI browser frameworks: heavy, opinionated, and framework-locked

Browser CLI solves this with a persistent daemon that any agent can call via subprocess. No extensions, no MCP config, no SDKs, no ecosystem lock-in. Sessions persist across agent calls (and daemon restarts) so you only log in once.

Benchmarks

On BU Bench V1 (browser-use's 100-task benchmark, opus-4-7, scored by their gemini-2.5-flash judge), Claude Code + browser-automation-cli tops every published harness × browser-tool configuration:

BU Bench V1 results

Methodology and per-task scores: benchmarks/busbench/.

Install

uv tool install browser-automation-cli
browser install

If commands are not found after install, add ~/.local/bin to your PATH:

export PATH="$HOME/.local/bin:$PATH"

Quick Start

1. Create a session (the daemon auto-starts)

browser create   # first command starts the daemon in the background (0.5.0+)

Nothing visible opens: the daemon runs Chromium headless. Manage it yourself with browser daemon (or the legacy browser-daemon alias) if you prefer; BROWSER_NO_AUTOSTART=1 disables auto-start.

2. Log in when a site needs it

browser create --show     # opens a window so you can log in; `browser <id> hide` afterwards

A session is an isolated browser profile (cookies, storage). Log into any sites you need while the window is shown; the agent drives it hidden afterwards. Sessions survive daemon restarts.

3. Run browser actions

browser <id> navigate https://github.com
browser <id> snapshot                      # interactive elements, one line each, with @refs
browser <id> click @e7                     # click by ref from the snapshot
browser <id> click --text "Sign in"        # or by visible text / role / label
browser <id> type --label "Username" octocat
browser <id> screenshot                    # JPEG saved under ~/.browser-daemon/shots/

A snapshot looks like this (Cloudflare login, 245 tokens):

url: https://dash.cloudflare.com/login
title: Cloudflare Dashboard | Manage Your Account
@e2 link "Sign up" href="/sign-up"
h1 "Sign in to Cloudflare"
@e3 button "Continue with Google"
@e7 textbox "Email"
@e8 textbox "Password" type="password"
@e10 checkbox "Save email and login method on this device"
@e11 button "Sign in" [disabled]

4. Manage sessions

browser list                  # JSON (--table for humans)
browser <id> show | hide      # move between a visible window and headless
browser <id> delete
browser shutdown              # stop the daemon; sessions are saved and restored next start

Commands Reference

Standalone (no daemon)

browser capture <url> [-f] [-o <path>]     # headless viewport screenshot (-f = full page)
browser install [--all]                    # download headless Chromium (Chrome for Testing, ~196 MB);
                                           # the headed build (~356 MB) downloads on first `show`, or now with --all
browser engine [auto|managed|system|<path>]  # use your installed Chrome/Edge/Brave instead (zero download);
                                           # auto = managed build if downloaded, else the system browser
browser profile [status]                   # by DEFAULT sessions use a persistent "default" profile:
                                           # the first session opens a window to sign in, every later
                                           # session reuses that login. Manage it with:
browser profile <name> | new <name>        #   switch to / create a named persistent profile
browser profile delete <name>              #   delete a profile + its logins (each is a full Chrome
                                           #   profile, ~100 MB+; delete ones you no longer need)
browser profile ephemeral                  #   make throwaway the default for new sessions
browser create --profile <name>            #   per-session: this session uses its own persistent login
browser create --ephemeral                 #   per-session: throwaway, isolated
browser <id> show | hide                   #   flip a profile visible<->headless seamlessly (sessions kept)
                                           #   (different --profile = concurrent & isolated; same = shared tabs)
browser cleanup                            # kill Chromium processes launched from Playwright's cache
browser install skill                      # install the agent skill into Claude Code / Codex / OpenCode

Sessions

Command Description
browser create [--show] New session; --show opens a window (for manual login)
browser list [--table] Sessions with state (active / frozen / hibernated) and visible
browser <id> show / hide Move the session to a visible window / back to headless (auth kept)
browser <id> delete Close session and forget its cookies
browser shutdown Stop the daemon gracefully
browser --version / browser update Show version; upgrade to the latest PyPI release. The daemon checks PyPI once a day and the CLI prints a one-line hint on stderr when a newer version exists (BROWSER_NO_UPDATE_CHECK=1 disables)

Page commands

All print JSON (snapshot prints text). Add -s / --snapshot to any action to get a fresh snapshot in the same call.

Command Description
navigate <url> [--wait load|domcontentloaded|networkidle] Returns as soon as the page is usable; never fails on a slow networkidle
snapshot [scope] [--all] [--max N] [--json] Visible interactive elements + headings. --all adds text blocks, --json gives boxes and unique selectors
click <target> [--double] Also click --at X,Y to click raw viewport pixels (canvas / vision, no DOM target; screenshot first, pixels map 1:1)
type <target> <text> [--sequential] [--submit] fill() by default; --sequential sends key events (autocomplete); --submit presses Enter
press <key> [target] Enter, Tab, Control+a, …
hover <target>
select <target> <value-or-label>
scroll [up|down] [px] / scroll <target>
text [selector] Readable text of the page or an element (cheap extraction)
wait [--text T | --selector S] [--gone] [--timeout ms]
screenshot [target] [-o path] [-f] [-q 70] JPEG; element screenshots via any target
eval <js> Evaluate an expression in the page
console [--clear] Buffered console messages
back / forward
batch JSON lines on stdin, run in one round-trip, stop at first failure

Targets: @e12 (ref from snapshot, preferred) · CSS selector · text=Create · role=button[name=Create] · label=Email · placeholder=Search · or flags --text / --role [--name] / --label / --placeholder. Ambiguous CSS selectors are refused (strict mode) instead of clicking the first match. For canvas / vision cases with no DOM target, click --at X,Y clicks raw viewport pixels (take a screenshot first; its pixels map 1:1 to click coordinates).


Architecture

  • Daemon (browser-daemon): Unix socket server (~/.browser-daemon/socket, mode 600) owning a headless Chromium, plus a headed one that exists only while some session is shown.
  • CLI (browser): a native Rust binary driving Chrome over raw CDP, ~2 ms per call, no Python/Node at runtime.
  • Sessions: one isolated browser context each. Hidden sessions are frozen after 10 s idle (script execution paused, ~3% CPU on animated dashboards; callbacks that fire while frozen are dropped, so BROWSER_FREEZE_AFTER=0 disables it) and hibernated to ~/.browser-daemon/sessions/<id>.json (cookies + storage + URL) after 10 min idle or on shutdown; they are rehydrated transparently on the next command. Tune with BROWSER_FREEZE_AFTER / BROWSER_HIBERNATE_AFTER (seconds).
  • Resource profile (M4, Cloudflare dashboard parked in a session): 2% CPU / 1.1 GB vs 264% CPU / 2.1 GB for v0.2. See benchmarks/ for methodology and measurements.

Anti-Detection

  • navigator.webdriver hidden via add_init_script
  • Desktop Chrome user agent derived from the actual Chromium version
  • 1280x800 viewport (desktop layouts; same size Anthropic/OpenAI computer-use tooling targets)

Output Format

Action responses:

{"success": true, "url": "https://github.com", "title": "GitHub"}

Errors (exit code 1):

{"success": false, "error": "ref @e9 is unknown or stale (page changed); run snapshot again"}

snapshot --json:

{"success": true, "url": "...", "title": "...", "scrollY": 0, "viewportHeight": 800, "viewportWidth": 1280, "documentHeight": 2400,
 "elements": [{"ref": "e3", "role": "button", "name": "Create", "pos": "", "box": [912, 640, 88, 36]}]}

Using with Coding Agents

Run browser install skill to install SKILL.md into Claude Code / Codex / OpenCode, or share the file with any other harness.

Implementation

Written entirely in Rust, one binary (browser, with a daemon subcommand) plus a browser-daemon compat shim, driving Chrome over raw CDP via a websocket. No Python, Playwright, or Node at runtime: browser install downloads the Chrome-for-Testing build directly, and capture is native too. ~2 ms per call, daemon RSS −90 MB vs the old Python build, frame- and shadow-DOM-aware snapshots. See the benchmarks/ directory for measurements. Since 0.4.0 the PyPI package ships these binaries under the same name (wheels for Linux x86_64/aarch64, macOS arm64/x86_64).

cargo build --release
./target/release/browser list          # the daemon auto-starts (`browser daemon` runs it in the foreground)

Development

cargo build --release
BROWSER_CLI=$PWD/target/release/browser BROWSER_DAEMON=$PWD/target/release/browser-daemon \
  python3 -m unittest -v tests/test_cli.py                        # end-to-end tests (needs python3 + a Chromium)
python3 benchmarks/performance-test/run.py <label>               # benchmark (latency, tokens, idle CPU/RSS)
python3 benchmarks/performance-test/compare.py baseline <label>

Troubleshooting

Symptom Fix
Command not found: browser export PATH="$HOME/.local/bin:$PATH"
Daemon not running Auto-start was disabled or failed, browser daemon &, check ~/.browser-daemon/daemon.log
Browser doesn't launch browser install
Session not found browser list
ref @eN is unknown or stale Page changed; run snapshot again
strict mode violation Selector matched several elements; use an @ref, --text, or a tighter selector
Stale Chromium processes browser cleanup

Installing the binaries

curl -fsSL https://raw.githubusercontent.com/jshan9078/browser-automation-cli/main/install.sh | sh

or simply uv tool install browser-automation-cli (the PyPI wheels are the Rust binaries). The script installs browser and browser-daemon into ~/.local/bin (set BROWSER_CLI_BIN to change); to build from source: cargo build --release.

Wheel: uvx maturin build --release → uv tool install target/wheels/browser_automation_cli-*.whl.

CI (.github/workflows/rust-wheels.yml) builds wheels for Linux x86_64/aarch64 and macOS arm64/x86_64 on every push touching the Rust sources, runs the end-to-end suite against the Rust daemon on Linux, attaches wheels to the release on v* tags, and publishes to PyPI when a PYPI_API_TOKEN repository secret exists. Windows is not a target: the daemon speaks over a Unix socket.

Metadata

Release files for browser-automation-cli 0.8.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for browser-automation-cli 0.8.3
File Size Uploaded
browser_automation_cli-0.8.3.tar.gz 414.0 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for browser-automation-cli 0.8.3
File
browser_automation_cli-0.8.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
browser_automation_cli-0.8.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
browser_automation_cli-0.8.3-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
browser_automation_cli-0.8.3-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details

Total release size: 7.1 MB

Release files / browser_automation_cli-0.8.3.tar.gz

Download URL browser_automation_cli-0.8.3.tar.gz
Size 414.0 kB
Tags Source
SHA-256 checksum
How to use checksums
91bceb9c9f619d8861ad1f1d170ad13cc040be41dd99ec83dd0ddd6699e15d0e
BLAKE2b-256 checksum
How to use checksums
51cb5d0a082f21e778240675f6a56f1c6e4f491b452377b27e79f807d7c2ce69
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / browser_automation_cli-0.8.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL browser_automation_cli-0.8.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.8 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
fa06d5bf324f087822ca4b08849b7d85a820cd51942bb77a11505dd72793d98c
BLAKE2b-256 checksum
How to use checksums
967847e136a8be156b305ba20e9699709327e2531d9e200a1a16667a4f5b2f7f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / browser_automation_cli-0.8.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL browser_automation_cli-0.8.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.7 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
b87972b029dcb748644344083cb5682f8ac13eb9160cf5651a2bf5383144a14e
BLAKE2b-256 checksum
How to use checksums
98e4b1813a384952fcab4b9751b379ab32d0ef4d21ea5750bd00737bd9b4d522
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / browser_automation_cli-0.8.3-py3-none-macosx_11_0_arm64.whl

Download URL browser_automation_cli-0.8.3-py3-none-macosx_11_0_arm64.whl
Size 1.6 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
3b23d3e894175112afc3dd092489edc66b78981a8532467dadeda881b4cbe308
BLAKE2b-256 checksum
How to use checksums
62674a4ec94a1bfdf5bbec48e648ac08ab0a1f40de99728b8a1c47e998560de8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / browser_automation_cli-0.8.3-py3-none-macosx_10_12_x86_64.whl

Download URL browser_automation_cli-0.8.3-py3-none-macosx_10_12_x86_64.whl
Size 1.7 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
c8e8e6156ec801cdb2bf0504e1e887b357fcd7b7368a67c820151bbecda84d3c
BLAKE2b-256 checksum
How to use checksums
2677cc25e2ee37eeb5d8cab16d3473bac337304577e607ccab1e835b6c5a9d8f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.8.4

5 release files

This release

0.8.3 This release

5 release files

0.8.2

5 release files

0.8.0

5 release files

0.7.2

5 release files

0.7.1

5 release files

0.7.0

5 release files

0.6.0

5 release files

0.5.1

5 release files

0.5.0

5 release files

0.4.1

5 release files

0.4.0

5 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

3 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page