bdg - Browser Automation CLI
Quick Start
bash
1bdg https://example.com # Start session (launches Chrome)
2bdg dom screenshot /tmp/page.png # Take screenshot
3bdg stop # End session
Session Management
bash
1bdg <url> # Start session (1920x1080, headless if no display)
2bdg <url> --headless # Force headless mode
3bdg <url> --no-headless # Force visible browser window
4bdg status # Check session status
5bdg peek # Preview data without stopping
6bdg stop # Stop and save output
7bdg cleanup --force # Kill stale session
8bdg cleanup --aggressive # Kill all Chrome processes
Sessions run indefinitely by default (no timeout). With HMR/hot-reload dev servers, keep the session running:
bash
1bdg http://localhost:5173 # Start once
2# ... make code changes, HMR updates the page ...
3bdg dom screenshot /tmp/s.png # Check anytime
4bdg peek # Preview collected data
5# No need to stop/restart - Chrome stays on the page
Screenshots
Always use bdg dom screenshot (raw CDP is blocked):
bash
1bdg dom screenshot /tmp/page.png # Full page
2bdg dom screenshot /tmp/viewport.png --no-full-page # Viewport only
3bdg dom screenshot /tmp/el.png --selector "#main" # Element only
4bdg dom screenshot /tmp/scroll.png --scroll "#target" # Scroll to element first
bash
1# Discover forms
2bdg dom form --brief # Quick scan: field names, types, required
3
4# Fill and interact
5bdg dom fill "input[name='user']" "myuser" # Fill by selector
6bdg dom fill 0 "value" # Fill by index (from query)
7bdg dom click "button.submit" # Click element
8bdg dom submit "form" --wait-navigation # Submit and wait for page load
9bdg dom pressKey "input" Enter # Press Enter key
10
11# Options
12--no-wait # Skip network stability wait
13--wait-navigation # Wait for page navigation (traditional forms)
14--wait-network <ms> # Wait for network idle (SPA forms)
15--index <n> # Select nth element when multiple match
DOM Inspection
bash
1bdg dom query "selector" # Find elements, returns [0], [1], [2]...
2bdg dom get "selector" # Get semantic a11y info (token-efficient)
3bdg dom get "selector" --raw # Get full HTML
4bdg dom eval "js expression" # Run JavaScript
CDP Access
Direct access to Chrome DevTools Protocol:
bash
1# Execute any CDP method
2bdg cdp Runtime.evaluate --params '{"expression": "document.title", "returnByValue": true}'
3bdg cdp Page.navigate --params '{"url": "https://example.com"}'
4bdg cdp Page.reload --params '{"ignoreCache": true}'
5
6# Discovery
7bdg cdp --list # List all 53 domains
8bdg cdp Network --list # List methods in domain
9bdg cdp Network.getCookies --describe # Show method schema
10bdg cdp --search cookie # Search methods
Important: Always use returnByValue: true for Runtime.evaluate to get serialized values.
Common Patterns
Login Flow
bash
1bdg https://example.com/login
2bdg dom form --brief
3bdg dom fill "input[name='username']" "$USER"
4bdg dom fill "input[name='password']" "$PASS"
5bdg dom submit "button[type='submit']" --wait-navigation
6bdg dom screenshot /tmp/result.png
7bdg stop
Wait for Element
bash
1for i in {1..20}; do
2 EXISTS=$(bdg cdp Runtime.evaluate --params '{
3 "expression": "document.querySelector(\"#target\") !== null",
4 "returnByValue": true
5 }' | jq -r '.result.value')
6 [ "$EXISTS" = "true" ] && break
7 sleep 0.5
8done
bash
1bdg cdp Runtime.evaluate --params '{
2 "expression": "Array.from(document.querySelectorAll(\"a\")).map(a => ({text: a.textContent, href: a.href}))",
3 "returnByValue": true
4}' | jq '.result.value'
Exit Codes
| Code | Meaning | Action |
|---|
| 0 | Success | - |
| 1 | Blocked command | Read error message, use suggested alternative |
| 81 | Invalid arguments | Check command syntax |
| 83 | Resource not found | Element/session doesn't exist |
| 101 | CDP connection failure | Run bdg cleanup --aggressive and retry |
| 102 | CDP timeout | Increase timeout or check page load |
Troubleshooting
bash
1bdg status --verbose # Full diagnostics
2bdg cleanup --force # Kill stale session
3bdg cleanup --aggressive # Kill all Chrome processes
Chrome won't launch? Run bdg cleanup --aggressive then retry.
Session stuck? Run bdg cleanup --force to reset.
Custom Chrome Flags
Use --chrome-flags or BDG_CHROME_FLAGS for self-signed certificates, CORS, etc.:
bash
1# CLI option
2bdg https://localhost:5173 --chrome-flags="--ignore-certificate-errors"
3
4# Environment variable
5BDG_CHROME_FLAGS="--ignore-certificate-errors" bdg https://localhost:5173
6
7# Multiple flags
8bdg https://example.com --chrome-flags="--ignore-certificate-errors --disable-web-security"
Common flags for development:
--ignore-certificate-errors - Self-signed SSL certs
--disable-web-security - CORS issues in development
--allow-insecure-localhost - Insecure localhost
--disable-features=IsolateOrigins,site-per-process - Cross-origin iframes
Verification Best Practices
Prefer DOM queries over screenshots for verification:
bash
1# GOOD: Fast, precise, scriptable
2bdg cdp Runtime.evaluate --params '{
3 "expression": "document.querySelector(\".error-message\")?.textContent",
4 "returnByValue": true
5}'
6
7# GOOD: Check element exists
8bdg dom query ".submit-btn"
9
10# GOOD: Check text content
11bdg cdp Runtime.evaluate --params '{
12 "expression": "document.body.innerText.includes(\"Success\")",
13 "returnByValue": true
14}'
15
16# AVOID: Screenshots for simple verification (slow, requires visual inspection)
17bdg dom screenshot /tmp/check.png # Only use when you need visual proof
When to use screenshots:
- Visual regression testing
- Capturing proof for user review
- Debugging layout issues
- When DOM structure is unknown
When to use DOM queries:
- Verifying text content appeared
- Checking element exists/visible
- Validating form state
- Counting elements
- Any programmatic assertion
When NOT to Use bdg
- Static HTML - Use
curl + htmlq/pq
- API calls - Use
curl + jq
- Simple HTTP - Use
wget/curl
Use bdg when you need: JavaScript execution, dynamic content, browser APIs, screenshots, or network manipulation.