con un clic
dev-browser
// Fallback browser automation with persistent Chrome state. Use only when Browser Use is unavailable or blocked.
// Fallback browser automation with persistent Chrome state. Use only when Browser Use is unavailable or blocked.
ALWAYS use this skill when working with convex or kitcn. Covers both setup and e2e feature paths using cRPC + ORM + auth + React.
Codex autoreview/code review closeout: local dirty changes, PR branch vs main, parallel tests.
Open a concise GitHub follow-up for reusable browser-use limitations. Use when browser automation is blocked by a likely tool-side issue that is worth fixing separately, especially for clicks, dropdowns, file inputs, focus traps, or other repeatable agent/browser failures.
Work heavyweight framework or library tasks with planning-first research, selective deep analysis, and rigorous handoff
Work a task end-to-end with lean context gathering, implementation, and verification
Read every doc in www and packages/kitcn/skills/kitcn, sync to active changeset(s), and track with checkmarks.
| description | Fallback browser automation with persistent Chrome state. Use only when Browser Use is unavailable or blocked. |
| name | dev-browser |
| metadata | {"skiller":{"source":".agents/rules/dev-browser.mdc"}} |
Use this only as the fallback browser path when [@browser-use](plugin://browser-use@openai-bundled) is unavailable or blocked.
Do not substitute Puppeteer, standalone Playwright, or raw Chrome DevTools for this fallback path.
npm install -g dev-browser
dev-browser install
Run dev-browser --help to learn more.
dev-browser --connect http://127.0.0.1:9222 by default. Do not preflight 9222 first.9222 after a direct dev-browser --connect http://127.0.0.1:9222 attempt fails.127.0.0.1:9222. Do not spin up disposable browser instances unless the user asks.--user-data-dir for that debug browser, not the user's normal daily Chrome data dir.open -na "Google Chrome" --args ... --remote-debugging-port=9222 so it opens as a separate Chrome instance without hijacking the user's normal window.browser.getPage("persistent-main") for the main app.dev-browser instead of agent-browser or next-devtools browser_eval.dev-browser gets blocked by a human prompt or loops on the same step, stop and ask the user to unblock.Use this only after dev-browser --connect http://127.0.0.1:9222 fails because no reusable debug Chrome is available or the CDP endpoint is broken.
--user-data-dir as mandatory, not optional. Chrome 136+ expects remote debugging to happen from a dedicated profile.dev. Do not use the user's normal daily Default profile as the source profile.--user-data-dir; do not point 9222 straight at the user's daily Chrome data dir.open -na "Google Chrome" --args ... for the debug browser. That starts a separate Chrome instance with the dedicated debug profile without touching the user's normal Chrome window.Use a dedicated browser/profile with:
--remote-debugging-address=127.0.0.1--remote-debugging-port=9222--user-data-dir=<debug-profile-dir>Sign in once in that dedicated browser and keep reusing it for agent work.
Quick sanity check:
curl -sS http://127.0.0.1:9222/json/version
Healthy output includes a JSON object with webSocketDebuggerUrl. Empty output or 404 means the wrong process owns 9222.
Then verify:
dev-browser --connect http://127.0.0.1:9222 <<'EOF'
const page = await browser.getPage("persistent-main");
console.log(await page.title());
EOF
If direct connect still cannot resolve CDP even though /json/version is healthy, connect with the exact websocket URL:
WS=$(curl -sS http://127.0.0.1:9222/json/version | jq -r '.webSocketDebuggerUrl')
dev-browser --connect "$WS" <<'EOF'
const page = await browser.getPage("persistent-main");
console.log(await page.title());
EOF
Default setup on macOS:
dev, not the daily Default profile.Local State.9222.python3 - <<'PY'
import json, pathlib
p = pathlib.Path('~/Library/Application Support/Google/Chrome/Local State').expanduser()
obj = json.loads(p.read_text())
for key, val in obj.get('profile', {}).get('info_cache', {}).items():
print(f"{key}\tname={val.get('name')}\tgaia_name={val.get('gaia_name')}")
PY
# Example: if `dev` maps to `Profile 1`, clone `Profile 1`.
mkdir -p "$HOME/.config/google-chrome-debug-profile/Default"
rsync -a --delete \
--exclude='Singleton*' \
--exclude='DevToolsActivePort' \
--exclude='lockfile' \
"$HOME/Library/Application Support/Google/Chrome/Profile 1/" \
"$HOME/.config/google-chrome-debug-profile/Default/"
cp "$HOME/Library/Application Support/Google/Chrome/Local State" \
"$HOME/.config/google-chrome-debug-profile/Local State"
open -na "Google Chrome" --args \
--user-data-dir="$HOME/.config/google-chrome-debug-profile" \
--profile-directory="Default" \
--remote-debugging-address=127.0.0.1 \
--remote-debugging-port=9222
Do not point 9222 at the normal daily Default Chrome profile.
If the wrong Chrome steals 9222, identify it with:
lsof -nP -iTCP:9222 -sTCP:LISTEN
Kill that listener and relaunch the dedicated debug browser. Do not keep debugging against a stale 404 or empty /json/version owner.