Files
claudetools/session-logs/2026-06-07-mike-firefox-driver.md
Mike Swanson 8b57a5c770 sync: auto-sync from GURU-5070 at 2026-06-07 07:54:09
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-07 07:54:09
2026-06-07 07:54:13 -07:00

8.5 KiB

Session Log — Firefox Browser Driver + claude-in-chrome Removal

User

  • User: Mike Swanson (mike)
  • Machine: GURU-5070
  • Role: admin

Session Summary

Mike wanted out of Chrome-based browser automation: he dislikes Chrome and considers the claude-in-chrome MCP extension garbage. Two deliverables came out of the session — disable the Chrome connector across his Claude Code sessions, and build a Firefox-driven equivalent so that "look at a website / interact with it / collect the logs" requests run against his preferred browser.

Investigation showed claude-in-chrome is not a JSON mcpServers entry; it is a built-in Claude Code browser connector toggled by four top-level keys in ~/.claude.json (claudeInChromeDefaultEnabled, cachedChromeExtensionInstalled, hasCompletedClaudeInChromeOnboarding, and a chromeExtension paired-device record named "Browser 1"). The repo already contained a non-extension Chrome driver — .claude/scripts/cdp.py (DevTools Protocol) — built specifically because the extension had invisible windows and screenshots that never landed on disk. The task was therefore to add a Firefox sibling, not to fix the extension.

Built .claude/scripts/ff.py, a Playwright-based Firefox driver modeled on cdp.py's CLI. Because Firefox dropped most CDP support, the stateless "new connection per command" approach cdp.py uses against Chrome's debug port does not port. Instead ff.py launch spawns a small background daemon that holds one Playwright Firefox page on a persistent profile (~/.claude/ff-profile, so logins survive); the other subcommands are thin HTTP clients to it on localhost:9333. The daemon accumulates console and network logs for retrieval — directly serving the "collect the logs" use case. Commands: launch [url] [--headless], status, nav, shot, click, type, key, eval, console [--clear], network [--clear], stop.

Installed Playwright + the Firefox browser binary, then debugged two real bugs (see Problems). Verified end-to-end headless against example.com: launch, status, eval (returned "Example Domain"), screenshot (26 KB real PNG render confirmed visually), network capture (200 response logged), and console capture (caught an injected console.log). Disabled the Chrome connector in ~/.claude.json (backup saved) and documented the driver in a new memory reference. Finally committed and pushed the four .claude/ files via the Gitea Agent, deliberately excluding the unrelated guru-rmm submodule bump and guru-connect untracked dir.

Key Decisions

  • Daemon architecture instead of cdp.py's stateless model. Firefox dropped most CDP support, so a long-lived Playwright page behind a local HTTP control port is the robust way to preserve the "nav now, screenshot later, page persists" UX and to accumulate console/network logs.
  • Headed (visible) by default, persistent profile. Lets Mike log into authenticated apps once and keeps sessions. The standing rule that Claude never types passwords still holds regardless.
  • Disable the connector via ~/.claude.json keys, not by uninstalling the extension. Flipping claudeInChromeDefaultEnabled/cachedChromeExtensionInstalled to false and dropping the chromeExtension pairing is the in-config switch; takes effect on next Claude Code restart.
  • Installed Playwright into both Python 3.12 and 3.14. py honors a script shebang, so py ff.py resolves to a different interpreter than bare py -c; installing into both makes the tool interpreter-agnostic.
  • Scoped the commit to the four .claude/ files only. Excluded the guru-rmm submodule pointer change and the guru-connect untracked dir as unrelated. Bundled the pre-existing untracked reference_antigravity_agy_not_headless.md because its MEMORY.md index line was already interleaved in the same file edit — keeps the index non-dangling.

Problems Encountered

  • Daemon died with ModuleNotFoundError: No module named 'playwright' despite the launcher having it. Root cause: the py launcher reads a script's shebang. ff.py's #!/usr/bin/env python made py ff.py resolve python via PATH → Python 3.12 (no playwright), while bare py -c used the default 3.14 (has playwright). The daemon inherited 3.12 via sys.executable. Fixed by installing Playwright + Firefox into Python 3.12 as well; both interpreters now work. A no-shebang probe script worked, which initially masked the cause.
  • launch never returned control / startup crashes were invisible. The detached daemon inherited the parent's stdout pipe, so the caller hung and any crash went unseen. Fixed by redirecting the detached child's stdio to ~/.claude/ff-daemon.log (stdin=DEVNULL, stdout/stderr=logfile).
  • Push raced a Howard auto-sync commit. origin/main advanced (5a9fe1bc) between commit and push; the Gitea Agent rebased the single commit on top (no content change) and pushed cleanly.

Configuration Changes

Created:

  • .claude/scripts/ff.py — Playwright Firefox driver (daemon + CLI).
  • .claude/memory/reference_ff_firefox_driver.md — memory reference documenting ff.py.

Modified:

  • .claude/memory/MEMORY.md — added index line for the Firefox driver (and carried a pre-existing index line for the antigravity reference).
  • ~/.claude.json (machine-local, NOT in repo) — claudeInChromeDefaultEnabled=false, cachedChromeExtensionInstalled=false, removed chromeExtension pairing. Backup: ~/.claude.json.bak-prechrome.

Committed alongside (pre-existing untracked, bundled for index consistency):

  • .claude/memory/reference_antigravity_agy_not_headless.md

Installed (machine-local):

  • Playwright 1.60.0 + Firefox browser binary into Python 3.14 and Python 3.12.

Credentials & Secrets

None created or discovered this session. The Firefox driver uses a dedicated persistent profile (~/.claude/ff-profile) and the standing rule remains: Claude does not type passwords; Mike logs into authenticated apps in the visible window himself.

Infrastructure & Servers

  • Firefox driver control port: localhost:9333 (env FF_PORT).
  • Firefox profile dir: C:\Users\guru\.claude\ff-profile (env FF_PROFILE).
  • Daemon log: C:\Users\guru\.claude\ff-daemon.log.
  • Existing Chrome driver (cdp.py) debug port: localhost:9222 — unchanged.
  • Python interpreters on GURU-5070: 3.14 (C:\Users\guru\AppData\Local\Programs\Python\Python314\python.exe, py default) and 3.12 (...\Python312\python.exe, selected by py <shebang-script>).

Commands & Outputs

# Install Playwright + Firefox (run for BOTH interpreters)
py -m pip install playwright && py -m playwright install firefox
"/c/Users/guru/AppData/Local/Programs/Python/Python312/python.exe" -m pip install playwright
"/c/Users/guru/AppData/Local/Programs/Python/Python312/python.exe" -m playwright install firefox

# Driver usage
py .claude/scripts/ff.py launch example.com        # visible (default); add --headless to hide
py .claude/scripts/ff.py status
py .claude/scripts/ff.py nav rmm.azcomputerguru.com
py .claude/scripts/ff.py shot D:/tmp/page.png
py .claude/scripts/ff.py eval "document.title"
py .claude/scripts/ff.py console                   # collected console/pageerror msgs (JSON)
py .claude/scripts/ff.py network                   # collected responses (JSON)
py .claude/scripts/ff.py stop

Verification output (headless example.com): status returned {"ok":true,"url":"https://example.com/","title":"Example Domain"}; shot wrote a 26303-byte PNG (confirmed a real render); network logged a 200; console caught an injected hello-from-ff.

Pending / Incomplete Tasks

  • claude-in-chrome disable applies on the next Claude Code restart; the Chrome tools are still loaded in the current session. If they reappear after restart (the app re-persists ~/.claude.json state), toggle off in the connectors UI or re-run the key edit after fully quitting.
  • Headed mode was not visually exercised this session (headless path proved Playwright Firefox works; headed is the same code with headless=False). Worth a quick visible-window smoke test next time a real site is requested.
  • Optional: teach the /rmm or site-review flows to prefer ff.py over cdp.py.

Reference Information

  • Commit: 8a975978feat(scripts): add Firefox driver (ff.py) via Playwright; disable claude-in-chrome (4 files, +330).
  • Push: 5a9fe1bc..8a975978 main -> main (origin).
  • Memory: .claude/memory/reference_ff_firefox_driver.md (peer of reference_cdp_chrome_driver.md).
  • claude-in-chrome backup: C:\Users\guru\.claude.json.bak-prechrome.
  • Playwright version: 1.60.0.