Control Chrome via the CDP protocol, reuse existing login sessions, and perform browser automation operations.
Prerequisites
- macOS / Linux / Windows (experimental), Google Chrome installed
- Node.js 20+
agent-browserInstalled:npm install -g agent-browser
⚠️ On first launch, it will kill the user's regular Chrome. Before launching, you must obtain the user's consent (see "Launch flow" below), otherwise the user may lose unsaved tabs/drafts.
Launch flow (mandatory steps in skill-mode)
Step 1: Detect the current state (no side effects)
node {SKILL_DIR}/scripts/setup-cdp-chrome.js 9222 --detect-only
Output in the form:
CDP_STATUS=ready # 已就绪,可直接复用
CDP_URL=http://127.0.0.1:9222/json/version
BROWSER=Chrome/148.0.7778.168
Or:
CDP_STATUS=needs-setup
CHROME_RUNNING=yes # 用户有 Chrome 在跑,启动会杀掉
CHROME_PID_COUNT=3
Step 2: Branch based on the detection result
CDP_STATUS=ready→ Use directlyagent-browser --cdp 9222 ...,Do not run setup。CDP_STATUS=needs-setupandCHROME_RUNNING=no→ Safe launch:node {SKILL_DIR}/scripts/setup-cdp-chrome.js 9222 --yesCDP_STATUS=needs-setupandCHROME_RUNNING=yes→ First confirm with the user using the current platform's interactive capability(Claude can useAskUserQuestion; TRAE Code asks directly in the main session and waits for a reply): inform them that N Chrome processes will be killed and unsaved work may be lost; after the user agrees, launch with--yes; if the user refuses, abandon this automation.
Why you cannot directly --yes: In a non-TTY environment (i.e., skill mode / Bash tool), if the script detects that Chrome is running and there is no --yes, it will exit with code 3 reporting NEEDS_CONSENT: ... and abort,will notsilently kill processes. This is an intentional fallback—but the skill flow should still ask the user first, rather than blindly passing --yes。
Launch script options
| Option | Description |
|---|---|
--detect-only |
Only detect, do not modify any state (used by skill) |
--yes |
Consent has been obtained, skip the interactive prompt |
--reset |
Clear before launch ~/chrome-debug-profile(used when login has expired) |
--profile <name> |
Use a non-Default Chrome profile (e.g., "Profile 1") |
--dry-run |
Print the steps to be executed without executing them |
Exit codes:0 success / 1 general error / 2 user refused (TTY) / 3 consent required but missing --yes。
Common operations
Open a page and wait for it to load
agent-browser --cdp 9222 open "<URL>"
agent-browser --cdp 9222 wait 3000
Extract page text
agent-browser --cdp 9222 eval 'document.body.innerText.substring(0, 8000)'
Extract Auth Token
agent-browser --cdp 9222 eval 'localStorage.getItem("token") || document.cookie'
Complex JS (containing quotes / $ / backticks)
Shell escaping is error-prone; use one of the following two methods:
# 1) base64 包裹
agent-browser --cdp 9222 eval -b "$(echo -n "document.querySelectorAll('a').length" | base64)"
# 2) heredoc + --stdin
cat <<'EOF' | agent-browser --cdp 9222 eval --stdin
const links = document.querySelectorAll('a');
links.length;
EOF
Page interaction (snapshot to get element references)
agent-browser --cdp 9222 snapshot -i # 仅交互元素
agent-browser --cdp 9222 click "<CSS or @e1>"
agent-browser --cdp 9222 type "<sel>" "<text>"
Stop / clean up
- Just close the debug Chrome window. If the window is unresponsive, first press
--user-data-dirverify the PID of the debug instance and then end only it:- macOS / Linux:
pgrep -af chrome-debug-profile - Windows:
wmic process where "name='chrome.exe'" get ProcessId,CommandLine | findstr chrome-debug-profileAfter obtaining the PIDkill -9 {PID}/taskkill /F /PID {PID}. If ownership cannot be verified, stop;Manual cleanup must not batch-kill processes by the Chrome executable name—that would also kill the user's regular Chrome. Exception:setup-cdp-chrome.js --resetinternally does perform a cleanup by executable name; it belongs to this skill's built-in launch flow that requires--yesexplicit consent; do not copy this approach for manual troubleshooting.
- macOS / Linux:
- Login session expired:
node {SKILL_DIR}/scripts/setup-cdp-chrome.js 9222 --reset --yes(note that--yesalso requires asking the user first).
OpenCode environment notes
opencode has no tool for executing command-line operations in the background. Long-running CDP operations (such as waiting for page loads or large-scale data scraping) will block the entire session, causing the CLI to become unresponsive.
Timeout Wrapper
On Windows, use a PowerShell Job wrapper timeout for CDP commands:
$job = Start-Job { agent-browser --cdp 9222 eval "window.location.replace('https://www.qidian.com/rank/')" }
Wait-Job $job -Timeout 30 | Out-Null
if ($job.State -eq 'Running') { Stop-Job $job; Write-Output "⏱ CDP 操作超时(30s),请重试或手动打断" }
else { Receive-Job $job }
Remove-Job $job -Force
On macOS / Linux, use the timeout command:
timeout 30 agent-browser --cdp 9222 eval "window.location.replace('https://www.qidian.com/rank/')" || echo "⏱ CDP 操作超时(30s),请重试或手动打断"
Known Limitations
Even with the timeout wrapper, the following scenarios may still cause problems:
| Scenario | Risk | Mitigation |
|---|---|---|
| Page load timeout | The eval command waits and never returns | Set a 30s timeout and retry after timeout |
| Large-scale data scraping | Cumulative wait becomes too long when paging through multiple pages | Independent timeout per page; continue from the breakpoint after failure |
| Chrome process hangs | CDP connection is lost but the process has not exited | First verify the PID corresponding to the debug profile, end only that debug instance, then reconnect; do not terminate regular Chrome as well |
| Network fluctuations | Requests hang with no timeout | Automatically retry once after timeout |
If you encounter an operation that keeps hanging, press ESC in opencode to interrupt it manually.
FAQ
| Problem | Solution |
|---|---|
NEEDS_CONSENT + exit code 3 |
Use the current platform's interactive capability to ask the user whether Chrome may be killed; with Claude you can use AskUserQuestion, TRAE Code asks directly and waits for a reply; after approval add --yes and rerun |
| CDP port is not listening | --detect-only and confirm again; if the port is occupied, switch to another port |
| Page redirects to the login page | snapshot -i to find the login button and interact with it |
eval returns null |
Check the localStorage key name; for JS containing quotes, use eval -b or --stdin |
| Login state expired | setup-cdp-chrome.js 9222 --reset --yes and copy again |
| There are multiple Chrome profiles | --profile "Profile 1" specify |
| Chrome will not start (30s timeout) | Try --reset; check for port conflicts; check whether ~/chrome-debug-profile/ is corrupted |