| name | browser |
| description | Browser automation via agent-browser CLI. Use when you need to navigate websites, verify deployed UI, test web apps, read online documentation, scrape data, fill forms, capture baseline screenshots before design work, or inspect current page state. Triggers on "check the page", "verify UI", "test the site", "read docs at", "look up API", "visit URL", "browse", "screenshot", "scrape", "e2e test", "login flow", "capture baseline", "see how it looks", "inspect current", "before redesign". |
Browser Automation
Browser automation via Vercel's agent-browser CLI. Runs headless by default; use --headed for visible window. Uses ref-based selection (@e1, @e2) from accessibility snapshots.
Setup & Version Check
command -v agent-browser >/dev/null 2>&1 && agent-browser --version || echo "MISSING: npm i -g agent-browser && agent-browser install"
Always run the version check at the start of a browser session. agent-browser iterates quickly — check for updates if the version is more than a week old:
npm view agent-browser version
Core Workflow
- Open URL
- Snapshot to get refs
- Interact via refs
- Re-snapshot after DOM changes
agent-browser open https://example.com
agent-browser snapshot -i
agent-browser click @e1
agent-browser wait --load networkidle
agent-browser snapshot -i
Command Chaining
Commands can be chained with && in a single shell invocation. The browser persists between commands via a background daemon, so chaining is safe and more efficient than separate calls.
agent-browser open https://example.com && agent-browser wait --load networkidle && agent-browser snapshot -i
agent-browser fill @e1 "user@example.com" && agent-browser fill @e2 "password123" && agent-browser click @e3
When to chain: Use && when you don't need intermediate output (e.g., open + wait + screenshot). Run separately when you need to parse output first (e.g., snapshot to discover refs, then interact).
Essential Commands
Navigation
agent-browser open <url>
agent-browser back
agent-browser forward
agent-browser reload
agent-browser close
Snapshots
agent-browser snapshot
agent-browser snapshot -i
agent-browser snapshot -i -C
agent-browser snapshot -i --json
agent-browser snapshot -c
agent-browser snapshot -d 3
agent-browser snapshot -s "#main"
Interactions
agent-browser click @e1
agent-browser click @e1 --new-tab
agent-browser dblclick @e1
agent-browser fill @e1 "text"
agent-browser type @e1 "text"
agent-browser press Enter
agent-browser press Control+a
agent-browser keydown Shift
agent-browser keyup Shift
agent-browser hover @e1
agent-browser check @e1
agent-browser uncheck @e1
agent-browser select @e1 "option"
agent-browser select @e1 "a" "b"
agent-browser scroll down 500
agent-browser scrollintoview @e1
agent-browser drag @e1 @e2
agent-browser upload @e1 file.pdf
Get Info
agent-browser get text @e1
agent-browser get value @e1
agent-browser get html @e1
agent-browser get attr href @e1
agent-browser get title
agent-browser get url
agent-browser get count "button"
agent-browser get box @e1
agent-browser get styles @e1
Check State
agent-browser is visible @e1
agent-browser is enabled @e1
agent-browser is checked @e1
Wait
agent-browser wait @e1
agent-browser wait 2000
agent-browser wait --text "Success"
agent-browser wait --url "**/dashboard"
agent-browser wait --load networkidle
agent-browser wait --fn "window.ready"
Screenshots & Capture
agent-browser screenshot
agent-browser screenshot out.png
agent-browser screenshot --full
agent-browser screenshot --annotate
agent-browser pdf out.pdf
Diff (Compare Page States)
Compare accessibility tree or visual state before/after changes:
agent-browser snapshot -i
agent-browser click @e2
agent-browser diff snapshot
agent-browser diff snapshot --baseline before.txt
agent-browser diff screenshot --baseline before.png
agent-browser diff url https://staging.example.com https://prod.example.com
agent-browser diff url <url1> <url2> --wait-until networkidle
agent-browser diff url <url1> <url2> --selector "#main"
agent-browser diff url <url1> <url2> --screenshot
diff snapshot uses +/- like git diff. diff screenshot produces a diff image with changed pixels in red + mismatch percentage.
Semantic Locators
Alternative when you know the element (no snapshot needed):
agent-browser find role button click --name "Submit"
agent-browser find text "Sign In" click
agent-browser find text "Sign In" click --exact
agent-browser find label "Email" fill "user@test.com"
agent-browser find placeholder "Search" fill "query"
agent-browser find alt "Logo" click
agent-browser find title "Close" click
agent-browser find testid "submit-btn" click
agent-browser find first ".item" click
agent-browser find last ".item" click
agent-browser find nth 2 "a" hover
Annotated Screenshots (Vision Mode)
Use --annotate to take a screenshot with numbered labels overlaid on interactive elements. Each label [N] maps to ref @eN. Also caches refs — interact immediately without separate snapshot.
agent-browser screenshot --annotate
agent-browser click @e2
Use when: unlabeled icon buttons, visual-only elements, canvas/charts (invisible to text snapshots), or spatial reasoning needed.
JavaScript Evaluation
Use eval to run JS in the browser. Shell quoting can corrupt complex expressions — use --stdin or -b to avoid issues.
agent-browser eval 'document.title'
agent-browser eval 'document.querySelectorAll("img").length'
agent-browser eval --stdin <<'EVALEOF'
JSON.stringify(
Array.from(document.querySelectorAll("img"))
.filter(i => !i.alt)
.map(i => ({ src: i.src.split("/").pop(), width: i.width }))
)
EVALEOF
agent-browser eval -b "$(echo -n 'Array.from(document.querySelectorAll("a")).map(a => a.href)' | base64)"
Rules of thumb:
- Single-line, no nested quotes →
eval 'expression' with single quotes
- Nested quotes, arrow functions, template literals, multiline →
eval --stdin <<'EVALEOF'
- Programmatic/generated scripts →
eval -b with base64
Sessions
Parallel isolated browsers (see auth.md for multi-user auth):
agent-browser --session test1 open site-a.com
agent-browser --session test2 open site-b.com
agent-browser session list
Session Persistence
Auto-save/restore cookies and localStorage across browser restarts:
agent-browser --session-name myapp open https://app.example.com/login
agent-browser close
agent-browser --session-name myapp open https://app.example.com/dashboard
export AGENT_BROWSER_ENCRYPTION_KEY=$(openssl rand -hex 32)
agent-browser --session-name secure open https://app.example.com
agent-browser state list
agent-browser state show myapp-default.json
agent-browser state clear myapp
agent-browser state clean --older-than 7
Connect to Existing Chrome
agent-browser --auto-connect open https://example.com
agent-browser --auto-connect snapshot
agent-browser --cdp 9222 snapshot
Local Files
agent-browser --allow-file-access open file:///path/to/document.pdf
agent-browser --allow-file-access open file:///path/to/page.html
agent-browser screenshot output.png
iOS Simulator (Mobile Safari)
agent-browser device list
agent-browser -p ios --device "iPhone 16 Pro" open https://example.com
agent-browser -p ios snapshot -i
agent-browser -p ios tap @e1
agent-browser -p ios fill @e2 "text"
agent-browser -p ios swipe up
agent-browser -p ios screenshot mobile.png
agent-browser -p ios close
Requires: macOS with Xcode, Appium (npm install -g appium && appium driver install xcuitest).
Real devices: Use --device "<UDID>" (UDID from xcrun xctrace list devices).
Configuration File
Create agent-browser.json in project root for persistent settings:
{
"headed": true,
"proxy": "http://localhost:8080",
"profile": "./browser-data"
}
Priority (lowest→highest): ~/.agent-browser/config.json < ./agent-browser.json < env vars < CLI flags. Use --config <path> or AGENT_BROWSER_CONFIG for custom path. All CLI options map to camelCase keys (--executable-path → "executablePath").
Timeouts and Slow Pages
Default Playwright timeout is 60s. For slow pages, use explicit waits:
agent-browser wait --load networkidle
agent-browser wait "#content"
agent-browser wait @e1
agent-browser wait --url "**/dashboard"
agent-browser wait --fn "document.readyState === 'complete'"
agent-browser wait 5000
Use wait --load networkidle after open for consistently slow sites.
JSON Output
Add --json for machine-readable output:
agent-browser snapshot -i --json
agent-browser get text @e1 --json
agent-browser is visible @e1 --json
Recording & Profiling
agent-browser record start demo.webm
agent-browser record stop
agent-browser record restart take2.webm
agent-browser profiler start
agent-browser profiler stop trace.json
See debugging.md for details.
Examples
Form Submission
agent-browser open https://example.com/form
agent-browser snapshot -i
agent-browser fill @e1 "user@example.com"
agent-browser fill @e2 "password123"
agent-browser click @e3
agent-browser wait --load networkidle
agent-browser snapshot -i
Auth with Saved State
agent-browser open https://app.example.com/login
agent-browser snapshot -i
agent-browser fill @e1 "username"
agent-browser fill @e2 "password"
agent-browser click @e3
agent-browser wait --url "**/dashboard"
agent-browser state save auth.json
agent-browser state load auth.json
agent-browser open https://app.example.com/dashboard
More auth patterns in auth.md.
Token Auth (Skip Login)
agent-browser open api.example.com --headers '{"Authorization": "Bearer <token>"}'
agent-browser snapshot -i --json
Debugging
agent-browser --headed open example.com
agent-browser console
agent-browser errors
agent-browser highlight @e1
agent-browser --debug open example.com
See debugging.md for traces, profiling, video, common issues.
Session Cleanup
Always close sessions when done to avoid leaked processes:
agent-browser close
agent-browser --session name close
If previous session not closed properly, daemon may still be running. agent-browser close cleans it up.
Troubleshooting
"Browser not launched" error: Daemon stuck. Kill and retry:
pkill -f agent-browser && agent-browser open <url>
--headed not showing window: Daemon reuse bug. If daemon started headless, --headed is ignored. Kill daemon first:
agent-browser close
pkill -f "node.*daemon.js.*AGENT_BROWSER"
pkill -f "Google Chrome for Testing"
sleep 1
agent-browser open <url> --headed
Window exists but not visible (macOS):
osascript -e 'tell application "Google Chrome for Testing" to activate'
Element not found: Re-snapshot after page changes. DOM may have updated.
Ref lifecycle: Refs (@e1, @e2) are invalidated when the page changes. Always re-snapshot after clicks that navigate, form submissions, or dynamic content loading.
Gotchas
- Element refs are disposable. Any navigation, major DOM update, or modal transition can invalidate them, so re-snapshot before acting on stale refs.
- Prefer direct browser commands over oversized injected scripts when possible. Large
javascript blocks are harder to debug and more brittle across pages.
- Session state can hide auth or cache problems. Be explicit when you need a clean browser session versus a reused one.
References
| Topic | File |
|---|
| Full command reference | commands.md |
| Snapshot refs, lifecycle, troubleshooting | snapshot-refs.md |
| Auth, OAuth, 2FA, state persistence | auth.md |
| Sessions, parallel browsers, state | session-management.md |
| Debugging, profiling, video recording | debugging.md |
| Proxy, geo-testing, rotating proxies | proxy.md |
| Network mocking, tabs, frames, dialogs, settings | advanced.md |
Update Check (End of Command)
ALWAYS run at the very end of command execution:
PLUGIN_ROOT="${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}}"
[ ! -d "$PLUGIN_ROOT/scripts" ] && PLUGIN_ROOT=$(ls -td ~/.claude/plugins/cache/nairon-flux/flux/*/ 2>/dev/null | head -1)
UPDATE_JSON=$("$PLUGIN_ROOT/scripts/version-check.sh" 2>/dev/null || echo '{"update_available":false}')
UPDATE_AVAILABLE=$(echo "$UPDATE_JSON" | jq -r '.update_available')
LOCAL_VER=$(echo "$UPDATE_JSON" | jq -r '.local_version')
REMOTE_VER=$(echo "$UPDATE_JSON" | jq -r '.remote_version')
If update available, append to output:
---
Flux update available: v${LOCAL_VER} → v${REMOTE_VER}
Update Flux from the same source you installed it from, then restart your agent session.
---