Releases: kepptic/ghax
v0.4.2
What's new in ghax 0.4.2
Fixed
- BUG-001 — auto-bootstrap daemon's Playwright runtime on fresh
attach. Release archives shipdist/ghax-daemon.mjswithout a
siblingnode_modules/, so the firstghax attachagainst a
fresh install used to fail withCannot find package 'playwright'.
attach.rsnow detects the missing-module sentinel and runs
scripts/bootstrap-daemon-runtime.sh(npm install playwright +
source-map) before the second attempt. Version literals
(PLAYWRIGHT_VERSION,SOURCE_MAP_VERSION) live at module level
and can be overridden by a siblingpackage.jsonif one is
present — so release archives can ship a real package.json and
skip the constants entirely.
Docs
- Reproducible cross-tool benchmark —
test/benchmark.tsnow reads
GHAX_BINenv var so the same 7-step workflow (launch → goto →
text → js → screenshot → snapshot → close) runs against any
binary. First published baseline: ghax 65ms/cmd, gstack 56ms/cmd,
agent-browser 178ms/cmd, playwright-cli 476ms/cmd.
Install ghax 0.4.2
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/kepptic/ghax/releases/download/v0.4.2/ghax-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/kepptic/ghax/releases/download/v0.4.2/ghax-installer.ps1 | iex"Download ghax 0.4.2
| File | Platform | Checksum |
|---|---|---|
| ghax-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| ghax-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| ghax-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| ghax-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| ghax-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
v0.4.1
What's new in ghax 0.4.1
First public release. The Rust CLI rewrite — ghax is now a ~2.6 MB
Rust binary that talks to the unchanged Node daemon over HTTP. ~3×
faster cold start (P50 ~20 ms vs ~70 ms), ~20× smaller download,
distributed as platform-specific binaries via cargo-dist for 5 target
triples (macOS x64/ARM, Linux x64/ARM, Windows x64).
Breaking
- Two error-surface tightenings fell out of the
evalInTargethelper
consolidation. Both are behavior improvements but worth flagging for
anyone with scripted error handling:ext storageused to return{ ok: true }when the underlying JS
expression threw. It now throws aDaemonError(exit code 4) with
the exception details, so a failedchrome.storage.*.setno longer
silently looks successful.ext messageused to returnnullwhen the cross-extension
chrome.runtime.sendMessagethrew outside its inner try/catch. It
now throws aDaemonErroras well.
Added
- 5-target release matrix via cargo-dist (macOS x64/ARM, Linux x64/ARM,
Windows x64). - Shell + PowerShell installer scripts that download from this repo's
GitHub Releases. All distribution stays insidekepptic/ghax— no
Homebrew tap, no crates.io publish, no npm publish required. - Daemon discovery precedence in
attach.rs: (1)$GHAX_DAEMON_BUNDLE
env var, (2) sibling of CLI binary, (3) dev fallback at
<repo root>/dist/ghax-daemon.mjs. - Smoke suite (
test/smoke.ts) readsGHAX_BINenv var so the same 80
checks run against any binary. 80/80 against the Rust binary in 31.3s. console --since <epoch-ms>andnetwork --since <epoch-ms>filter
buffer entries server-side, so callers (notablyqaandcanary)
don't have to ship hundreds of irrelevant entries across the HTTP
RPC just to discard them locally.consolealso acceptserrors: truevia RPC opts so the daemon drops non-error levels before
serialising; the Rust CLI uses this on the hot path.ghax xpath <expression> [--limit N]— query the page's DOM with an
XPath expression, return every matching element with its tag, text
preview, and bounding box. XPath is also usable via Playwright's
xpath=...prefix in every selector-accepting command (click,
fill,screenshot,is, etc.).ghax box <@ref|selector>— return{x, y, width, height}of the
first matching element. Resolves snapshot refs (@e3,@c1) or any
selector form.ghax attach --capture-bodies[=<url-glob>]— opt-in response-body
capture. Without a pattern, captures every JSON/text-like response
(application/json, text/*, javascript, xml, html, css, graphql)
up to 32KB each. With a glob (e.g.'*/api/*'), restricts to
matching URLs so browsing doesn't blow memory on images or chunks.
Bodies past 32KB truncate with a[truncated N bytes]marker.
Included in HAR export when the content is available.ghax console --source-maps— resolve bundled stack frames back to
their original source locations via the page's source maps. Each
capturedpageerroralready parses its stack; with--source-maps,
every frame is run through the daemon's source-map cache. Silent
fallback to the bundled frame on any failure. Adds ~60KB to the
daemon bundle for thesource-maplibrary; zero cost when the flag
isn't used.ghax shell— interactive REPL. Reads commands from stdin, tokenises
with shell-ish quoting, re-enters the main dispatcher per line. One
process for the whole session, so the per-command Bun spawn cost goes
away. Measured 1.8× faster for 10-command batches (138ms/cmd vs
247ms/cmd for separate invocations). Works as a pipe or TTY prompt.- Disconnect recovery. The daemon now listens for
browser.on('disconnected')and self-shuts cleanly when the user's
browser quits or a scratch browser crashes. State file gets cleared,
nextghax attachis fresh. CLI-side, Playwright "browser has been
closed" / "Target page has been closed" errors get rewritten to
"browser has disconnected — runghax attachto reconnect". ghax try [<js>] [--css <rules>] [--selector <sel>] [--measure <expr>] [--shot <path>]— live-injection fix-preview. Composable wrapper over
page.evaluate+page.screenshotfor the "mutate the live page,
measure, maybe screenshot" loop. Revert = reload the page.ghax perf [--wait <ms>]— Core Web Vitals + navigation timing. Reads
LCP (with size + URL), FCP, FP, CLS, TTFB, INP, long-task count, plus
full navTiming breakdown. LCP/CLS/longtask come via a buffered
PerformanceObserver.ghax find <url-substring>— list tabs whose URL contains the
substring. Returns[{id, url, title}].ghax new-window [url]— open a new OS-level window via
Target.createTarget({ newWindow: true, background: true }). Same
profile, so auth + extensions carry over. Does NOT steal focus.ghax tab <id> --quiet— skipbringToFront. Lets an agent lock onto
a tab without raising the window or stealing focus.ghax attachergonomics: auto-port fallback (scans :9222-9230 and
picks the first free one on--launch), multi-CDP picker,--headless
flag (scratch-profile only), and a clearer kind-mismatch error when
--browser chromeis asked for but only Edge is running.ghax console --dedup— groups repeated entries by (level, text)
into[{level, text, count, firstAt, lastAt, url, source, stack}]
sorted by count desc. On capture,pageerrorevents now include a
parsed stack[{fn, url, line, col}]via a new V8 stack-trace parser.ghax network --status <code|family|range>filter —--status 404
(exact),--status 4xx(family),--status 400-499(range).ghax network --har <path>— export captured entries as HAR 1.2 JSON
consumable by Charles, har-analyzer, WebPageTest, and the Chrome
DevTools network panel.- Request + response headers captured on every network entry (not
just URL + status). ResponsestatusTextanddurationalso
captured. test/cross-browser.ts+bun run test:cross-browser— iterates every
Chromium-family browserdetectBrowsers()finds, launches each
headless in a disposable scratch profile, runs the full smoke suite
against it, tabulates pass/fail + timing per browser. First baseline:
Edge 64/64 in 24.3s, Chrome 64/64 in 26.5s.test/benchmark.ts+bun run test:benchmark— headless CLI benchmark
against gstack-browse, playwright-cli, and agent-browser on a
6-step workflow.ghax profile [--duration sec] [--heap] [--extension <id>]— CDP
Performance.getMetricssnapshot for the active tab or an
extension service worker. Optional duration-based delta capture and
heap snapshot (writes a.heapsnapshotloadable in DevTools).ghax console --follow/ghax network --follow/ghax ext sw <id> logs --follow— live Server-Sent-Events streaming. Daemon exposes
/sse/console,/sse/network,/sse/ext-sw-logs/<ext-id>; CLI
consumes and prints each event as JSON. Ctrl-C exits 0.ghax ext sw <id> logs [--last N] [--errors]— dedicated SW
console buffer (subscribes toRuntime.consoleAPICalled+
Runtime.exceptionThrownon first call). Persists across reads,
auto-resubscribes after hot-reload.ghax ext popup <id> eval <js>+ghax ext options <id> eval <js>
— same shape asext panel, for the popup and options pages.ghax diff-state <before.json> <after.json>— structural JSON
diff. Emits RFC-6901-style paths with+/-/~prefixes.ghax ship [--message "..."] [--no-check] [--no-build] [--no-pr] [--dry-run]— opinionated commit + push + PR workflow.ghax canary <url> [--interval sec] [--max sec] [--out r.json] [--fail-fast]— periodic prod health check. Goto + snapshot +
capture console errors + HTTP >=400 responses per cycle.ghax review [--base origin/main] [--diff]— emits a Claude-ready
review prompt wrapping the branch's diff against a base.ghax pair status— v0 SSH-tunnel setup instructions.ghax qa— orchestrated QA pass over a URL list.ghax qa --crawl <root>— auto URL discovery via<root>/sitemap.xml,
falls back to same-origin<a href>scraping up to--depth Nhops.ghax is <visible|hidden|enabled|disabled|checked|editable> <@ref|selector>— assertion command. Exit 0 if condition holds, 1
otherwise.ghax storage [local|session] [get|set|remove|clear|keys] [key] [value]— page-level localStorage / sessionStorage.ghax ext message <ext-id> <json-payload>—chrome.runtime.sendMessage
wrapper.ghax gesture dblclick <x,y>+ghax gesture scroll <dir> [amount]—
real CDPInput.dispatch*gestures.ghax attach --launch --load-extension <path> [--data-dir <path>]—
pass-through for Chrome's--load-extension+ scratch profile.- Invariant enforcement:
ctx.refsis now cleared when the active
tab changes (viatab <id>ornew-window). Previously the
"refs survive only until next snapshot" rule held within a tab but
broke across tab switches —@e3from tab A could silently resolve
against tab B's DOM. A new smoke check (refs cleared on tab switch)
asserts the invariant. test/smoke.ts— 80-check harness against a live browser.test/hot-reload-smoke.ts— fully scripted hot-reload verification.test/fixtures/test-extension/— minimal MV3 fixture.
Changed
- ghax CLI: TypeScript/Bun → Rust 2021 edition. Single source of truth.
- Distribution: 61 MB Bun-compiled universal blob → ~2.6 MB stripped Rust
binary on Apple Silicon (~10 MB on Linux x64) per platform. - Cold start: ~70 ms (P50) → ~20 ms (P50). P99 ~600 ms → ~20 ms.
- Build: now requires Rust toolchain (1.80+). Bun stays as a dev tool for
the daemon bundle (bun build --target=node) and the test runner. - Daemon DRY pass: three new helpers collapse repeated shapes.
evalInTarget()centralises nineRuntime.evaluatesites with
consistentexceptionDetailshandling.getSwTarget()owns the...
Version 0.4.1-rc.3
Release Notes
Added
-
ghax xpath <expression> [--limit N]— query the page's DOM with an
XPath expression, return every matching element with its tag, text
preview, and bounding box. XPath is also usable via Playwright's
xpath=...prefix in every selector-accepting command (click,
fill,screenshot,is, etc.). -
ghax box <@ref|selector>— return{x, y, width, height}of the
first matching element. Resolves snapshot refs (@e3,@c1) or any
selector form. -
ghax attach --capture-bodies[=<url-glob>]— opt-in response-body
capture. Without a pattern, captures every JSON/text-like response
(application/json, text/*, javascript, xml, html, css, graphql)
up to 32KB each. With a glob (e.g.'*/api/*'), restricts to
matching URLs so browsing doesn't blow memory on images or chunks.
Bodies past 32KB truncate with a[truncated N bytes]marker.
Included in HAR export when the content is available. -
ghax console --source-maps— resolve bundled stack frames back to their
original source locations via the page's source maps. Each captured
pageerroralready parses its stack; with--source-maps, every frame
is run through the daemon's source-map cache: fetch the script, read
itssourceMappingURLcomment (or data: URI), parse the map, look up
the original position. Result includes the resolved{url, line, col}
plus{bundledUrl, bundledLine, bundledCol}for correlation. Silent
fallback to the bundled frame on any failure (script unreachable, no
map comment, parse error, position out of range). Adds ~60KB to the
daemon bundle for thesource-maplibrary; zero cost when the flag
isn't used. -
ghax shell— interactive REPL. Reads commands from stdin, tokenises
with shell-ish quoting (single/double quotes, backslash escapes),
re-enters the main dispatcher per line. One process for the whole
session, so the per-command Bun spawn cost goes away. Measured 1.8x
faster for 10-command batches (138ms/cmd vs 247ms/cmd for separate
invocations). Works as a pipe (cat script.txt | ghax shell) or
interactively (TTY prompt, history, Ctrl-D to exit).exit/quit
stop the loop;#lines are comments. -
Disconnect recovery. The daemon now listens for
browser.on('disconnected')and self-shuts cleanly when the user's
browser quits or a scratch browser crashes. State file gets cleared,
nextghax attachis fresh. CLI-side, "browser has been closed" /
"Target page has been closed" errors get rewritten to
"browser has disconnected — runghax attachto reconnect" instead
of surfacing as a raw Playwright stack trace. -
ghax try [<js>] [--css <rules>] [--selector <sel>] [--measure <expr>] [--shot <path>]— live-injection fix-preview. Composable wrapper over
page.evaluate+page.screenshotfor the "mutate the live page,
measure, maybe screenshot" loop. CSS appends<style class="ghax-try">;
JS is IIFE-wrapped with optionalelbinding;--measureruns
post-mutation. Revert = reload the page. -
ghax perf [--wait <ms>]— Core Web Vitals + navigation timing. Reads
LCP (with size and URL), FCP, FP, CLS, TTFB, INP, long-task count,
plus full navTiming breakdown (DNS, TCP, TLS, TTFB, response,
DOMInteractive, DOMContentLoaded, load, transfer/encoded/decoded
sizes). LCP/CLS/longtask come via a buffered PerformanceObserver — they
don't live in the default timeline buffer. -
ghax find <url-substring>— list tabs whose URL contains the
substring. Returns[{id, url, title}]. Pipe the id intoghax tab
to attach to a matching tab, or fall through tonew-window. -
ghax new-window [url]— open a new OS-level window via
Target.createTarget({ newWindow: true, background: true }). Same
profile, so auth + extensions carry over. Does NOT steal focus.
Auto-locks the new tab as the daemon's active tab so subsequent
commands land in the fresh window without an extratabstep. -
ghax tab <id> --quiet— skipbringToFront. Lets an agent lock onto
a tab without raising the window or stealing focus from whatever
the user is actively doing. -
ghax attachergonomics: auto-port fallback (--launchwithout
--portscans :9222-9230 and picks the first free one, prints the
chosen port on fallback), multi-CDP picker (plainghax attachwith
multiple live CDPs shows a numbered selector),--headlessflag
(scratch-profile only — spawns with--headless=newso extensions
still work), and a clearer kind-mismatch error when--browser chrome
is asked for but only Edge is running. -
ghax console --dedup— groups repeated entries by (level, text)
into[{level, text, count, firstAt, lastAt, url, source, stack}]
sorted by count desc. Turns "500 identical errors" into one row with
count=500. On capture,pageerrorevents now include a parsed stack
[{fn, url, line, col}]via a new V8 stack-trace parser in
buffers.ts. -
ghax network --status <code|family|range>filter —--status 404
(exact),--status 4xx(family),--status 400-499(range). -
ghax network --har <path>— export captured entries as HAR 1.2 JSON
consumable by Charles, har-analyzer, WebPageTest, and the Chrome
DevTools network panel. -
Request + response headers captured on every network entry (not
just URL + status). ResponsestatusTextanddurationalso
captured. Bodies are still not captured by default (memory cost too
high for a 5k rolling buffer). -
test/cross-browser.ts+bun run test:cross-browser— iterates every
Chromium-family browserdetectBrowsers()finds, launches each
headless in a disposable scratch profile, runs the full smoke suite
against it, tabulates pass/fail + timing per browser. Arc is filtered
out (no CDP). First baseline: Edge 64/64 in 24.3s, Chrome 64/64 in
26.5s. -
test/benchmark.ts+bun run test:benchmark— headless CLI benchmark
against gstack-browse, playwright-cli, and agent-browser on a
6-step workflow (launch → goto → text → js → screenshot → snapshot →
close). Reports cold (end-to-end) and warm (per-command, session
reused) numbers. First baseline: ghax 65ms/cmd, gstack 56ms/cmd,
agent-browser 178ms/cmd, playwright-cli 476ms/cmd. -
ghax profile [--duration sec] [--heap] [--extension <id>]— CDP
Performance.getMetricssnapshot for the active tab or an
extension service worker. Optional duration-based delta capture and
heap snapshot (writes a.heapsnapshotloadable in DevTools).
Report written to.ghax/profiles/<ts>.json. -
ghax console --follow/ghax network --follow/ghax ext sw <id> logs --follow— live Server-Sent-Events streaming. Daemon exposes
/sse/console,/sse/network,/sse/ext-sw-logs/<ext-id>;
CLI consumes and prints each event as JSON. Ctrl-C exits 0. -
ghax ext sw <id> logs [--last N] [--errors]— dedicated SW
console buffer (subscribes toRuntime.consoleAPICalled+
Runtime.exceptionThrownon first call). Persists across reads,
auto-resubscribes after hot-reload. -
ghax ext popup <id> eval <js>+ghax ext options <id> eval <js>
— same shape asext panel, for the popup and options pages. URL
pattern matching against/popup.html,/options.html, etc. -
ghax diff-state <before.json> <after.json>— structural JSON
diff. Emits RFC-6901-style paths (/a/b/0) with+/-/~
prefixes. Supports--jsonfor machine output. -
ghax ship [--message "..."] [--no-check] [--no-build] [--no-pr] [--dry-run]— opinionated commit + push + PR workflow. Runs
typecheck + build first; on a non-main branch, fires
gh pr create --fillor reports the existing PR URL. -
ghax canary <url> [--interval sec] [--max sec] [--out r.json] [--fail-fast]— periodic prod health check. Goto + snapshot +
capture console errors + HTTP >=400 responses per cycle. Appends
a rolling log to.ghax/canary-<host>.log; writes a structured
JSON report on exit. -
ghax review [--base origin/main] [--diff]— emits a Claude-ready
review prompt wrapping the branch's diff against a base. No API
calls — stdout only, user pipes toclaudeor pastes. -
ghax pair status— v0 SSH-tunnel setup instructions. A proper
token-auth multi-tenant mode is deferred to v0.5. -
ghax qa— orchestrated QA pass over a URL list. Flow: attach →
goto each URL →snapshot -i→ record console errors + HTTP >=400
responses → writeqa-report.json. Flags:--url(repeatable),
--urls a,b,c, positional URLs, or stdin JSON array.--out,
--screenshots,--annotate,--gif. -
ghax qa --crawl <root>— auto URL discovery. Tries
<root>/sitemap.xmlfirst; falls back to same-origin<a href>
scraping up to--depth Nhops (default 1), capped by--limit N
(default 20). -
ghax is <visible|hidden|enabled|disabled|checked|editable> <@ref|selector>
— assertion command. Exit 0 if condition holds, 1 otherwise. -
ghax storage [local|session] [get|set|remove|clear|keys] [key] [value]
— page-level localStorage / sessionStorage. -
ghax ext message <ext-id> <json-payload>—chrome.runtime.sendMessage
wrapper. -
ghax gesture dblclick <x,y>+ghax gesture scroll <dir> [amount]—
real CDPInput.dispatch*gestures. -
ghax attach --launch --load-extension <path> [--data-dir <path>]—
pass-through for Chrome's--load-extension+ scratch profile. -
test/smoke.ts— 64-check harness against a live browser (grew from
24 as the v0.4 + debugging-tier-1 surface landed). -
test/hot-reload-smoke.ts— fully scripted hot-reload verification. -
test/fixtures/test-extension/— minimal MV3 fixture.
Changed
ghax ext listenriches each entry with manifest-derivedname,
version, andenabledfields.ghax attachdefaults changed: no--portnow scans :9222-9230 for
existing CDPs (multiple → picker, one → attach). With--launchand
no--port, auto-picks the first free port in the same range. Pass
--port <n>explicitly to opt ...