Skip to content

Instantly share code, notes, and snippets.

@jerrylususu
Created March 5, 2026 14:48
Show Gist options
  • Select an option

  • Save jerrylususu/a3c02d6ff5c0b428a4176b6c7258fab9 to your computer and use it in GitHub Desktop.

Select an option

Save jerrylususu/a3c02d6ff5c0b428a4176b6c7258fab9 to your computer and use it in GitHub Desktop.
rodney.md

Rodney 深入分析:工作机制与远程开发场景下使用本地 Chrome

1. 结论先行

  • rodney 的核心模型是:短生命周期 CLI + 长生命周期 Chrome + state.json 持久化连接信息。每次命令执行时只做“连接->操作->退出”,Chrome 不退出。
  • 这个工具天然支持“连接外部已运行 Chrome”:rodney connect <host:port>。因此“远程开发环境里跑 rodney,实际控制本地 Chrome”在架构上是可行的。
  • 大概率需要端口转发。典型做法是:把本地 Chrome 的远程调试端口(如 9222)反向转发到远程环境,再在远程执行 rodney connect 127.0.0.1:9222

2. Rodney 是如何工作的

2.1 命令分发和会话目录选择

main.go 会先解析 --local/--global,再分发具体命令。

  • 参数解析:extractScopeArgs()main.go:43
  • 会话目录决策:resolveStateDir()main.go:61
    • --local => ./.rodney/
    • --global => ~/.rodney/
    • 自动模式:若当前目录存在 ./.rodney/state.json 就走本地会话,否则走全局

这部分行为在测试中有覆盖:main_test.go:637 起。

2.2 状态文件是整个系统的锚点

状态结构(main.go:79):

  • debug_url:Chrome DevTools WebSocket 地址(核心)
  • chrome_pid:是否由 rodney 启动并“拥有”这个 Chrome
  • active_page:当前活动 tab index
  • data_dir:Chrome 用户数据目录
  • proxy_pid / proxy_port:认证代理辅助进程信息(如存在)

读写路径:

  • loadState()main.go:104
  • saveState()main.go:116
  • statePath()main.go:100

README 的架构图也与此一致(README.md:10 起)。

2.3 启动路径:rodney start

cmdStart()main.go:321)核心步骤:

  1. 构建 launcher 启动参数(no-sandboxdisable-gpusingle-process 等)
  2. Leakless(false)main.go:357)确保 CLI 进程退出后 Chrome 继续存活
  3. 启动 Chrome,拿到 debugURLmain.go:408
  4. 写入 state(main.go:413

要点:Rodney 不是常驻 daemon,Chrome 才是常驻进程

2.4 每个命令为什么能“接力”

命令执行通路统一依赖:

  • connectBrowser()main.go:131)用 state.debug_url 建立 CDP 连接
  • withPage()main.go:299)拿到当前 tab,并应用默认超时
  • 具体命令(例如 cmdOpen()main.go:529)只做一次动作

这就是 shell 脚本里多条 rodney ... 命令可连续操作同一浏览器会话的原因。

2.5 connect 模式:连接外部 Chrome

cmdConnect()main.go:430)做两步:

  1. 请求 http://<host:port>/json/version,读取 webSocketDebuggerUrlmain.go:439
  2. 用该 WS URL 连通后写入 state(main.go:456

关键细节:

  • 保存 state 时 ChromePID=0main.go:462),代表“此浏览器不归 rodney 管”。
  • 所以 rodney stop 在这种场景下只清理状态,不会关闭外部 Chrome(main.go:494 注释)。

2.6 代理机制(容器/企业网络环境)

cmdStart() 会自动检测带凭据的 HTTPS_PROXY/HTTP_PROXYdetectProxy()main.go:1841)。

若检测到,rodney 会拉起隐藏子命令 _proxymain.go:1868),本地监听一个端口,向上游代理注入 Proxy-Authorization,重点逻辑在 proxyConnect()main.go:1895)。

这套设计与作者笔记一致(notes/claude-chrome-proxy/README.md:56 起)。

3. 远程开发环境使用本地 Chrome:可行性与方案

3.1 是否可行

可行,依据是:

  • 官方文档已定义 rodney connect host:9222README.md:47
  • 源码确实按 CDP 标准去连 /json/version + WebSocket(main.go:439main.go:457

3.2 最推荐拓扑(远程 rodney -> 本地 Chrome)

本地启动 Chrome 调试端口

# 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

rodney connect 127.0.0.1:9222
rodney open https://example.com
rodney title

3.3 为什么建议端口保持同号(9222 对 9222)

cmdConnect() 会使用 /json/version 返回的 webSocketDebuggerUrl 原样连接(main.go:449main.go:457)。

如果你把远程入口映射成别的端口,可能出现这种问题:

  • 远程请求的是 127.0.0.1:13337/json/version
  • 返回的 WS URL 却是 ws://127.0.0.1:9222/...
  • 结果 rodney 改去连 9222,若该端口在远程没打通就失败

所以实操上优先用同号映射,最省心。

3.4 如果页面服务在远程,本地 Chrome 打不开怎么办

这是常见坑:

  • 远程执行 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 实际会访问远程应用

4. 风险与边界

  • 安全风险高:DevTools 端口意味着“完全控制浏览器上下文”。只绑定 127.0.0.1,仅通过受控 SSH 隧道暴露,不要直接 0.0.0.0
  • rodney stopconnect 模式下不会关闭本地 Chrome(ChromePID=0 语义,main.go:462main.go:494)。
  • 如远程环境本身有代理限制,connect 模式下网页访问走的是本地 Chrome 网络出口,不是远程容器出口。这通常是优点,但要有预期。

5. 建议的验证流程(最小可行)

  1. 本地启动 Chrome 远程调试端口(9222)。
  2. 建立 ssh -R 9222:127.0.0.1:9222 ...
  3. 远程执行:curl http://127.0.0.1:9222/json/version,确认可达。
  4. 远程执行:rodney connect 127.0.0.1:9222
  5. 远程执行:rodney open https://example.com && rodney title

若第 3 步失败,是隧道问题;若第 4 步失败,多半是 WS URL/端口映射不一致。

6. 参考证据(源码与文档)

  • README.md:10 架构概览
  • README.md:47 connect 用法
  • main.go:79 状态结构
  • main.go:321 start 实现
  • main.go:430 connect 实现
  • main.go:476 stop 实现
  • main.go:1841 代理检测
  • main.go:1895 CONNECT 代理透传
  • proc_unix.go:10 _proxy 子进程 Setsid
  • notes/claude-chrome-proxy/README.md:56 代理设计背景与方案
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment