Skip to content

Latest commit

 

History

42 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

br: a browser CLI built for agents

CI License Zig Bun Platform Status


br turns a web page into a compact, semantic snapshot with stable @refs, then lets you (or an agent) act on those refs. No Playwright, no Selenium, no huge DOM dumps. Just a tiny command surface built for minimal context.

$ br open https://example.com
$ br snap --compact

Example Domain | https://example.com/
@1 link  More information...

That's the whole model:

browser  →  snapshot  →  @refs  →  actions

Demos

Live website(yeswehack.com) rendering inside terminal

Screendrop_2026-08-28-16-59-38_46FD44.mp4

Open, snapshot, click, capture:

demo-open-snapshot-click.mp4

Live page snapshot:

demo-live-snapshot.mp4

Form automation:

demo-form-automation.mp4

Batch mode:

demo-batch-mode.mp4

Why br?

  • Small by design. Not a terminal browser or a full automation framework, a thin agent interface. Zig owns the CLI and protocol; Bun's WebView owns the browser.
  • Stable @refs. Every interactive element gets a short, reusable handle. If one goes stale, br fails loudly with STALE_REF instead of clicking the wrong thing.
  • Batch-native. Multi-step flows run as JSONL in one shot, so agents do more with less back-and-forth.
  • Visual when you want it. br view and br live render the real page inline via the Kitty graphics protocol.

Usage

If it renders in a browser, br turns it into commands: same page, whether you're an agent, a human, or hunting bugs.

🤖 As an AI agent: a browser without the DOM firehose

Give your agent a browser without drowning it in DOM. It reads a compact snapshot, acts on @refs, and branches on exit codes. No Playwright, no flaky selectors, no 50k-token HTML dumps.

br batch <<'JSONL'
{"command":"open","url":"https://app.example.com/login"}
{"command":"snapshot","compact":true}
{"command":"fill","target":"@2","text":"user@example.com"}
{"command":"fill","target":"@3","text":"hunter2"}
{"command":"click","target":"@4"}
{"command":"snapshot","compact":true}
JSONL

One JSON line in, one out. Stable handles, deterministic exit codes (STALE_REF, TIMEOUT, …), and --json on everything: everything an agent needs to loop reliably.

🧑‍💻 As a human: a browser you can pipe

Inspect a page, fill a form, grab a screenshot, or read it inline, without leaving the terminal or writing a script.

br open https://news.ycombinator.com
br snap --compact            # scan the interactive elements
br find "login"              # locate a control by text
br screenshot shot.png       # grab evidence
br view                      # render the viewport inline (Kitty graphics)
br live https://github.com   # full interactive terminal browser

Great for quick checks, demos, scraping one page, or driving a site from a shell script.

🛡️ For bug bounty & recon: scriptable, pipeable triage

A scriptable, headful browser is a fast triage tool: map inputs, read client-side state, and run JS in page context, all pipeable into your recon pipeline. On targets you're authorized to test.

br --profile target open https://app.example.com/account
br snap                                              # every interactive element + its attrs
br eval 'document.cookie'                            # inspect session/CSRF state
br eval '[...document.querySelectorAll("input[type=hidden]")].map(i => [i.name, i.value])'
br attr @5 href                                      # pull hrefs, tokens, data-* attrs
br cookies                                           # dump cookies
br console                                            # surface client-side JS errors
br --backend chrome cdp Network.enable               # Chrome DevTools Protocol (Chromium only)
br screenshot finding.png                            # capture proof for the report

Keep separate authenticated contexts with --profile, script repeatable checks across params/endpoints with batch, and drive it all from the same tools as the rest of your recon.

cdp needs the Chromium backend. Pick it per command with --backend chrome (the default webkit backend has no CDP). See Backends.

Install

Download a release

Grab the tarball for your platform from Releases. Each one bundles br, a matching Bun, and the worker/ scripts, so it runs on a fresh machine with nothing else to install. Untar and run: br finds its Bun and worker beside itself.

tar xzf br-<version>-<target>.tar.gz        # e.g. br-0.1.0-aarch64-macos.tar.gz
./br-<version>-<target>/br open https://example.com

Important

macOS: the binary is ad-hoc signed but not notarized, so Gatekeeper blocks the first run ("Apple could not verify 'br' is free of malware…"). Clear the download quarantine once and it runs normally:

xattr -dr com.apple.quarantine ./br-<version>-<target>
# e.g. xattr -dr com.apple.quarantine ./br-0.1.0-aarch64-macos

Build from source

You'll need Zig 0.16+ and Bun 1.4+ (with Bun.WebView: WebKit by default, Chromium via --backend chrome).

# with Nix (recommended)
nix develop
zig build
export BR_BUN="$(command -v bun)"
./zig-out/bin/br open https://example.com

# or build the packaged version
nix build
./result/bin/br open https://example.com

Tip

Using a downloaded Bun? Point br at it with export BR_BUN=/path/to/bun. For packaged installs, set BR_WORKER_DIR to the installed worker/ directory.

Quickstart

Log in to a page in five commands:

br open https://example.com/login
br snap                      # list the interactive elements + their @refs

br fill @2 "user@example.com"
br fill @3 "$PASSWORD"
br click @4

br snap --compact            # see where you landed

Important

@refs only exist after a snap. If a page changes under you, an old ref goes stale, br fails loudly instead of clicking the wrong thing. Just snapshot again and use the fresh ref:

STALE_REF @4

Batch mode

For anything multi-step, prefer batch: one JSON object in per line, one out. It never runs shell commands.

br batch <<'JSONL'
{"command":"open","url":"https://example.com"}
{"command":"snapshot","compact":true}
{"command":"click","target":"@1"}
{"command":"snapshot","compact":true}
JSONL

Recipes

Use record when a human should teach br a workflow once, then replay it later from an agent, script, or shell.

br record yeswehack-login https://yeswehack.com/login --pane
br recipes
br show yeswehack-login
br replay yeswehack-login --pause-on-secret --pause-on-fail
br patch yeswehack-login
br export yeswehack-login --jsonl
br recipes delete yeswehack-login
br recipes delete --all

Recipes are stored locally as JSONL in ~/.local/share/br/recipes. A recorded live session captures opens, clicks, typed input, key presses, and scrolls exactly as entered, including login values. Add --pane to record or patch to show the live JSONL log inside the terminal while browsing. Clicks include a selector plus coordinate fallback so replay can use the DOM when possible and still preserve the original gesture.

Common commands

Command What it does
open <url> Navigate to a page
snap [--compact] Semantic snapshot with @refs
click <ref|selector> Click an element
fill <ref> <text> Focus, clear, and type into a field
type <text> / press <key> Send keystrokes
find <text> Search the current refs
get <ref> Inspect a single element
screenshot [path] Save a PNG
view Render the viewport inline (Kitty graphics)
live [url] Interactive terminal browser (for humans)
record <name> [url] Record a live workflow as a local recipe
replay <name> Replay a saved recipe
recipes / show <name> List or inspect saved recipes
recipes delete <name> Delete one saved recipe
recipes delete --all Delete all saved recipes
recipes clear --yes Delete all saved recipes
patch <name> [url] Append repaired live actions to a recipe
export <name> --jsonl Print a recipe for agents or scripts

Tip

In br live, press a to AI-answer the current page: multiple choice and free text. br collects the clickable options and the writable fields, hands them plus the page prose to a solver command, then clicks the right options and writes the essay/short answers it gets back. The solver is $BR_SOLVER (default claude -p); it receives the page on stdin and returns a JSON plan {"clicks":["@2"],"fills":[{"ref":"#0","text":"..."}]}. Tune it with BR_SOLVER="claude -p --model haiku" or BR_SOLVER_TIMEOUT=60.

Full command reference
open <url>
snap | snapshot [--compact]
click <ref|selector>          fill <ref|selector> <text>
type <text>                   press <key>              hover <ref|selector>
text [ref|selector]           html [ref|selector]      get <ref|selector>
attr <ref|selector> <attr>    value <ref|selector>     find <text>
scroll <amount>               scroll-to <ref|selector>
url                           title                    back | forward | reload
wait <selector|ms>            eval <javascript>
screenshot [path] [--format png|jpeg|webp] [--quality 0-100]
view                          resize <width> <height>  cookies    console
close

# recipes
record <name> [url] [--refresh ms] [--pane]
replay <name> [--pause-on-secret] [--pause-on-fail]
recipes
recipes delete <name>
recipes delete --all
recipes clear --yes
show <name>
patch <name> [url] [--refresh ms] [--pane]
export <name> --jsonl

# admin
session list | close <name> | close-all
daemon status | stop

Every meaningful command supports --json (stdout is pure JSON; diagnostics go to stderr).

Sessions & profiles

The default session is default. Name others to keep separate browser contexts:

br --session github open https://github.com
br --session github snap
br session list
br session close github

Profiles persist browser state across runs (stored under ~/.local/share/br/profiles/):

br --profile github open https://github.com

Session and profile names are limited to [A-Za-z0-9_.-].

Backends

Bun.WebView can drive more than one engine. Pick one per command with --backend:

br --backend chrome open https://example.com   # Chromium
br --backend webkit open https://example.com   # WebKit (default)
Backend Notes
webkit Default. Available everywhere Bun.WebView is (macOS WebKit today).
chrome Chromium engine, required for cdp (Chrome DevTools Protocol).

The backend is fixed when a session's browser is first created, so set it on your first command in that session (e.g. the open).

How it works

agent / human  →  br CLI (Zig)  →  JSONL over a Unix socket  →  Bun worker  →  Bun.WebView

br starts a persistent Bun worker automatically and talks to it over a Unix socket under $XDG_RUNTIME_DIR/br/. See .github/ARCHITECTURE.md and .github/AGENTS.md for the details.

Exit codes
Code Meaning Code Meaning
0 success 13 stale ref
2 invalid arguments 14 timeout
10 browser unavailable 15 evaluation failed
11 navigation failed 16 protocol error
12 element not found 70 internal error

Status

Experimental and early, but usable for local work.

Warning

The browser backend targets Bun's experimental Bun.WebView API, so behavior can shift as Bun evolves. br live is a human debugging mode, not agent context.

Development

br is Zig (CLI + protocol) plus a Bun worker (the browser). A justfile wraps the common tasks. Run just with no args to list them:

just recipes
Recipe What it does
just build Release binary → zig-out/bin/br
just debug Debug build
just run <args> Run br, e.g. just run open github.com
just test Run the test suite
just fmt Format Zig + TypeScript
just fmt-check Check formatting (what CI runs)
just check fmt-check + build + test
just clean Remove build artifacts

Note

All paths need Zig 0.16+ and Bun 1.4+. Pick whichever setup fits you.

With Nix + just: recommended, zero manual installs
nix develop            # drops you in a shell with Zig + a repo-local Bun on PATH
just                   # list recipes
just check             # fmt-check, build, test
just run open github.com

The devShell auto-adds a repo-local Bun from .tools/<platform>/ to PATH if one is present; otherwise point BR_BUN at your Bun.

With just, without Nix: you bring Zig + Bun

Install the tools yourself, then let just drive the rest:

# just:  https://github.com/casey/just
# zig 0.16+:  https://ziglang.org/download
# bun 1.4+:
curl -fsSL https://bun.sh/install | bash
export BR_BUN="$(command -v bun)"

just check
just run open github.com
Raw: no just, download Bun manually

Just Zig and a Bun binary; call the underlying commands directly:

# 1. Bun 1.4+ (any location works, just tell br where it is)
curl -fsSL https://bun.sh/install | bash
export BR_BUN="$HOME/.bun/bin/bun"

# 2. build / test / format
zig build -Doptimize=ReleaseSafe
zig build test
zig fmt src build.zig
"$BR_BUN" x prettier --write "worker/**/*.ts"

# 3. run
./zig-out/bin/br open https://example.com

License

MIT

Copyright © 2026 - present pwnwriter

About

🕷️Browser CLI built for agents, powered by Bun WebView with Kitty graphics rendering

Topics

Resources

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages