Browser Automation CLI
If you are an LLM, see AGENTS.md for quick setup and usage instructions.
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.
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 cleanup # kill Chromium processes launched from Playwright's cache
browser docs [skill|agents] # print SKILL.md / AGENTS.md (shipped in the binary)
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] |
|
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.
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 isshown. - CLI (
browser): ~40 ms per call, no Playwright import on the daemon path. - 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=0disables 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 withBROWSER_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 AGENTBENCH.md for the measurements.
Anti-Detection
navigator.webdriverhidden viaadd_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
Share SKILL.md with your coding agent harness; see AGENTS.md for the integration guide.
Rust implementation (preview)
rust/ contains a Rust client and daemon with the same CLI and socket protocol — no Python, Playwright or
Node at all: browser install downloads the same Chrome-for-Testing build Playwright pins (into the same
cache, so both implementations share it), and capture is native too. Per-call overhead 40 ms → 2 ms,
daemon RSS −90 MB, frame- and shadow-DOM-aware snapshots. See AGENTBENCH.md. Published to PyPI as
0.4.0 under the same name (wheels for Linux x86_64/aarch64, macOS arm64/x86_64).
cd rust && cargo build --release
./target/release/browser list # the daemon auto-starts (`browser daemon` runs it in the foreground)
Development
uv sync
.venv/bin/python -m unittest -v tests/test_cli.py # end-to-end tests against scratch/bench/site
.venv/bin/python scratch/bench/run.py <label> # benchmark (latency, tokens, idle CPU/RSS)
.venv/bin/python scratch/bench/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 Rust binaries
curl -fsSL https://raw.githubusercontent.com/jshan9078/browser-automation-cli/main/rust/install.sh | sh
or simply uv tool install browser-automation-cli (0.4.0+ wheels are the Rust binaries). The script installs
browser and browser-daemon into ~/.local/bin (set BROWSER_CLI_BIN to change); other platforms: cd rust && cargo build --release.
Wheel (same PyPI project name, so download stats carry over): cd rust && uvx maturin build --release →
uv tool install rust/target/wheels/browser_automation_cli-*.whl. From 0.4.0 the PyPI package ships
the Rust binaries; the Python implementation remains in cli/ and daemon/ for reference and for
python -m daemon.server.
CI (.github/workflows/rust-wheels.yml) builds wheels for Linux x86_64/aarch64 and macOS arm64/x86_64 on every
push touching rust/, 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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file browser_automation_cli-0.5.1.tar.gz.
File metadata
- Download URL: browser_automation_cli-0.5.1.tar.gz
- Upload date:
- Size: 57.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
552edb6e9b18cd8e51cb98cdb4d088c8155804851b340a05619acb1b6e1bb956
|
|
| MD5 |
bed1453308fcc319aca91267b38ce64a
|
|
| BLAKE2b-256 |
9e9833944e5e06e4344eb91bc7e2907115150dc9f7d260c12c3ae0985f0ac655
|
File details
Details for the file browser_automation_cli-0.5.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: browser_automation_cli-0.5.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.7 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8e5eefa81dd18818b3f43d88afc9fe1d13146d4a3629eac0217b2381df70cfb
|
|
| MD5 |
8ff649381bdab636b6d99c683aec4053
|
|
| BLAKE2b-256 |
d3e7358fc4454efcadac1c94afa70405feed350d5788e0096cf077f4c536097b
|
File details
Details for the file browser_automation_cli-0.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: browser_automation_cli-0.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 1.6 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c610608af61c93f75b356f77565a6c174a0f426b5882892fc155f074bf02ccc9
|
|
| MD5 |
c5621fda60ce29aaa47100684da3200a
|
|
| BLAKE2b-256 |
6e107cafc38d15ffb36d5e22e306193489237a9b77b43fddda1a46aaab399bc2
|
File details
Details for the file browser_automation_cli-0.5.1-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: browser_automation_cli-0.5.1-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.5 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
925ee2b9a604349fe03edd16dc278b7b9f92eda21d12801305909bc6eb7a7091
|
|
| MD5 |
5fa5b7000bc9aa57bdf1fc04be819c41
|
|
| BLAKE2b-256 |
ecfeb282de21a3f8aba21c12780f5c84c39b5c75fb497170f3d46d554c6c389f
|
File details
Details for the file browser_automation_cli-0.5.1-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: browser_automation_cli-0.5.1-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 1.7 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0ea6cc564c772606413b70b61b7f39093c11649d495a176047e3cda43d1e187
|
|
| MD5 |
7ea543af4c8126a6773dff29057ba990
|
|
| BLAKE2b-256 |
d02e7a0566e602a33658cb72c6ea90608af6540858bcb0f39957214ddcd5dac7
|