提示词.com

人机共享容器浏览器

本站托管 已被复制 1 次

一行指令在 Docker 里拉起带网页远程桌面和 CDP 接口的持久化 Chrome:人可远程看画面、手动登录和过验证码,Agent 同步操作并复用同一套 Cookie。

复制这句话,粘给 Claude Code

帮我安装人机共享容器浏览器 skill https://prompt.free2gpt.com/s/shared-chrome.md,然后帮我在当前目录部署一个 Chrome 容器

它会下载 https://prompt.free2gpt.com/s/shared-chrome.md 并把 skill 装到 ~/.claude/skills/shared-chrome/,然后直接开始干活。

想手动安装?
curl -fsSL https://prompt.free2gpt.com/s/shared-chrome.md --create-dirs -o ~/.claude/skills/shared-chrome/SKILL.md

它能帮你做什么

  • 一键容器化部署:自动生成 docker-compose.yml、官方 Google Chrome(x86_64)或 linuxserver/chromium(arm64)镜像配置、独立 Profile 自启保活脚本与 CDP 端口转发服务
  • 人机同屏协作:用户通过浏览器打开 Selkies 网页远程桌面(:3001 控制 / :#shared 多人只读围观),Agent 通过本机 127.0.0.1:9222 用 agent-browser 或 Playwright 操作同一浏览器
  • 持久化登录态:Profile 与 Cookie 落盘到 ./config,容器重启不丢登录状态;遇到登录、扫码、2FA 或验证码时,人工接管完成后 Agent 直接在同一页面继续
  • 内置避坑规范:避开 network_mode: host 冲突、Chrome 136+ 默认目录限制、GUI 模式 CDP 仅监听 127.0.0.1、共享视口裁切与多 Profile 串台等常见坑

还可以这样说

  • 帮我在 ./browser 部署一个带远程桌面和 CDP 的持久化 Chrome 容器,端口用 3010 和 9225
  • 给我的服务器装一个我和 Agent 能同时操作、能人工过验证码的 Chrome 浏览器
  • 帮我接上这个共享 Chrome 的 CDP,不要改视口大小,直接打开页面让我扫码登录

使用前须知

  • 需要宿主机已安装 Docker 与 Docker Compose;x86_64 架构默认构建官方 Google Chrome(arm64 自动改用基础镜像自带的 Chromium)
  • CDP 接口仅绑定在 127.0.0.1:9222(无鉴权,切勿暴露到 0.0.0.0),网页远程桌面通过 .env 中的 Basic Auth 账号密码保护
  • 人工登录或过完验证码后,建议等待约 30 秒再重启容器,确保 Chrome 将 Cookie 完整刷盘
查看 SKILL.md 原文

Shared Chrome — 人机共享持久化容器浏览器

在 Docker 中部署一个带完整 Wayland 桌面(Selkies HTML5 远程桌面)与 CDP 调试接口的持久化 Chrome 实例:

  • 人类用户:通过浏览器打开网页远程桌面(HTTPS :3001 / HTTP :3000),实时看到画面并操作键鼠、登录账号、处理验证码或 2FA
  • Agent:通过宿主机 http://127.0.0.1:9222(CDP)用 agent-browser / Playwright / Puppeteer 操作同一浏览器会话、共享全部 Cookie 与登录态
  • 持久化:Profile、Cookie、缓存落盘到 ./config,容器重建后登录态不丢失

第一步:检查环境与确认参数

动手前先快速检查以下信息,缺失或冲突时向用户确认:

  1. 宿主机架构:运行 uname -m
    • x86_64(amd64):可构建官方 Google Chrome 镜像(支持 Google 账号同步、风控与兼容性更好)
    • aarch64(arm64):Google 官方无 Linux ARM64 .deb 包,跳过构建,直接用 linuxserver/chromium:latest 自带的 chromium
  2. 部署目录与端口:
    • 默认目录:./browser(若用户指定了路径则用用户路径)
    • 默认端口:Web 远程桌面 127.0.0.1:3000(HTTP,供反代)+ 3001:3001(HTTPS 自签证书,局域网/直连),CDP 127.0.0.1:9222
    • 先用 ss -tln 或 lsof 检查 3000、3001、9222 是否被占用,冲突则递增端口
  3. 登录凭据与代理:
    • 网页远程桌面默认开启 Basic Auth(BROWSER_USER / BROWSER_PASSWORD),未指定时生成强随机密码写入 .env
    • 若宿主机访问 Google / 下载 google-chrome.deb 需要代理,询问或读取代理地址(供构建下载及 CHROME_CLI --proxy-server=... 使用)
    • 运行用户:PUID/PGID 默认 1000:1000(若当前非 root 用户则用 id -u / id -g)

第二步:创建目录与核心文件

按下面结构在目标目录(如 ./browser)生成文件:

browser/
├── .env                          # Basic Auth 账号密码 (chmod 600)
├── docker-compose.yml            # 容器编排(bridge 网络 + shm_size 2gb)
├── chrome/                       # [仅 x86_64 官方 Chrome]
│   ├── Dockerfile
│   └── google-chrome.deb
├── services/
│   └── cdp-forward               # [必须 +x] 0.0.0.0:9223 -> 127.0.0.1:9222 转发
└── config/.config/labwc/
    └── autostart                 # [必须 +x] 独立 profile + 崩溃/关闭 3s 自动重启

1. 下载官方 Chrome 安装包(仅 x86_64)与 chrome/Dockerfile

若 Docker Hub 拉取慢可改用镜像前缀(如 docker.1panel.live/linuxserver/chromium:latest),避免用经常超时的 lscr.io。

mkdir -p chrome services config/.config/labwc
curl -fSL https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -o chrome/google-chrome.deb

chrome/Dockerfile:

FROM linuxserver/chromium:latest
COPY google-chrome.deb /tmp/
RUN rm -f /etc/apt/sources.list.d/* \
    && sed -i "s#deb.debian.org#mirrors.aliyun.com#g" /etc/apt/sources.list \
    && sed -i "/^deb-src/d; /security/d" /etc/apt/sources.list \
    && apt-get update \
    && apt-get install -y --no-install-recommends /tmp/google-chrome.deb \
    && rm -rf /tmp/google-chrome.deb /var/lib/apt/lists/* /etc/apt/sources.list.d/google-chrome.*

若服务器在海外无需阿里云源,可去掉两行 sed。rm -f /etc/apt/sources.list.d/* 是为了清理基础镜像里可能卡住 apt-get update 的第三方源。若为 arm64,跳过此步。

2. services/cdp-forward(必须 chmod +x)

关键机制:Chrome 有界面模式下 CDP 固定只监听容器内 127.0.0.1:9222,Docker 端口映射无法直接转出。利用基础镜像自带的 /lsiopy/bin/python3 起一个 s6 自定义常驻服务,将 0.0.0.0:9223 转发到 127.0.0.1:9222:

#!/lsiopy/bin/python3
import asyncio

async def pipe(r, w):
    try:
        while data := await r.read(65536):
            w.write(data)
            await w.drain()
    finally:
        w.close()

async def handle(cr, cw):
    try:
        ur, uw = await asyncio.open_connection("127.0.0.1", 9222)
    except OSError:
        cw.close()
        return
    await asyncio.gather(pipe(cr, uw), pipe(ur, cw), return_exceptions=True)

async def main():
    server = await asyncio.start_server(handle, "0.0.0.0", 9223)
    await server.serve_forever()

asyncio.run(main())

3. config/.config/labwc/autostart(必须 chmod +x)

关键机制:

  1. Chrome 136+ 禁止对默认配置目录启用 --remote-debugging-port,必须显式指定独立 --user-data-dir=$HOME/.config/chrome-profile。
  2. 镜像默认只在启动时拉起一次浏览器,用户误关窗口后桌面会变空;while true 循环在退出 3 秒后自动重开并清理残留 Singleton* 锁。
#!/bin/bash
PROFILE=$HOME/.config/chrome-profile
BIN=google-chrome   # 若 arm64 直接使用基础镜像,改为 chromium
while true; do
  pgrep -x chrome >/dev/null || pgrep -x chromium >/dev/null || rm -f "$PROFILE"/Singleton*
  "$BIN" \
    --no-sandbox \
    --test-type \
    --password-store=basic \
    --start-maximized \
    --ozone-platform=wayland \
    --user-data-dir="$PROFILE" \
    ${CHROME_CLI} > /dev/null 2>&1
  sleep 3
done &

4. .env(chmod 600)与 docker-compose.yml

.env:

BROWSER_USER=admin
BROWSER_PASSWORD=<生成强随机密码>

docker-compose.yml(x86_64 用 build: ./chrome + image: shared-chrome:latest;arm64 直接 image: linuxserver/chromium:latest):

services:
  chromium:
    build: ./chrome
    image: shared-chrome:latest
    container_name: shared-chrome
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
      - LC_ALL=zh_CN.UTF-8
      - CUSTOM_USER=${BROWSER_USER}
      - PASSWORD=${BROWSER_PASSWORD}
      - CHROME_CLI=--remote-debugging-port=9222 --remote-allow-origins=* --lang=zh-CN
      # 若需浏览器走代理,追加:
      # --proxy-server=http://<proxy-host>:<port> --proxy-bypass-list=<local>;127.*;10.*;172.16.*;192.168.*
    volumes:
      - ./config:/config
      - ./services:/custom-services.d:ro
    ports:
      - 127.0.0.1:3000:3000
      - 3001:3001
      - 127.0.0.1:9222:9223
    shm_size: "2gb"
    restart: unless-stopped

安全与架构红线:

  • 严禁 network_mode: host:容器内 Wayland/X :1 抽象 unix socket 会与宿主机桌面环境冲突,导致 PulseAudio/Selkies 卡死。
  • CDP 端口严禁暴露到 0.0.0.0:必须绑定 127.0.0.1:9222:9223(CDP 无鉴权,可读写 Cookie、执行任意 JS)。
  • shm_size: "2gb" 不可省:Docker 默认 64MB /dev/shm 会导致 Chrome 标签页频繁崩溃。

第三步:设权限、启动与验收

chmod +x services/cdp-forward config/.config/labwc/autostart
chmod 600 .env
# 确保挂载目录属主与 PUID:PGID 一致,否则容器内写 profile 会报权限错误
chown -R 1000:1000 config

docker compose up -d --build

等待约 5~10 秒后验收:

# 1. 验证 CDP 就绪
curl -fsS http://127.0.0.1:9222/json/version

# 2. 验证远程桌面端口监听
curl -skI https://127.0.0.1:3001 | head -n 3

验收成功后向用户输出:

  • 远程桌面控制地址:https://<服务器IP>:3001(及只读观看地址 https://<服务器IP>:3001/#shared)
  • Basic Auth 账号密码(来自 .env)
  • 宿主机 CDP 地址:http://127.0.0.1:9222

第四步:Agent 与用户协同操作规范

人类用户端(Selkies 远程桌面)

  • 控制端:直接打开 URL(不带 hash),同一时刻只允许一个控制端,新接入控制端会接管并踢掉上一个控制端
  • 只读观众(多人围观):打开 URL/#shared,可无限多人同时观看,输入事件会被丢弃,不抢控制权
  • 人工登录落盘:人工登录或过完验证码后,等待约 30 秒再重启容器,确保 Chrome 把 Cookie 刷回磁盘

Agent 端(CDP 连接)

  • agent-browser:在 ~/.agent-browser/config.json 写入 {"cdp": "9222"}(或每次传 --cdp 9222);结束会话用 agent-browser close 只断开连接,不会关闭 Chrome
  • Playwright:
    const browser = await chromium.connectOverCDP("http://127.0.0.1:9222");
    const context = browser.contexts()[0]; // 必须取 contexts()[0] 才能复用持久化登录态!
    
  • 不要固定/修改共享实例的视口大小:禁止在共享实例上执行 agent-browser set viewport 或 setViewportSize,否则页面尺寸与远程桌面窗口不一致会导致底部/右侧按钮被裁切;保持随窗口自适应。若误设,执行 agent-browser close 断开该会话即可清除模拟,无需重启容器
  • 只保留单个 Profile:不要在 Chrome 里新建第二个用户配置(Profile 2),否则 CDP 新开标签页会落到未登录的 Profile 上
  • 人机接力流程:自动化遇到登录、扫码、短信 2FA 或强风控验证码时,Agent 停留在当前页并提示用户打开远程桌面操作,待用户确认完成后直接在同一页面/上下文继续执行
自动化 浏览器docker自动化agent部署