mini-browser (mb) — Browser CLI for Agents
mb is a browser CLI where each command is a small Unix tool. It talks to Chrome over CDP (port 9222) via puppeteer-core.
Setup (only if not already available)
Setup is only needed when mb is not installed or Chrome is not reachable. Run these checks first — if both pass, skip straight to the Command Reference.
Check if ready
# 1. Is mb installed?
which mb && echo "mb: ok" || echo "mb: MISSING"
# 2. Is Chrome listening on CDP?
curl -sf http://127.0.0.1:9222/json/version > /dev/null && echo "chrome: ok" || echo "chrome: NOT RUNNING"
If both print "ok", everything is ready — go use mb commands directly.
Install (only if mb is missing)
npm install -g @runablehq/mini-browser
Start Chrome (only if not running)
mb-start-chrome
This launches Chrome with --remote-debugging-port=9222, a fresh profile, and a 1024×768 window. It no-ops if Chrome is already running.
To kill and relaunch:
mb-restart-chrome
Verify
mb go "https://example.com" && mb text
Environment Variables
| Variable | Default | Description |
|---|
CHROME_PORT | 9222 | CDP port |
CHROME_BIN | auto-detected | Path to Chrome/Chromium binary |
CHROME_PID_FILE | <scripts>/.chrome-pid | PID file location |
CHROME_USER_DATA_DIR | <scripts>/.chrome-profile | Chrome profile directory |
Command Reference
Navigation
| Command | Description |
|---|
mb go <url> | Navigate to URL (waits for networkidle) |
mb url | Print current URL |
mb back | Go back |
mb forward | Go forward |
Observation
| Command | Description |
|---|
mb text [selector] | Visible text content (default: body) |
mb shot [file] | Screenshot to PNG (default: ./shot.png) |
mb snap | List interactive elements with coordinates |
Interaction
| Command | Description |
|---|
mb click <x> <y> | Click at coordinates |
mb type [x y] <text> | Type text (with coords: selects first) |
mb fill <k=v...> | Fill form fields by label/name/placeholder |
mb key <key...> | Press keys (Enter, Tab, Meta+a) |
mb move <x> <y> | Hover at coordinates |
mb drag <x1> <y1> <x2> <y2> | Drag between points |
mb scroll [dir] [px] | Scroll (default: down 500) |
Recording
| Command | Description |
|---|
mb record start <file> | Start recording (.webm, .mp4, .gif) |
mb record stop | Stop recording and save |
mb record status | Check if recording is active |
Tabs
| Command | Description |
|---|
mb tab list | List open tabs |
mb tab new [url] | Open new tab, print index |
mb tab close [n] | Close tab (default: last) |
Other
| Command | Description |
|---|
mb js <code> | Run JavaScript in page context |
mb wait <target> | Wait for ms / selector / networkidle / url:pattern |
mb audit | Design audit (palette, typography, contrast, a11y, SEO) |
mb logs | Stream console logs (Ctrl+C to stop) |
Flags
| Flag | Default | Description |
|---|
--timeout <ms> | 30000 | Command timeout |
--tab <n> | 0 | Target tab index |
--json | false | Structured JSON output |
--right | false | Right-click |
--double | false | Double-click |
--fps <n> | 30 | Recording frame rate |
--scale <n> | 1 | Recording scale factor |
Usage Patterns
Observe → Act loop
The standard agent loop: snapshot the page, pick an element, act on it.
mb snap # list interactive elements with (x, y)
mb click 512 380 # click the button at those coordinates
mb wait networkidle # wait for the page to settle
mb snap # observe again
Fill and submit a form
mb go "https://example.com/login"
mb fill "Email=user@example.com" "Password=hunter2"
mb key Enter
mb wait url:/dashboard
Take a screenshot
mb shot page.png
mb shot page.png --width 1440 --height 900
Extract text
mb text "main" # text from <main>
mb text "#content" # text from #content
mb text # full body text
Run JavaScript
mb js 'document.title'
echo 'document.querySelectorAll("a").length' | mb js -
Record a screencast
mb record start demo.mp4 --fps 30 --scale 1
# ... interact with the page ...
mb record stop
Design audit
mb audit # human-readable report
mb audit --json # structured JSON output
Dismiss overlays
Cookie banners and modals block clicks. Remove them with JS:
mb js 'document.querySelector("[class*=cookie]")?.remove()'
Wait strategies
mb wait 2000 # sleep 2 seconds
mb wait ".modal" # wait for selector to appear
mb wait networkidle # wait for no network activity
mb wait url:/dashboard # wait for URL to contain string
Important Notes
- Viewport is 1024×768.
snap only returns elements in the current viewport — scroll and snap again to find more. text uses querySelector — returns first match only. Use text "main" over text "p" for better results.go waits for networkidle. For heavy SPAs, follow up with wait ".selector".type with coordinates triple-clicks first to select existing text, then types the replacement.fill field matching order: aria-label → placeholder → name attr → id → label text → CSS selector (use #/./[ prefix).--json output: snap → [{role, name, x, y, state}], tab list → [{index, url, title}], logs → JSON lines, audit → full audit object.- Recording state is stored in
~/.mb-recorder.json. Only one recording at a time. tab close cannot close the last remaining tab.
Troubleshooting
| Problem | Fix |
|---|
| "Chrome not found" | Set CHROME_BIN=/path/to/chrome |
| Connection refused | Run mb-start-chrome first |
| Stale recording state | Delete ~/.mb-recorder.json |
| Chrome window wrong size | mb-restart-chrome (creates fresh profile) |
| Element not in snap output | mb scroll down 500 then mb snap again |