rodney的核心模型是:短生命周期 CLI + 长生命周期 Chrome + state.json 持久化连接信息。每次命令执行时只做“连接->操作->退出”,Chrome 不退出。- 这个工具天然支持“连接外部已运行 Chrome”:
rodney connect <host:port>。因此“远程开发环境里跑 rodney,实际控制本地 Chrome”在架构上是可行的。 - 你大概率需要端口转发。典型做法是:把本地 Chrome 的远程调试端口(如
9222)反向转发到远程环境,再在远程执行rodney connect 127.0.0.1:9222。
main.go 会先解析 --local/--global,再分发具体命令。
- 参数解析:
extractScopeArgs()(main.go:43) - 会话目录决策:
resolveStateDir()(main.go:61)--local=>./.rodney/--global=>~/.rodney/- 自动模式:若当前目录存在
./.rodney/state.json就走本地会话,否则走全局
这部分行为在测试中有覆盖:main_test.go:637 起。
状态结构(main.go:79):
debug_url:Chrome DevTools WebSocket 地址(核心)chrome_pid:是否由 rodney 启动并“拥有”这个 Chromeactive_page:当前活动 tab indexdata_dir:Chrome 用户数据目录proxy_pid/proxy_port:认证代理辅助进程信息(如存在)
读写路径:
loadState()(main.go:104)saveState()(main.go:116)statePath()(main.go:100)
README 的架构图也与此一致(README.md:10 起)。
cmdStart()(main.go:321)核心步骤:
- 构建
launcher启动参数(no-sandbox、disable-gpu、single-process等) Leakless(false)(main.go:357)确保 CLI 进程退出后 Chrome 继续存活- 启动 Chrome,拿到
debugURL(main.go:408) - 写入 state(
main.go:413)
要点:Rodney 不是常驻 daemon,Chrome 才是常驻进程。
命令执行通路统一依赖:
connectBrowser()(main.go:131)用state.debug_url建立 CDP 连接withPage()(main.go:299)拿到当前 tab,并应用默认超时- 具体命令(例如
cmdOpen()在main.go:529)只做一次动作
这就是 shell 脚本里多条 rodney ... 命令可连续操作同一浏览器会话的原因。
cmdConnect()(main.go:430)做两步:
- 请求
http://<host:port>/json/version,读取webSocketDebuggerUrl(main.go:439) - 用该 WS URL 连通后写入 state(
main.go:456)
关键细节:
- 保存 state 时
ChromePID=0(main.go:462),代表“此浏览器不归 rodney 管”。 - 所以
rodney stop在这种场景下只清理状态,不会关闭外部 Chrome(main.go:494注释)。
cmdStart() 会自动检测带凭据的 HTTPS_PROXY/HTTP_PROXY(detectProxy(),main.go:1841)。
若检测到,rodney 会拉起隐藏子命令 _proxy(main.go:1868),本地监听一个端口,向上游代理注入 Proxy-Authorization,重点逻辑在 proxyConnect()(main.go:1895)。
这套设计与作者笔记一致(notes/claude-chrome-proxy/README.md:56 起)。
可行,依据是:
- 官方文档已定义
rodney connect host:9222(README.md:47) - 源码确实按 CDP 标准去连
/json/version+ WebSocket(main.go:439、main.go:457)
# Linux 示例
google-chrome \
--remote-debugging-port=9222 \
--remote-debugging-address=127.0.0.1 \
--user-data-dir=/tmp/rodney-local-profile# 在本地执行,把“本地127.0.0.1:9222”暴露为“远程127.0.0.1:9222”
ssh -N -R 9222:127.0.0.1:9222 <user>@<remote-host>rodney connect 127.0.0.1:9222
rodney open https://example.com
rodney titlecmdConnect() 会使用 /json/version 返回的 webSocketDebuggerUrl 原样连接(main.go:449、main.go:457)。
如果你把远程入口映射成别的端口,可能出现这种问题:
- 远程请求的是
127.0.0.1:13337/json/version - 返回的 WS URL 却是
ws://127.0.0.1:9222/... - 结果 rodney 改去连
9222,若该端口在远程没打通就失败
所以实操上优先用同号映射,最省心。
这是常见坑:
- 远程执行
rodney open http://localhost:3000 - 但真正打开的是“本地 Chrome 的 localhost:3000”,不是远程机器的 3000
解决:再加一条本地转发,把远程应用端口拉到本地。
# 在本地执行
ssh -N \
-R 9222:127.0.0.1:9222 \
-L 3000:127.0.0.1:3000 \
<user>@<remote-host>此时:
- 远程
rodney connect 127.0.0.1:9222能控制本地 Chrome - 本地 Chrome 打开
http://127.0.0.1:3000实际会访问远程应用
- 安全风险高:DevTools 端口意味着“完全控制浏览器上下文”。只绑定
127.0.0.1,仅通过受控 SSH 隧道暴露,不要直接0.0.0.0。 rodney stop在connect模式下不会关闭本地 Chrome(ChromePID=0语义,main.go:462、main.go:494)。- 如远程环境本身有代理限制,
connect模式下网页访问走的是本地 Chrome 网络出口,不是远程容器出口。这通常是优点,但要有预期。
- 本地启动 Chrome 远程调试端口(9222)。
- 建立
ssh -R 9222:127.0.0.1:9222 ...。 - 远程执行:
curl http://127.0.0.1:9222/json/version,确认可达。 - 远程执行:
rodney connect 127.0.0.1:9222。 - 远程执行:
rodney open https://example.com && rodney title。
若第 3 步失败,是隧道问题;若第 4 步失败,多半是 WS URL/端口映射不一致。
README.md:10架构概览README.md:47connect用法main.go:79状态结构main.go:321start实现main.go:430connect实现main.go:476stop实现main.go:1841代理检测main.go:1895CONNECT 代理透传proc_unix.go:10_proxy子进程Setsidnotes/claude-chrome-proxy/README.md:56代理设计背景与方案