Skip to main content

Chrome automation from the command line

Project description

Rodney: Chrome automation from the command line

PyPI Changelog Tests License

A Go CLI tool that drives a persistent headless Chrome instance using the rod browser automation library. Each command connects to the same long-running Chrome process, making it easy to script multi-step browser interactions from shell scripts or interactive use.

Architecture

rodney start     →  launches Chrome (headless, persists after CLI exits)
                     saves WebSocket debug URL to ~/.rodney/state.json

rodney open URL  →  connects to running Chrome via WebSocket
                     navigates the active tab, disconnects

rodney js EXPR   →  connects, evaluates JS, prints result, disconnects

rodney stop      →  connects and shuts down Chrome, cleans up state

Each CLI invocation is a short-lived process. Chrome runs independently and tabs persist between commands.

Building

go build -o rodney .

Requires:

  • Go 1.21+
  • Google Chrome or Chromium installed (or set ROD_CHROME_BIN=/path/to/chrome)

Usage

Start/stop the browser

rodney start          # Launch headless Chrome
rodney status         # Show browser info and active page
rodney stop           # Shut down Chrome

Navigate

rodney open https://example.com    # Navigate to URL
rodney open example.com            # http:// prefix added automatically
rodney back                        # Go back
rodney forward                     # Go forward
rodney reload                      # Reload page

Extract information

rodney url                    # Print current URL
rodney title                  # Print page title
rodney text "h1"              # Print text content of element
rodney html "div.content"     # Print outer HTML of element
rodney html                   # Print full page HTML
rodney attr "a#link" href     # Print attribute value
rodney pdf output.pdf         # Save page as PDF

Run JavaScript

rodney js document.title                        # Evaluate expression
rodney js "1 + 2"                               # Math
rodney js 'document.querySelector("h1").textContent'  # DOM queries
rodney js '[1,2,3].map(x => x * 2)'            # Returns pretty-printed JSON
rodney js 'document.querySelectorAll("a").length'     # Count elements

The expression is automatically wrapped in () => { return (expr); }.

Interact with elements

rodney click "button#submit"       # Click element
rodney input "#search" "query"     # Type into input field
rodney clear "#search"             # Clear input field
rodney select "#dropdown" "value"  # Select dropdown by value
rodney submit "form#login"         # Submit a form
rodney hover ".menu-item"          # Hover over element
rodney focus "#email"              # Focus element

Wait for conditions

rodney wait ".loaded"       # Wait for element to appear and be visible
rodney waitload             # Wait for page load event
rodney waitstable           # Wait for DOM to stop changing
rodney waitidle             # Wait for network to be idle
rodney sleep 2.5            # Sleep for N seconds

Screenshots

rodney screenshot                      # Save as screenshot.png
rodney screenshot page.png             # Save to specific file
rodney screenshot-el ".chart" chart.png  # Screenshot specific element

Manage tabs

rodney pages                    # List all tabs (* marks active)
rodney newpage https://...      # Open URL in new tab
rodney page 1                   # Switch to tab by index
rodney closepage 1              # Close tab by index
rodney closepage                # Close active tab

Query elements

rodney exists ".loading"    # Exit 0 if exists, exit 1 if not
rodney count "li.item"      # Print number of matching elements
rodney visible "#modal"     # Exit 0 if visible, exit 1 if not

Accessibility testing

rodney ax-tree                           # Dump full accessibility tree
rodney ax-tree --depth 3                 # Limit tree depth
rodney ax-tree --json                    # Output as JSON

rodney ax-find --role button             # Find all buttons
rodney ax-find --name "Submit"           # Find by accessible name
rodney ax-find --role link --name "Home" # Combine filters
rodney ax-find --role button --json      # Output as JSON

rodney ax-node "#submit-btn"             # Inspect element's a11y properties
rodney ax-node "h1" --json               # Output as JSON

These commands use Chrome's Accessibility CDP domain to expose what assistive technologies see. ax-tree uses getFullAXTree, ax-find uses queryAXTree, and ax-node uses getPartialAXTree.

# CI check: verify all buttons have accessible names
rodney ax-find --role button --json | python3 -c "
import json, sys
buttons = json.load(sys.stdin)
unnamed = [b for b in buttons if not b.get('name', {}).get('value')]
if unnamed:
    print(f'FAIL: {len(unnamed)} button(s) missing accessible name')
    sys.exit(1)
print(f'PASS: all {len(buttons)} buttons have accessible names')
"

Shell scripting examples

# Wait for page to load and extract data
rodney start
rodney open https://example.com
rodney waitstable
title=$(rodney title)
echo "Page: $title"

# Conditional logic based on element presence
if rodney exists ".error-message"; then
    rodney text ".error-message"
fi

# Loop through pages
for url in page1 page2 page3; do
    rodney open "https://example.com/$url"
    rodney waitstable
    rodney screenshot "${url}.png"
done

rodney stop

Configuration

Environment Variable Default Description
ROD_CHROME_BIN /usr/bin/google-chrome Path to Chrome/Chromium binary
ROD_TIMEOUT 30 Default timeout in seconds for element queries
HTTPS_PROXY / HTTP_PROXY (none) Authenticated proxy auto-detected on start

State is stored in ~/.rodney/state.json. Chrome user data is stored in ~/.rodney/chrome-data/.

Proxy support

In environments with authenticated HTTP proxies (e.g., HTTPS_PROXY=http://user:pass@host:port), rodney start automatically:

  1. Detects the proxy credentials from environment variables
  2. Launches a local forwarding proxy that injects Proxy-Authorization headers into CONNECT requests
  3. Configures Chrome to use the local proxy

This is necessary because Chrome cannot natively authenticate to proxies during HTTPS tunnel (CONNECT) establishment. The local proxy runs as a background process and is automatically cleaned up by rodney stop.

See claude-code-chrome-proxy.md for detailed technical notes.

How it works

The tool uses the rod Go library which communicates with Chrome via the DevTools Protocol (CDP) over WebSocket. Key implementation details:

  • start uses rod's launcher package to start Chrome with Leakless(false) so Chrome survives after the CLI exits
  • Proxy auth handled via a local forwarding proxy that bridges Chrome to authenticated upstream proxies
  • State persistence via a JSON file containing the WebSocket debug URL and Chrome PID
  • Each command creates a new rod Browser connection to the same Chrome instance, executes the operation, and disconnects
  • Element queries use rod's built-in auto-wait with a configurable timeout (default 30s)
  • JS evaluation wraps user expressions in arrow functions as required by rod's Eval
  • Accessibility commands call CDP's Accessibility domain directly via rod's proto package (getFullAXTree, queryAXTree, getPartialAXTree)

Dependencies

Commands reference

Command Arguments Description
start Launch headless Chrome
stop Shut down Chrome
status Show browser status
open <url> Navigate to URL
back Go back in history
forward Go forward in history
reload Reload current page
url Print current URL
title Print page title
html [selector] Print HTML (page or element)
text <selector> Print element text content
attr <selector> <name> Print attribute value
pdf [file] Save page as PDF
js <expression> Evaluate JavaScript
click <selector> Click element
input <selector> <text> Type into input
clear <selector> Clear input
select <selector> <value> Select dropdown value
submit <selector> Submit form
hover <selector> Hover over element
focus <selector> Focus element
wait <selector> Wait for element to appear
waitload Wait for page load
waitstable Wait for DOM stability
waitidle Wait for network idle
sleep <seconds> Sleep N seconds
screenshot [file] Page screenshot
screenshot-el <selector> [file] Element screenshot
pages List tabs
page <index> Switch tab
newpage [url] Open new tab
closepage [index] Close tab
exists <selector> Check element exists (exit code)
count <selector> Count matching elements
visible <selector> Check element visible (exit code)
ax-tree [--depth N] [--json] Dump accessibility tree
ax-find [--name N] [--role R] [--json] Find accessible nodes
ax-node <selector> [--json] Show element accessibility info

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rodney-0.3.0-py3-none-musllinux_1_2_x86_64.whl (11.5 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

rodney-0.3.0-py3-none-musllinux_1_2_aarch64.whl (11.1 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

rodney-0.3.0-py3-none-manylinux_2_17_x86_64.whl (11.5 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

rodney-0.3.0-py3-none-manylinux_2_17_aarch64.whl (11.1 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

rodney-0.3.0-py3-none-macosx_11_0_arm64.whl (11.5 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

rodney-0.3.0-py3-none-macosx_10_9_x86_64.whl (11.9 MB view details)

Uploaded Python 3macOS 10.9+ x86-64

File details

Details for the file rodney-0.3.0-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for rodney-0.3.0-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 4331754e16a3ee5872ff2f3441df7be7bb929636952a4913867dfbb28a8febfe
MD5 874c7e63dd444474343c0f2692c3eb13
BLAKE2b-256 244412276b1522015c0c92f9b6e96aece12fc96a7aee88f56d142376d2b8e259

See more details on using hashes here.

Provenance

The following attestation bundles were made for rodney-0.3.0-py3-none-musllinux_1_2_x86_64.whl:

Publisher: publish.yml on simonw/rodney

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rodney-0.3.0-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for rodney-0.3.0-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 a13406bb543d82350d7393911d11301b76af93435674f7da80341d9aca0ed413
MD5 4b79d69cea7081ceac1ed7eb4e6b6844
BLAKE2b-256 ccfd48e9d24ece4ddf55897356e5654b9745a24a077f79785956fdd628f7bb56

See more details on using hashes here.

Provenance

The following attestation bundles were made for rodney-0.3.0-py3-none-musllinux_1_2_aarch64.whl:

Publisher: publish.yml on simonw/rodney

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rodney-0.3.0-py3-none-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for rodney-0.3.0-py3-none-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 51f16dde7df26a74fadd1af6ae34710f33a7bd53c63556797dbba4645d894363
MD5 9009ae9a558e901ea76d8418b7c51417
BLAKE2b-256 3dc4f2816355d05ddba1760b91324f7383cb663c1e9e7b1506771779c1743fb5

See more details on using hashes here.

Provenance

The following attestation bundles were made for rodney-0.3.0-py3-none-manylinux_2_17_x86_64.whl:

Publisher: publish.yml on simonw/rodney

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rodney-0.3.0-py3-none-manylinux_2_17_aarch64.whl.

File metadata

File hashes

Hashes for rodney-0.3.0-py3-none-manylinux_2_17_aarch64.whl
Algorithm Hash digest
SHA256 4001c7c10a94b2de222d85d7e6fce56b429f9cda638d8b1d6116969e6de012ed
MD5 2b5c238e8ffaa54463bce3434e2ecd9d
BLAKE2b-256 4ec23c89e9483fad6cab14899df04406504821141deffc3651ced678ade8b654

See more details on using hashes here.

Provenance

The following attestation bundles were made for rodney-0.3.0-py3-none-manylinux_2_17_aarch64.whl:

Publisher: publish.yml on simonw/rodney

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rodney-0.3.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rodney-0.3.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 dcdc8ceb09bd58a527a6a2bcacca91391cb7a947762d8678dcf7e0316d977bb9
MD5 ea552222c01b2d1d9f505395258b5554
BLAKE2b-256 428c1d33fd63bb686e4c4f2e151bda74307b9556a5ca13e03e1ef0e6e1915eb8

See more details on using hashes here.

Provenance

The following attestation bundles were made for rodney-0.3.0-py3-none-macosx_11_0_arm64.whl:

Publisher: publish.yml on simonw/rodney

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rodney-0.3.0-py3-none-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for rodney-0.3.0-py3-none-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 2a9e27d9f6e019543dbc54a05c89ee7f58a0fe15418fb06d77d53f39bff03851
MD5 1ccb74bdf1cc5608e9edc31e79d25d1c
BLAKE2b-256 ca06c01e13cec8ea8ad405b4c08236ee3cc2e905ba8fb0153886406ffec05bd9

See more details on using hashes here.

Provenance

The following attestation bundles were made for rodney-0.3.0-py3-none-macosx_10_9_x86_64.whl:

Publisher: publish.yml on simonw/rodney

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page