skills/tkersey/dotfiles/web-browser

web-browser

SKILL.md

Web Browser

When to use

  • Navigate or interact with live websites.
  • Click buttons, fill forms, or extract page content.
  • Evaluate JavaScript in a real browser context.
  • Capture screenshots for debugging or review.
  • UI validation and smoke testing in a real browser.
  • Measure and gate latency regressions in web-browser tools with the local benchmark scripts.

Requirements

  • Run commands from codex/skills/web-browser/ (so ./tools/*.js resolves).
  • Node.js (ESM + top-level await) and repo deps installed (notably puppeteer-core).
  • Chrome/Chromium installed, or set CODEX_CHROME_PATH.

Quick start

# Start Chrome with CDP on :9222
./tools/start.js

# Safer (recommended on your laptop): avoid killing an existing Chrome session
./tools/start.js --no-kill

# Open a deterministic tab, then operate on it
./tools/nav.js https://example.com --new
./tools/eval.js 'document.title'
./tools/screenshot.js

Loop: take small steps → inspect state (eval.js / screenshot.js) → repeat.

Performance pass ($lift)

Use this when optimizing or validating web-browser tool latency.

./tools/start.js --no-kill
./tools/nav.js https://example.com --new

cd tools
npm run -s bench:all -- --warmup 5 --samples 30 --screenshot-samples 20

For stricter CI-style gating, add p95 budgets:

cd tools
npm run -s bench:all -- \
  --warmup 5 \
  --samples 30 \
  --screenshot-samples 20 \
  --budget-nav-p95-ms 310 \
  --budget-eval-p95-ms 220 \
  --budget-screenshot-p95-ms 320 \
  --budget-start-p95-ms 140

Configuration

  • Flags
    • start.js: --port, --user-data-dir, --chrome-path, --profile, --no-kill
    • nav.js / eval.js / screenshot.js / pick.js: --port, --browser-url
  • Environment
    • CODEX_BROWSER_URL: CDP URL (used when no CLI --port/--browser-url)
    • CODEX_BROWSER_PORT (alias: CODEX_CDP_PORT): CDP port for localhost
    • CODEX_BROWSER_USER_DATA_DIR: Chrome user data dir (used by start.js)
    • CODEX_CHROME_PATH (aliases: CHROME_PATH, PUPPETEER_EXECUTABLE_PATH): Chrome/Chromium executable

Targeting (active tab)

All tools operate on the “active tab”, defined as the last page returned by puppeteer.pages() (roughly: the most recently opened tab).

  • Prefer ./tools/nav.js <url> --new when you want deterministic targeting.
  • If actions hit the “wrong” tab, close extra tabs or open a fresh one.

Common commands

# See all options (safe; does not start/kill Chrome)
./tools/start.js --help

# Use a non-default port
./tools/start.js --port 9223 --no-kill
./tools/nav.js --port 9223 https://example.com --new

# Or configure once via env
export CODEX_BROWSER_URL=http://localhost:9223
./tools/eval.js 'document.title'

# Wait for a selector (polling with timeout; good for SPAs)
./tools/eval.js '(async () => { for (let i = 0; i < 50; i++) { const el = document.querySelector("button[type=submit]"); if (el) return true; await new Promise(r => setTimeout(r, 100)); } return false; })()'

# Screenshot current viewport
# Prints a PNG filepath in your system temp dir
./tools/screenshot.js

# Pick elements interactively
# Prints tag/id/class/text/html/parents for one element (or many via Cmd/Ctrl+click)
./tools/pick.js "Click the submit button"

Tip: use pick.js to inspect attributes/text, then craft a selector for eval.js.

Recipes

# Scrape structured data (return an array of objects for readable output)
./tools/eval.js 'Array.from(document.querySelectorAll("a"), a => ({ href: a.href, text: a.textContent?.trim() }))'
  • Login flows
    • Dedicated automation profile: run ./tools/start.js, log in once, then reuse the persisted profile in your user-data-dir (default: ~/.cache/scraping).
    • Default-profile bootstrap: run ./tools/start.js --profile when you truly need existing cookies/logins.

Security & privacy

  • ./tools/start.js --profile uses rsync -a --delete to copy your default Chrome profile into your user-data-dir (cookies/sessions/PII).
  • Treat your user-data-dir (default: ~/.cache/scraping) as sensitive; avoid on shared machines and delete it when done if needed.

Troubleshooting

  • ✗ Failed to connect to Chrome via CDP: run ./tools/start.js (or set CODEX_BROWSER_URL/--port to match your Chrome).
  • ✗ No active tab found: open a page first (e.g., ./tools/nav.js https://example.com --new).
  • Port 9222 is busy: pick a new one (./tools/start.js --port 9223).
  • Selector flakiness: use the polling pattern above; SPAs often need a wait after domcontentloaded.
  • Chrome path not found: set CODEX_CHROME_PATH or pass --chrome-path.

Pitfalls

  • ./tools/start.js defaults to killing Chrome processes (use --no-kill to avoid this).
  • Use single quotes around JS to avoid shell-escaping issues.

Prove $lift uses Zig

When you need objective proof that the local $lift tooling is Zig-based:

bench_stats --help 2>&1 | sed -n '1,6p'
perf_report --help 2>&1 | sed -n '1,6p'
brew cat tkersey/tap/lift | rg -n 'depends_on \"zig\" => :build|build-exe|bench_stats.zig|perf_report.zig'

References

  • codex/skills/web-browser/tools/start.js
  • codex/skills/web-browser/tools/nav.js
  • codex/skills/web-browser/tools/eval.js
  • codex/skills/web-browser/tools/screenshot.js
  • codex/skills/web-browser/tools/pick.js
  • codex/skills/web-browser/tools/bench-eval.js
  • codex/skills/web-browser/tools/bench-all.js
Weekly Installs
39
GitHub Stars
43
First Seen
Feb 20, 2026
Installed on
opencode39
github-copilot38
codex38
kimi-cli38
gemini-cli38
cursor38