Skip to main content

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. Start the daemon

browser create   # the daemon auto-starts on first use (0.5.0+)

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

2. Create a session

browser create            # headless session
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 it is shown; the agent can drive 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                            # download Chromium (~170 MB)
browser cleanup                            # kill Chromium processes launched from Playwright's cache

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 is shown.
  • 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=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 AGENTBENCH.md for the 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

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-daemon &      # drop-in for the Python daemon
./target/release/browser list

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 browser-daemon
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 --releaseuv 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

browser_automation_cli-0.5.0.tar.gz (56.6 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

browser_automation_cli-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

browser_automation_cli-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

browser_automation_cli-0.5.0-py3-none-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

browser_automation_cli-0.5.0-py3-none-macosx_10_12_x86_64.whl (1.7 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file browser_automation_cli-0.5.0.tar.gz.

File metadata

  • Download URL: browser_automation_cli-0.5.0.tar.gz
  • Upload date:
  • Size: 56.6 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

Hashes for browser_automation_cli-0.5.0.tar.gz
Algorithm Hash digest
SHA256 b22112999adc680bed79a92519826b5f81f16670f1335dfe71eaa3ff63096e1c
MD5 84d657b255e828ddd2f2e1b504291298
BLAKE2b-256 7c5028c275a83ff1e542b59ecbead522d5d051f013e364d91fc30bf032dd9d48

See more details on using hashes here.

File details

Details for the file browser_automation_cli-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: browser_automation_cli-0.5.0-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

Hashes for browser_automation_cli-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7e9e35d541a3c6342b37513243e6c0629e768dda42295ab6f82da747b1df3c78
MD5 6af4bba6643c3ea0f29423b9f3122d98
BLAKE2b-256 ecbd979184ee28f3a6982695bf280eac93a5ac55687c9b7d7971c8da33256be9

See more details on using hashes here.

File details

Details for the file browser_automation_cli-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

  • Download URL: browser_automation_cli-0.5.0-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

Hashes for browser_automation_cli-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 7ff61b8ad38906688696493cb0dc2bc0fedf8b3ad3c4a7b7a6c69d132eb77463
MD5 10f654a609cfa39abacb3b85545105bd
BLAKE2b-256 3af7d9f418f2dd4a8bdb18a42573e553a5d8943ca636f6683de8b396ee66c376

See more details on using hashes here.

File details

Details for the file browser_automation_cli-0.5.0-py3-none-macosx_11_0_arm64.whl.

File metadata

  • Download URL: browser_automation_cli-0.5.0-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

Hashes for browser_automation_cli-0.5.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 1ab88452b24399a817c0b656e3deb287c8f4ae686565e14165b4c863baf3c83b
MD5 dcefd906f6315664868f3f519300cfc2
BLAKE2b-256 fd1c58742add17c25c192c44c83bdcdec1b5a07242131bec422762152426af52

See more details on using hashes here.

File details

Details for the file browser_automation_cli-0.5.0-py3-none-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: browser_automation_cli-0.5.0-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

Hashes for browser_automation_cli-0.5.0-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 463ebfb3248524611b7a554c4ecce83fe01ec35f79eebbf82ec972ef951d1b45
MD5 b8ce3a82525cf0d8f2f8dbf3334c4dac
BLAKE2b-256 4b757ea0e675375deefcfa121a8fe673f202cfe7ba9aee808821b2a7432933f2

See more details on using hashes here.

Release history Release notifications | RSS feed

0.8.0

5 files

0.7.2

5 files

0.7.1

5 files

0.7.0

5 files

0.6.0

5 files

0.5.1

5 files

This release

0.5.0 This release

5 files

0.4.1

5 files

0.4.0

5 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

3 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page