Skip to content

kepptic/ghax

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

106 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ghax

ghax is a command-line tool that drives your real, already-running Chrome or Edge (or other Chromium-family browser) from the terminal. It connects over the browser's built-in remote-debugging protocol — no separate sandboxed browser window, no logging in again. Your tabs, your cookies, your extensions, your session: ghax just drives it.

It's an alternative to playwright-cli, Puppeteer, and Claude-in-Chrome for interactive and agent-driven browser workflows.

What it does

  • Navigate pages, go back/forward, open and switch tabs
  • Click and fill form fields by accessibility role and name — not brittle CSS selectors
  • Take an accessibility-tree snapshot of the page and act on it with @e<n> refs
  • Screenshot the page, or read its text and DOM state
  • Read console messages and network requests as they happen
  • Drive Chrome extensions — service workers, chrome.storage, hot-reload after a rebuild
  • Drive your real logged-in session (real auth, SSO cookies, open tabs) via the ghax bridge extension — even though Edge 150 / Chrome 136 block CDP on the default profile
ghax attach
ghax goto https://example.com
ghax snapshot -i
ghax click @e3
ghax fill @e5 "hello"

Install · Quickstart · Benchmarks · Features · AI agent integration · Commands


Install

Requires Node 20+ (the daemon that drives the browser runs on Node). The first ghax attach finishes the rest of the setup automatically — it fetches its small browser-driver bundle if it's missing and installs the one runtime dependency (Playwright) it needs — so there's nothing else to run by hand.

macOS (Apple Silicon or Intel)

curl -fsSL https://github.com/kepptic/ghax/releases/latest/download/ghax-installer.sh | sh

Linux (x86_64 or arm64)

curl -fsSL https://github.com/kepptic/ghax/releases/latest/download/ghax-installer.sh | sh

Windows (x86_64, PowerShell)

irm https://github.com/kepptic/ghax/releases/latest/download/ghax-installer.ps1 | iex

Then, on any OS:

ghax --version     # → ghax 0.4.4
ghax --help        # full command surface (73 verbs)

Pre-built release archives for macOS, Linux, and Windows are published on GitHub Releases.

Stay current: ghax update installs the latest release in place; ghax update --check is a dry-run. ghax attach also prints a one-line banner when a newer version is published, cached for 24h and refreshed in the background (set GHAX_NO_UPDATE_CHECK=1 to silence).

From source (for contributors)

Needs Rust 1.80+ and Node 20+.

git clone https://github.com/kepptic/ghax.git
cd ghax
npm install
npm run build:all
npm run install-link

Ensure ~/.local/bin is on PATH. Uninstall: npm run uninstall-link.


Quickstart

Launch Chrome or Edge with CDP enabled. Since Edge 150 / Chrome 136, --remote-debugging-port is silently ignored on the default profile (Chromium's remote-debugging hardening), so a --user-data-dir pointing at a non-default directory is always required — see Browser compatibility below. The easiest path is to let ghax launch one:

ghax attach --launch                 # picks Edge/Chrome, scratch profile
ghax attach --launch --browser edge  # or pick explicitly

Or launch manually:

# macOS — Edge or Chrome (both need the explicit profile dir)
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge" \
  --remote-debugging-port=9222 \
  --user-data-dir="$HOME/.config/edge-ghax" &

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --remote-debugging-port=9222 \
  --user-data-dir="$HOME/.config/chrome-ghax" &
# Windows — Edge
Start-Process "${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe" `
  -ArgumentList "--remote-debugging-port=9222", "--user-data-dir=$env:LOCALAPPDATA\edge-ghax"

# Windows — Chrome
Start-Process "${env:ProgramFiles}\Google\Chrome\Application\chrome.exe" `
  -ArgumentList "--remote-debugging-port=9222", "--user-data-dir=$env:LOCALAPPDATA\chrome-ghax"
# Linux — Edge, Chromium, or Chrome
microsoft-edge --remote-debugging-port=9222 --user-data-dir="$HOME/.config/edge-ghax" &
chromium --remote-debugging-port=9222 --user-data-dir="$HOME/.config/chromium-ghax" &
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.config/chrome-ghax" &

Attach and drive:

ghax attach
ghax tabs
ghax goto https://example.com
ghax snapshot -i
ghax click @e3
ghax fill @e5 "hello"
ghax screenshot --path /tmp/shot.png
ghax perf
ghax detach

Benchmarks

Most browser-automation CLIs boot a fresh browser on every command. Ghax keeps a CDP session open via a small persistent daemon, so the cold start is paid once per session instead of once per call.

Cold-start workflow (launch → goto → text → eval → screenshot → snapshot → close), Apple Silicon, against example.com:

Tool Cold start Warm (per command) Speedup
ghax 1.56 s 49 ms
gstack-browse 6.70 s 58 ms ghax 4.3× faster cold
agent-browser 3.48 s 344 ms ghax 7.0× faster warm
playwright-cli 5.13 s 680 ms ghax 13.9× faster warm

Warm-loop on a real Wikipedia article (~250 KB): ghax 117 ms/cmd vs playwright-cli 778 ms/cmd. Text extraction 9× faster (154 ms vs 1,404 ms) because ghax hits a DOM that's already parsed instead of booting a browser to query it.

Binary: ~3 MB stripped on Apple Silicon. Cold single-command invocation: ~20 ms. Daemon bundle: ~80 KB of JavaScript.

Full methodology, per-operation breakdowns, and reproduction steps: docs/BENCHMARK.md.


Profile modes

Mode How When
Scratch profile ghax attach --launch (add --headless for no window) Default. Fresh instance, reproducible environments, CI-style runs.
Dedicated profile Launch with --remote-debugging-port=9222 and --user-data-dir=<persistent path>, then ghax attach A reusable automation profile that accumulates logins/state across sessions.
Real session (bridge) ghax attach --extension — the ghax bridge extension relays CDP from your real profile Your actual logged-in browser: real auth, SSO cookies, open tabs. CDP can't reach the default profile since Edge 150 / Chrome 136 — the bridge is how.

Real session via the ghax bridge (experimental, v0.5+)

Since Edge 150 / Chrome 136, --remote-debugging-port is ignored on the default profile, so plain CDP can't drive your everyday browser. The ghax bridge restores that: a small MV3 extension (extension/) relays CDP to a real tab through chrome.debugger — the one API the restriction doesn't touch — over a localhost WebSocket.

# One-time: load the extension unpacked
#   edge://extensions → Developer mode → Load unpacked → ./extension
ghax attach --extension --control-active   # drive the active tab, no popup click
ghax snapshot -i                            # full verb surface over your real session
ghax bridge control --tab-id <n>            # or point it at a specific tab (see `tabs`)
ghax bridge control --stop                  # release the tab (debugging banner clears)

Most verbs work over the bridge (navigation, snapshot/@ref click/fill/press/type, screenshot, tabs, console, network, batch, …). Browser-context verbs (cookies, storage, viewport, qa, perf, gestures, the ext family) return a clear "not supported over the extension bridge yet". Notes: attaching shows Chrome's persistent "extension is debugging this browser" banner (visible consent); reload the unpacked extension after updating ghax; the bridge WebSocket is localhost-only but currently unauthenticated (a handshake token is planned) — run it as a deliberate foreground act.


Features

Snapshot and interact

  • Accessibility-tree snapshots with @e<n> refs. Click by role and name, not brittle CSS selectors.
  • Dialog-aware walker. When a modal is open, snapshots walk the modal instead of the aria-hidden="true" app behind it.
  • Shadow-DOM traversal. Chain selectors (host >> inner) descend into open shadow roots for custom-element apps (Lit, Shoelace, web components).
  • Framework-safe fill. Native-setter plus input event for React, explicit blur for Angular validators, contenteditable paths for Material chip inputs and rich editors, and Monaco-aware — routes into monaco.editor.getEditors()/setValue() when the target lives inside a .monaco-editor (Datto RMM, Splunk, Grafana, Postman, GitLab Web IDE).
  • ghax select <@ref|selector> <value> for dropdowns/comboboxes. Cascades native <select> → AntD <Select> (React fiber traversal to the controlled onChange, bypassing AntD's pointer-event quirks) → a real click-and-pick for everything else (react-select, MUI, Headless UI, role=combobox), including options rendered into a portal under <body>. Also takes --index <n> and --by-value <val>.
  • Real user gestures via CDP Input.dispatch*. Needed for APIs like chrome.sidePanel.open() that refuse synthetic clicks.

MV3 extensions

  • Service worker eval: ghax ext sw <id> eval "<js>"
  • chrome.storage read/write: ghax ext storage <id> local get|set|remove|clear
  • Popup, options, and side-panel eval via the same shape
  • Runtime message dispatch: ghax ext message <id> <json-payload>
  • Hot-reload: ghax ext hot-reload <id> reloads the service worker and re-injects content scripts into every matching tab in ~5 seconds, without losing tab state.

Observability

  • Console and network capture from attach onward. Rolling 5k-entry buffers, --errors and --pattern filters, request and response headers, HAR 1.2 export, stack-frame parsing, dedup grouping.
  • ghax attach --capture-bodies[=<url-glob>] records response bodies (responseBody) and POST/PUT/PATCH request bodies (requestBody) — e.g. a captured GraphQL mutation's variables, not just its reply. Same 32KB cap + [truncated N bytes] marker, same glob + JSON/text content-type filter on both sides. ghax network --har <path> includes captured request bodies as HAR postData.
  • Source-map resolution: console --source-maps maps main.abc123.js:1:48291 back to src/AuthForm.tsx:42:12.
  • Core Web Vitals (ghax perf): LCP with the element that triggered it, FCP, CLS, TTFB, full nav timing. Buffered observers catch entries that fired before the call.
  • Live SSE tail: console --follow, network --follow, ext sw <id> logs --follow.
  • ghax cookies scopes to the active tab's URL by default (Playwright's own domain/path/secure applicability match — handles subdomains, localhost, and IP+port correctly) and redacts values (value: "<redacted, N chars>") unless you pass --values. --all opts into the whole-profile dump (every domain the browser profile has ever set a cookie for — the old, unscoped default); --domain <d> filters that dump by domain substring/suffix; --url <u> scopes applicability to an explicit URL instead of the current tab. ghax cookies --has <name> exits 0/1 — the scripting primitive for "did login land?" instead of inferring auth state from a redirect.

Downloads

  • Attached-browser downloads behave like normal browsing: they land in the real ~/Downloads under the site-suggested filename with its extension — not as extension-less GUIDs in a Playwright temp dir. (ghax re-asserts sane CDP Browser.setDownloadBehavior after attach, undoing Playwright's connectOverCDP hijack.)
  • ghax attach --downloads-dir <path> redirects downloads to a chosen dir.
  • ghax downloads [--last N] lists captured downloads: url, filename, final path, state, byte counts, and timestamps.

Execution patterns

  • ghax batch '<json-array>' ships a whole plan in one round-trip and auto-re-snapshots between ref-using steps, so a mid-plan combobox reshuffle doesn't break later refs.
  • ghax chain reads the same shape from stdin for ad-hoc flows.
  • ghax try [<js>] [--css <rules>] [--selector <sel>] [--measure <expr>] [--shot <path>] mutates the running page, measures a result, and screenshots in one call. Revert = reload.
  • ghax eval <js> runs in page context and awaits Promises and async IIFEs automatically(async () => { await fetch(...); return 'ok' })() resolves before ghax prints the result; no special await syntax or retry needed on the caller's side.
  • ghax shell is a REPL that keeps the CLI process alive between commands, ~1.8× faster for multi-turn agent sessions.
  • ghax record start / stop captures commands into a replayable JSON file; ghax replay <file> runs them back; ghax gif <recording> stitches frames via ffmpeg.

Orchestrated verbs

  • ghax qa --url <u> [--crawl <root>] walks URLs and produces a JSON report with screenshots, console errors, and failed requests.
  • ghax canary <url> --interval 60 tails a deployed URL for regressions.
  • ghax perf and ghax profile for page-level and extension performance.
  • ghax diff-state <before.json> <after.json> for comparing captured state snapshots.

Multi-agent isolation

Per-agent state files let multiple agents share the same browser without stepping on each other:

# Agent A
GHAX_STATE_FILE=/tmp/ghax-a.json ghax attach
GHAX_STATE_FILE=/tmp/ghax-a.json ghax new-window https://app-a.com

# Agent B
GHAX_STATE_FILE=/tmp/ghax-b.json ghax attach
GHAX_STATE_FILE=/tmp/ghax-b.json ghax new-window https://app-b.com

Same browser process, separate windows and separate daemon state. Neither agent sees the other's active-tab pointer.


Command reference

73 verbs across attach/detach, navigation, snapshot and interact, file uploads, real user gestures, MV3 extension internals, console and network capture, downloads, Core Web Vitals, screenshots, XPath, live injection, batch execution, recording and replay, and orchestrated flows.

ghax --help           # full surface — authoritative
ghax --help | less    # scroll it

Every verb accepts --json for machine-readable output. Exit codes: 0 ok, 1 usage error, 2 not attached, 4 CDP error, 10 build/bootstrap failure.


AI agent integration

Ghax ships a repo-level llms.txt that any shell-capable AI agent can follow to install and verify itself.

One-paste install (any agent)

Drop this into Claude Code, Cursor, Codex, Aider, Continue, or similar:

Clone https://github.com/kepptic/ghax and follow the install steps in its llms.txt. Verify with ghax --version.

The agent reads llms.txt, runs the three build commands, verifies the binary, and reports back.

Claude Code

One skill ships under .claude/skills/: ghax (full command surface + workflow recipes). Copy it once:

mkdir -p ~/.claude/skills
cp .claude/skills/ghax.md ~/.claude/skills/

They're picked up automatically on phrases like "snapshot the dashboard" or "hot-reload the extension".

Codex, Cursor, Aider, Continue, Windsurf

These agents read a project memory file (AGENTS.md, .cursorrules, CONVENTIONS.md, or equivalent). Drop the following into your project's memory file:

## Browser automation: use ghax

When a task requires a browser, use the `ghax` CLI. Three patterns:

1. Attach: `ghax attach` (scratch), `ghax attach --launch` (fresh instance), or `ghax attach --extension --control-active` (the user's real session via the bridge)
2. Snapshot-then-interact:
   `ghax snapshot -i --json``ghax click @e3``ghax fill @e5 "..."`
3. One-round-trip batch:
   `ghax batch '[{"cmd":"click","args":["@e7"]}, ...]'`

Every verb takes `--json`. Full surface: `ghax --help`.

The agent reads the memory file at session start and knows when to reach for ghax.


Browser compatibility

Chromium-family only: Edge, Chrome, Chromium, Brave, Arc. Firefox and Safari are out of scope (CDP-only).

Chrome 136+ and Edge 150+ silently ignore --remote-debugging-port on the default user-data-dir (Chromium's remote-debugging hardening) — no error, no DevToolsActivePort file, nothing listens. There is no policy or flag bypass, and copying the default profile into another directory doesn't carry sessions over (cookies/passwords are encrypted per data dir). Always pass --user-data-dir=<path> explicitly (the quickstart above shows this), or use ghax attach --launch.

To drive your real logged-in session anyway, use the ghax bridge (ghax attach --extension, see Real session via the ghax bridge) — its extension uses chrome.debugger inside the profile, which the socket restriction doesn't affect. Any other extension bridge (e.g. Claude-in-Chrome) works on the same principle.

Both browsers set navigator.webdriver = true when launched with --remote-debugging-port. Add --disable-blink-features=AutomationControlled to suppress that bit if a page you're automating treats it as a bot signal. Full notes: CONTRIBUTING.md → Known browser quirks.


Architecture

ghax CLI (Rust, ~3 MB, ~20 ms cold start)
        │  HTTP to 127.0.0.1:<random>
        ▼
ghax daemon (Node ESM bundle, ~80 KB)
        │  ├─ CDP tab driver — navigation, snapshot, interact
        │  └─ Raw CDP WebSocket pool — service workers, side panels, gestures
        ▼
Chromium-family browser (--remote-debugging-port=9222)

The CLI is a thin HTTP client so the binary stays small. The daemon owns every CDP session and auto-shuts after 30 minutes idle. Full notes: ARCHITECTURE.md.


Contributing

Issues and PRs welcome. Start with CONTRIBUTING.md — it covers the Rust + Node split, the 95-check live-browser smoke suite, and hard invariants. Coding agents working on the repo should read AGENTS.md first.

This project follows the Contributor Covenant v2.1.


License

MIT. See LICENSE.

Portions adapted from gstack by Garry Tan (also MIT): buffers.ts, config.ts, and the accessibility-snapshot algorithm in snapshot.ts.

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors