cd /news/ai-agents/browser-cli-for-agents Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-43468] src=github.com β†— pub= topic=ai-agents verified=true sentiment=↑ positive

Browser CLI for Agents

A new open-source Playwright CLI tool called 'brow' achieves 82% task success at approximately $0.22 per task, outperforming browser-use, playwright-mcp, and agent-browser on 22 benchmark tasks. The tool provides a standalone Chromium instance with an agent-friendly API, persistent profiles, and structured commands for common browser actions.

read5 min views1 publishedJun 29, 2026
Browser CLI for Agents
Image: source

The browser tool agents win with: 82% task success at ~$0.22/task β€” beating browser-use, playwright-mcp, and agent-browser on 22 benchmark tasks.

A standalone Playwright CLI that gives your agent a real Chromium instance with an agent-friendly API β€” structured commands for common actions, plus an eval

escape hatch for full power.

Homebrew:

brew tap detrin/tap
brew install brow

pip:

pip install brow-cli

Then install Chromium once (either method above):

brow setup            # ~150MB, one-time

Agent skill:

npx -y skills add detrin/brow

git clone https://github.com/detrin/brow.git
ln -s "$(pwd)/brow/skills/brow" ~/.opencode/skills/brow   # OpenCode

A real use case: use your Google account to search Maps in a city you've never visited, and extract structured results.

Open a headed browser with a persistent profile and sign in manually:

brow session new --profile personal --headed
brow navigate -s 1 "https://accounts.google.com"
brow session delete 1

Your login is saved in ~/.brow/profiles/personal/

-you won't need to sign in again.

Paste this into Claude Code:

Open a brow session with my personal profile, go to Google Maps, and search for bars near Times Square in New York. Return the names, Google Maps URLs, ratings, and number of reviews in a markdown table.

Claude Code runs:

brow session new --profile personal --headed    # β†’ 1 (already logged in)
brow navigate -s 1 "https://www.google.com/maps/search/bars+near+Times+Square+New+York"
brow screenshot -s 1
brow eval -s 1 "
results = await page.evaluate('''() => {
    const items = document.querySelectorAll('div.Nv2PK');
    return Array.from(items).slice(0, 8).map(el => {
        const name = el.querySelector('.fontHeadlineSmall, .qBF1Pd');
        const rating = el.querySelector('.MW4etd');
        const reviews = el.querySelector('.UY7F9');
        const link = el.querySelector('a[href*=\"/maps/place\"]');
        return {
            name: name?.innerText || '',
            rating: rating?.innerText || '',
            reviews: reviews?.innerText.replace(/[()]/g, '') || '',
            url: link?.href || ''
        };
    });
}''')
import json
result = json.dumps(results, indent=2)
"
brow session delete 1
Bar Rating Reviews Link
The Riff Raff Club 4.4 60

MapsMapsMapsMapsMapsBecause the google

profile persists your login, you get personalized results -no cookie banners, no sign-in walls, just data.

22 tasks total (16 fixture + 6 new), Claude Sonnet via AWS Bedrock. Compared against playwright-cli, MCP Playwright, agent-browser (Rust/CDP), and browser-use (full-stack agent framework).

| Metric | brow | agent-browser | browser-use | playwright-cli | MCP Playwright | |---|---|---|---|---|---| | Success rate (16 fixture) | 88% (14/16) | 63% (10/16) | 63% (10/16) | 50% (8/16) | 44% (7/16) | | Success rate (22 total) | 82% (18/22) | 64% (14/22) | 64% (14/22) | 55% (12/22) | 36% (8/22) | | Avg tokens/task (16 fixture) | 68K | 73K | 75K | 113K | 118K | | Avg tokens/task (22 total) | 88K | 69K | 81K | 96K | 132K | | Avg tool calls | 9.6 | 11.2 | 5.8 | 9.6 | 11.6 | | Avg wall-clock (fixture) | 41s | 36s | 73s | 44s | 50s | | Est. cost/task | $0.22 | $0.23 | $0.27 | $0.35 | $0.37 |

brow leads on success rate across both suites. On token efficiency, brow leads the 16-task fixture suite (68K avg) but agent-browser is most efficient across all 22 tasks (69K avg) β€” brow's average is inflated by one live task (github-trending-python: 383K tokens, agent didn't use snapshot filtering). browser-use runs its own agent loop β€” included for completeness.

Per-task success grid, token breakdown, and analysis: benchmarks/README.md

brow daemon start [--port 19987]
brow daemon stop
brow daemon status
brow session new [--profile <name>] [--headed]
brow session list
brow session delete <id>
brow -s <id> navigate <url>
brow -s <id> wait <selector>
brow -s <id> wait --load
brow -s <id> snapshot [--search <regex>] [--locator <selector>]
brow -s <id> screenshot [--full] [--path <file>]
brow -s <id> html [--locator <selector>] [--search <regex>]
brow -s <id> logs [--search <regex>] [--count <n>]
brow -s <id> url
brow -s <id> click <selector>
brow -s <id> fill <selector> <value>
brow -s <id> type <text>
brow -s <id> key <key>            # Enter, Tab, Meta+a
brow -s <id> hover <selector>
brow -s <id> scroll <pixels>
brow -s <id> scroll-to <selector>
brow -s <id> drag <from> <to>
brow -s <id> upload <selector> <filepath>
brow -s <id> page list
brow -s <id> page new [url]
brow -s <id> page close [index]
brow -s <id> page switch <index>
brow profile list
brow profile delete <name>
brow state save <name> -s <id>
brow state restore <name> -s <id>
brow state list
brow -s <id> eval <code>

Variables available in eval: page

, context

, browser

, state

, pages

.

Playwright selector syntax:

  • CSS: button.submit

,#login

  • Text: text=Login

  • Role: role=button[name="Save"]

  • XPath: xpath=//div

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Agent (Claude Code, script, etc.)                              β”‚
  β”‚                                                                 β”‚
  β”‚  brow session new --headed          ← start browser             β”‚
  β”‚  brow navigate -s 1 "https://..."   ← go to page               β”‚
  β”‚  brow snapshot -s 1                 ← read page (a11y tree)     β”‚
  β”‚  brow click -s 1 "text=Login"       ← interact                  β”‚
  β”‚  brow fill -s 1 "#email" "me@..."   ← fill form                 β”‚
  β”‚  brow screenshot -s 1               ← capture screen            β”‚
  β”‚  brow eval -s 1 "await page..."     ← escape hatch              β”‚
  β”‚  brow session delete 1              ← cleanup                   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚ HTTP (localhost:19987)
                 β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  brow daemon (FastAPI + uvicorn)     β”‚
  β”‚                                      β”‚
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
  β”‚  β”‚ Session 1 β”‚  β”‚ ProfileManager   β”‚  β”‚
  β”‚  β”‚ (browser) β”‚  β”‚ ~/.brow/profiles β”‚  β”‚
  β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
  β”‚  β”‚ Session 2 β”‚                       β”‚
  β”‚  β”‚ (browser) β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚ StateManager     β”‚  β”‚
  β”‚                 β”‚ ~/.brow/states   β”‚  β”‚
  β”‚                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚ CDP (Chrome DevTools Protocol)
                 β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Chromium (via Playwright)           β”‚
  β”‚                                      β”‚
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
  β”‚  β”‚ Page 1 β”‚ β”‚ Page 2 β”‚ β”‚ Page 3 β”‚   β”‚
  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • Daemon auto-starts on first brow

command - Persistent Chromium profiles for login session survival

  • One browser per session, full isolation
  • Headless by default, --headed

to watch

Variable Default Description
BROW_HOME
~/.brow
Data directory
BROW_PORT
19987
Daemon port
BROW_MAX_SESSIONS
10
Max concurrent sessions

~150-300MB per Chromium instance. 10 sessions = ~2-3GB.

MIT

── more in #ai-agents 4 stories Β· sorted by recency
── more on @brow 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/browser-cli-for-agen…] indexed:0 read:5min 2026-06-29 Β· β€”