Install
npx skillscat add russ534/myopenclawbot/fygo-browser-cdp Install via the SkillsCat registry.
SKILL.md
fygo-browser 真桌面 Chrome + CDP 全链路方案
适用场景
- Google 账号申诉 / Gmail 登录(Google 会拦截 headless Chrome)
- 任何被 WAF / 验证码识别的"自动化"场景
- 需要真实浏览器指纹 + 真实 IP + 真实 UA 的任务
关键事实
- fygo-browser 是 fnOS native 应用(不是 Docker 容器),跑在 host 用
fygo-browser用户 - 自带 Chrome 152.0.7977.82,User-Agent 是真桌面(无 HeadlessChrome 标记)
- 自带 KasmVNC 提供远程桌面,端口是
16000(KASM_WEB_PORT) /16001(KASM_RFB_PORT),实际 gateway 报 16001 timeout 要看 netstat,不是 env 写的值——不同 fygo-browser 版本可能把端口写到data/run/runtime-ports.env决定,写错会被 supervisor 跳过 - 关键:fnos host 没外网,必须走 mihomo 代理(端口
7890HTTP /7891SOCKS,无 auth) - fygo-browser-gateway 暴露 unix socket
/vol1/@appcenter/fygo-browser/app.sock - chromium 实际 CDP 端口是
16005(不管 ports.env 怎么写都看 netstat)
关键坑(必看)
- fnos sshd 默认禁 PortForwarding —— 改
/etc/ssh/sshd_config.d/trim_sshd.conf里AllowTcpForwarding no→yes+kill -HUP $(cat /var/run/sshd.pid) - SSH 隧道端口冲突 —— 之前 tunnel 死了 16005 还 TIME_WAIT 占着,用别的端口转发(
16025) - agent-browser daemon 跟 chromium WebSocket 状态会错位 —— 报"Request ID 重复 + Connection lost",简单 list_tabs 能用,goto_url 卡死
- agent-browser
page_info会卡 IPC timeout —— 别用 - chromium 启参数必须有
--proxy-server=http://127.0.0.1:7890—— 不加连不上 Google - fygo-browser start.sh
RUNTIME_IDLE_TIMEOUT默认 1m —— 改 24h,不然一会儿就 idle 关掉 - fnoncebrowser start.sh 改 start_chromium_process 那段(line ~600 附近)加 proxy
完整启动流程
1. fnos sshd 开 PortForwarding
ssh fnos "cp /etc/ssh/sshd_config.d/trim_sshd.conf{,.bak} && sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/' /etc/ssh/sshd_config.d/trim_sshd.conf && kill -HUP \$(cat /var/run/sshd.pid)"2. 改 fygo-browser start.sh 加 proxy
ssh fnos "sed -i '/--disable-dev-shm-usage/a\\ --proxy-server=http://127.0.0.1:7890 \\\\' /vol1/@appcenter/fygo-browser/app/scripts/start.sh"
# + 改 RUNTIME_IDLE_TIMEOUT:-1m → :-24h3. 启 fygo-browser runtime(用 fygo-browser 用户)
ssh fnos "runuser -u fygo-browser -- bash -c 'cd /vol1/@appcenter/fygo-browser/app && bash scripts/start.sh runtime-start'"4. 写 ports.env(如果还没建)
ssh fnos "runuser -u fygo-browser -- bash -c 'cat > /vol1/@appcenter/fygo-browser/data/run/runtime-ports.env <<EOF
KASM_WEB_PORT=16000
KASM_RFB_PORT=16001
CDP_PORT=16002
CHROMIUM_CDP_PORT=16003
EOF'"5. 验证 chromium + 拿到实际 CDP 端口
ssh fnos "netstat -tlnp 2>/dev/null | grep 16005" # 实际是 16005
ssh fnos "curl -s -m 5 http://127.0.0.1:16005/json/version" # 拿 webSocketDebuggerUrl6. 建 SSH 隧道(用 16025 避开 TIME_WAIT)
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -L 127.0.0.1:16025:127.0.0.1:16005 -N fnos &7. 验证 CDP 过隧道
curl -s -m 5 http://127.0.0.1:16025/json/version # 200 OKPython CDP 客户端(绕开 agent-browser)
不要用 agent-browser daemon —— WebSocket 状态会错位。
直接用 Python websockets 库(hermes venv 已装 websockets==15.0.1):
import asyncio, websockets, json, base64
import urllib.request
# 拿 tab list
tabs = json.loads(urllib.request.urlopen('http://127.0.0.1:16025/json', timeout=5).read())
for t in tabs:
if t.get('type') == 'page':
print(t['title'], '|', t['url'])
print('ws:', t['webSocketDebuggerUrl'])
WS_URL = 'ws://127.0.0.1:16025/devtools/page/{targetId}'
async def cdp_call(ws, method, params=None, _id=[1]):
_id[0] += 1
msg = {'id': _id[0], 'method': method}
if params: msg['params'] = params
await ws.send(json.dumps(msg))
while True:
r = json.loads(await ws.recv())
if r.get('id') == _id[0]:
return r
async def google_login():
async with websockets.connect(WS_URL, max_size=10*1024*1024) as ws:
await cdp_call(ws, 'Runtime.enable')
# 找 input + set value
r = await cdp_call(ws, 'Runtime.evaluate', {
'expression': """(() => {
const i = document.querySelector('input[type="email"]');
if (!i) return 'no input';
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
setter.call(i, 'russer1109@gmail.com');
i.dispatchEvent(new Event('input', { bubbles: true }));
return 'ok: ' + i.value;
})()""", 'returnByValue': True
})
# click Next
await cdp_call(ws, 'Input.dispatchMouseEvent', {'type':'mousePressed','x':1024,'y':440,'button':'left','clickCount':1})
await cdp_call(ws, 'Input.dispatchMouseEvent', {'type':'mouseReleased','x':1024,'y':440,'button':'left','clickCount':1})
await asyncio.sleep(3)
# 截图
r = await cdp_call(ws, 'Page.captureScreenshot', {'format': 'png'})
with open('/tmp/x.png', 'wb') as f:
f.write(base64.b64decode(r['result']['data']))用 CDP 打开本机 / LAN 的 Web 看板(真浏览器登录 + 抓页面)
需要"用真浏览器登录某个内网看板 / 后台"时走这条,不要指望 hermes 自带 browser 工具(容器内没有 CDP,也进不了内网服务)。
两个必踩的网络坑(一起出现时最难辨):
- Chrome 带
--proxy-server=http://127.0.0.1:7890启动 → 打 LAN IP 会被 mihomo 拦成 HTTP 502(Chrome 显示chrome-error://chromewebdata/,正文This page isn't working)。不是服务挂了。 - 目标服务可能只 bind 在 LAN IP 上(
docker port <c>显示8788/tcp -> 192.168.31.201:8788)→ 从宿主打http://127.0.0.1:8788是 ERR_CONNECTION_REFUSED。
解法:在宿主上起一个只 bind localhost 的 TCP 转发——Chrome 即使带 --proxy-server 也会自动绕过 localhost,于是既避开 mihomo 502 又绕开 bind 限制:
ssh fnos-root 'cat > /tmp/wbfwd.py <<PY
import socket, threading
L=("127.0.0.1",8799); T=("192.168.31.201",8788) # 改 T 成目标
...
PY
nohup python3 /tmp/wbfwd.py > /tmp/wbfwd.log 2>&1 &'
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8799/ # 期望 200收尾清理用方括号技巧,否则 pkill -f wbfwd 会连你自己的 ssh 会话一起杀(ssh 命令行里含同一个字符串):
ssh fnos-root 'for p in $(pgrep -f "[w]bfwd"); do kill $p; done'从 hermes 容器调 CDP 必须显式关代理:urllib.request.build_opener(ProxyHandler({}));curl 加 --noproxy '*'。否则容器的 HTTP_PROXY 把 127.0.0.1:16025 也送去 mihomo。
开新标签页要 PUT(GET 会 405/timeout):
curl -s --noproxy '*' -X PUT 'http://127.0.0.1:16025/json/new?http://127.0.0.1:8799/?pwd=<panel_password>'抓内容 / 截图(SPA 看板导航项常是 <button>,没有 href——按 innerText 点,不要猜 ?tab= 参数):
await call("Runtime.evaluate", {"expression": "document.body.innerText", "returnByValue": True})
await call("Runtime.evaluate", {"expression": "(()=>{const el=[...document.querySelectorAll('button,a,div,span')].find(e=>e.innerText.trim()==='数据看板'); el.click(); return 'clicked';})()"})
await call("Page.captureScreenshot", {"format": "png"}) # base64 → 落盘后自己看收尾:杀掉隧道(kill <ssh pid>,用后台进程管理工具别用 nohup)、杀掉宿主转发;看板 URL 里的 ?pwd= 会留在浏览器历史,公网暴露时不要用这种免密链接。
KasmVNC 远程桌面(爸爸手动操作时用)
https://192.168.31.201:5666/app/fygo-browser/(fnOS 认证后)- 或者直接 VNC:
127.0.0.1:16001(noVNC web) /127.0.0.1:16002(VNC RFB) - KasmVNC disableBasicAuth + SecurityTypes None —— 无密码直接连
关键经验
- 真桌面 Chrome 152 + mihomo 代理 = Google 接受登录(不像 agent-browser 报"This browser or app may not be secure")
- fnoncebrowser 用户跑 mihomo 代理 = 走 host 127.0.0.1:7890
- agent-browser daemon 简化操作可以,复杂操作(goto_url/page_info)会卡 IPC timeout
- Python websockets 直连 CDP 是最稳的 —— 简单 list_tabs / set value / click / screenshot 都行
- 点击坐标要先看 input 元素 rect 再点 —— Google 登录页 input 一般在 y=200-300 区域