Controlling a User's Authenticated Chrome via agent-browser (Linux)
agent-browser
Install once if not present:
npm i -g agent-browser
Do not run agent-browser install — that downloads a testing Chromium with no user session, which defeats the purpose of this skill.
Before running any agent-browser commands, load the actual workflow content from the CLI:
agent-browser skills get core # workflows, common patterns, troubleshooting
agent-browser skills get core --full # include full command reference and templates
The CLI serves skill content that always matches the installed version, so instructions never go stale.
The Core Constraint
Chrome refuses remote debugging on the default user-data-dir (~/.config/google-chrome) for security. You'll see this error in /tmp/chrome.log:
DevTools remote debugging requires a non-default data directory. Specify this using --user-data-dir.
The fix: copy the user's profile to a non-default dir, kill all Chrome, launch from the copy with the debug port, connect agent-browser via CDP.
Step 0: Find the Right DISPLAY
Everyone's Linux setup is different. Before launching Chrome, find the X display where the user's desktop session is running:
# Option A: check what the user's shell already has
echo $DISPLAY
# Option B: scan running processes for an X display
ps aux | grep -oP 'DISPLAY=\S+' | sort -u
# Option C: list /tmp/.X11-unix sockets (each one is a display)
ls /tmp/.X11-unix/
# X0 → :0, X1 → :1, etc.
# Option D: look at who's logged into the X session
who | grep '(:[0-9]'
Common values are :0, :1, or :10. Use whatever you find — substitute it for DISPLAY=:1 in the recipe below. If you're in a Kasm/VNC environment, :1 is typical, but verify first.
If echo $DISPLAY returns something, that's the right one. If it's empty, you're likely in an SSH session without X forwarding — look at the X11-unix sockets or check with the user.
The Recipe (run in order, every time)
Before going through the full setup, check if Chrome is already running with the debug port — if so, you can skip straight to connecting:
# Quick check: is port 9222 already listening?
curl -s -m 2 http://127.0.0.1:9222/json/version | head -2
If that returns a {"Browser": "Chrome/... response, Chrome is already up with remote debugging enabled. Run the verification one-liner from the Verification section below — if you land on the target site (not a sign-in page), you're done, skip the rest of the recipe.
If the port is not listening, or you land on a sign-in page, proceed with the full recipe:
# 1. Kill ALL Chrome processes (any leftover holds the SingletonLock)
pkill -9 -f 'google-chrome\|/opt/google/chrome/chrome'
sleep 3
pgrep -af chrome | grep -v 'crashpad\|chrome-devtools\|grep' # confirm empty
# 2. Verify the user's session is fresh in their profile
ls -la ~/.config/google-chrome/Default/Cookies # check mtime is recent
# 3. Copy profile to a non-default location, strip the lock files
rm -rf /tmp/chrome-debug-profile
cp -a ~/.config/google-chrome /tmp/chrome-debug-profile
rm -f /tmp/chrome-debug-profile/Singleton{Lock,Cookie,Socket}
# 4. Launch Chrome with debug port on the display the user can see
# Replace :1 with the DISPLAY value you found in Step 0
DISPLAY=:1 nohup google-chrome \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/chrome-debug-profile \
--no-sandbox --no-first-run --no-default-browser-check \
'https://target-url-to-load-immediately' \
>/tmp/chrome.log 2>&1 &
disown
# 5. Confirm port is listening (3-7s after launch)
sleep 6
curl -s -m 3 http://127.0.0.1:9222/json/version | head -2
# Expect: {"Browser": "Chrome/...
# 6. Connect agent-browser to the running Chrome
agent-browser --session <name> connect 9222
agent-browser --session <name> get url # sanity check
From here, every agent-browser --session <name> ... command drives the same Chrome window the user can see.
Why Each Flag Matters
| Flag | Why |
|---|---|
DISPLAY=:1 |
Targets the X display where the user's session is running — find the right value with Step 0 |
--remote-debugging-port=9222 |
The CDP endpoint agent-browser connects to |
--user-data-dir=/tmp/chrome-debug-profile |
Required by Chrome's "non-default data dir" rule |
--no-sandbox |
Required in many container/coder envs (no SUID sandbox available) |
--no-first-run --no-default-browser-check |
Skip welcome dialogs that block initial render |
disown |
So the bg job survives if the parent shell exits |
Gotchas
1. /tmp gets wiped between sessions.
Coder/sandbox envs sometimes clean /tmp. If /tmp/chrome-debug-profile has shrunk dramatically since last use (e.g., 168M → 59M), the session is corrupt — re-copy from ~/.config/google-chrome.
2. User must be signed in somewhere first.
Re-copying from ~/.config/google-chrome only works if the user has a valid session there. If cookies have expired or been invalidated, you'll land on a sign-in page you can't get past — ask the user to re-auth in their normal Chrome first, then re-copy.
3. Multiple Chrome instances ≠ multiple debug ports.
If the user runs another google-chrome (without flags), it joins the existing instance. Only the first one with --remote-debugging-port binds the port. Always pkill first.
4. Old singleton files block startup.
After cp -a, always rm -f Singleton{Lock,Cookie,Socket} from the copy or Chrome refuses to launch.
5. Don't try to log in via the debug Chrome. Sites that block automated sign-in (MFA prompts, CAPTCHA, OAuth flows, headless browser detection) will block you here too. The recipe sidesteps this entirely by piggybacking on a session the user already established in their normal Chrome.
6. agent-browser can disappear.
agent-browser is npm-installed globally, and nvm/tmp cleanups can wipe it. Reinstall with npm i -g agent-browser (~5s), then connect 9222 again — Chrome's session survives independently.
7. Never run agent-browser install.
agent-browser install downloads a separate testing Chromium binary with no user profile or cookies. Always use the system google-chrome binary.
8. Initial page blank/loading.
Chrome takes 3-7s after launch before CDP responds. Always sleep 6 then curl /json/version before connecting.
Verification
After launch, confirm the whole chain works:
agent-browser --session <name> connect 9222 && \
agent-browser --session <name> get url
- Real URL on the target site → you're in.
- A sign-in or auth page → copied session is stale or expired. Ask the user to re-auth in their normal Chrome, then re-copy.
When NOT to Use This
If the target site doesn't require authentication, skip the profile copy and launch a fresh empty profile:
google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/fresh-chrome --no-sandbox &
The copy-the-user's-profile dance is only needed for sites where you can't automate login — MFA, CAPTCHA, OAuth flows, or headless browser detection at the sign-in page.