diff --git a/session-logs/2026-06-07-mike-firefox-driver.md b/session-logs/2026-06-07-mike-firefox-driver.md new file mode 100644 index 0000000..ef50118 --- /dev/null +++ b/session-logs/2026-06-07-mike-firefox-driver.md @@ -0,0 +1,93 @@ +# 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 `). + +## Commands & Outputs + +```bash +# 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: `8a975978` — `feat(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.