Skip to content

RFC: Workspace Display & Browser #404

@Menci

Description

@Menci

摘要

将浏览器自动化和 GUI/Computer-Use 能力统一收入 Workspace Container,取代独立的 Browser Gateway 服务。通过在 Bridge gRPC 协议中新增 Tunnel RPC,让 Memoh Server 能够透明地拨号到容器内部的任意 TCP 服务(CDP、VNC 等),在此基础上分别构建浏览器操控和桌面交互两套能力。

动机

当前架构中 Browser Gateway 是一个独立的 Bun/Elysia 服务,运行 Playwright 驱动浏览器。这带来几个问题:

  1. 运维复杂度:多一个独立服务需要部署、监控、升级
  2. 无 bot 隔离:所有 bot 共享同一组浏览器进程,存在安全和资源隔离问题
  3. 无 GUI 能力:当前架构不支持 Computer-Use(屏幕截图 + 鼠标键盘操控)和可视化桌面

新架构将浏览器和显示栈都放进每个 bot 的 Workspace Container,复用已有的容器隔离机制,消除独立服务,同时引入 GUI 能力。

设计

0. 共享基础设施:Bridge Tunnel RPC

Tunnel 是所有后续能力的基础。它允许 Memoh Server 通过已有的 Bridge gRPC 连接(UDS)拨号到容器内的任意本地 TCP 端口。

Proto 定义

// 新增到 bridge.proto

message TunnelOpen {
  string address = 1;  // e.g. "127.0.0.1:9222", "127.0.0.1:5999"
}

message TunnelData {
  bytes data = 1;
}

message TunnelClose {
  string error = 1;  // 空字符串表示正常关闭
}

message TunnelFrame {
  oneof frame {
    TunnelOpen open = 1;    // 仅第一帧,client→bridge
    TunnelData data = 2;    // 双向
    TunnelClose close = 3;  // 任一端发起
  }
}

service ContainerService {
  // ... 现有 RPC ...
  rpc Tunnel(stream TunnelFrame) returns (stream TunnelFrame);
}

Bridge 侧实现

收到 TunnelOpennet.Dial("tcp", address) 建立到目标的 TCP 连接,之后双向 relay TunnelData,任一端发送 TunnelClose 或 stream 断开时关闭 TCP 连接。

安全约束:只允许 dial 127.0.0.1,拒绝所有其他地址。方向仅 server→container,container 侧不可主动建立反向隧道。

Server 侧封装

bridge.Client 新增方法:

func (c *Client) DialContext(ctx context.Context, network, address string) (net.Conn, error)

返回实现 net.Conn 接口的 tunnelConn,内部持有一个 gRPC stream。一次 Tunnel stream = 一条 TCP 连接,不做多路复用。

调用方可直接注入到 http.Transport、chromedp allocator、或任何接受 Dialer 的 Go 组件中:

transport := &http.Transport{DialContext: bridgeClient.DialContext}

Part 1: VNC / Computer-Use

1.1 Display Bundle

自包含的 X11/VNC 运行时,mount 到容器内 /opt/memoh/display/

内容

  • Xvnc(TigerVNC)
  • xkbcomp(X keyboard compiler,Xvnc 运行时依赖)
  • xkb-data(键盘布局数据文件)
  • 最小字体集(cursor font)

构建方式:Nix buildEnv 产出自包含 closure(含 glibc 副本,不依赖宿主 libc 版本)。CI 构建后打成 tarball 作为 release artifact。安装脚本 docker/display/install.shwget + tar 下载解压,与 docker/toolkit/install.sh 模式一致。

平台支持:glibc only。Xvnc 无法静态链接,musl 容器不支持 display 能力。Bridge 启动时检测 /opt/memoh/display/bin/Xvnc 是否存在且可执行,不可用时 display 相关功能静默禁用。

1.2 Xvnc 管理

启停策略:Bot 级别配置开关「启用显示器」。启用后 bridge 在容器启动时拉起 Xvnc:

LD_LIBRARY_PATH=/opt/memoh/display/lib \
  /opt/memoh/display/bin/Xvnc :99 \
  -geometry 1280x800 -depth 24 \
  -SecurityTypes None

监听 RFB 端口 5999(display :99 → port 5900 + 99 = 5999)。Bridge 监控子进程,异常退出自动重启。

VNC 不设密码认证(-SecurityTypes None)—— 容器内部网络不暴露,所有外部访问都经过 Tunnel + Memoh auth。

1.3 Computer-Use(Server 侧 RFB Client)

Server 侧实现轻量的 RFB(VNC 协议,RFC 6143)client,通过 Tunnel dial 127.0.0.1:5999 连接容器内的 Xvnc。

能力

操作 RFB 实现
截屏 FramebufferUpdateRequest → 接收像素数据 → Go 侧 encode PNG,全内存,无临时文件
鼠标操作 PointerEvent 消息(6 bytes)
键盘操作 KeyEvent 消息(8 bytes)
屏幕尺寸 RFB 握手 ServerInit 消息中获取

不使用 xdotool 或 ImageMagick —— 所有交互通过 VNC 协议完成,display bundle 内无需额外工具。

1.4 Agent Tools

新增 computer_action / computer_observe 工具,与 browser tools 平级,仅在 display 已启用时暴露给 agent:

computer_observe

  • screenshot — 返回 base64 PNG
  • get_screen_size — 返回宽高

computer_action

  • click(x, y) / double_click(x, y) / right_click(x, y)
  • move(x, y)
  • type(text)
  • key(combo) — 如 ctrl+cEnter
  • scroll(direction, amount)
  • drag(from_x, from_y, to_x, to_y)

1.5 前端 noVNC

Memoh web 端内嵌 noVNC 组件(纯 JavaScript VNC 客户端),不进 container。

连接路径:

noVNC (浏览器) → WebSocket → Memoh Server WS endpoint → Tunnel → container:5999

Server 侧实现 WebSocket-to-TCP 代理,一层薄 relay,复用现有 auth 体系鉴权。

1.6 桌面环境(可选,待定)

Memoh 不 bundle 桌面环境,也不假定容器内安装了什么桌面。用户自行选择包含桌面环境的 workspace image(Memoh 可提供预装 XFCE 的镜像变体作为参考),并在 bot 设置中:

  1. 启用桌面环境开关
  2. 填写桌面启动命令(如 startxfce4gnome-session

Bridge 在 Xvnc 启动后执行用户配置的启动命令(DISPLAY=:99)。

运行时组合:

  • 无桌面:Xvnc :99 → 空 X display,headful 浏览器或其他 GUI 应用直接显示
  • 有桌面:Xvnc :99 → 用户配置的桌面 session → 完整桌面体验

本节细节待定,将在 Part 1 核心能力(1.1-1.5)实现后再细化。

Part 2: Browser / Browser-Use

2.1 Playwright 与 Chromium

Playwright CLI 及 browser server 脚本 mount 到 /opt/memoh/playwright/,使用 /opt/memoh/toolkit/ 中已有的 Node.js 运行。

Chromium 获取方式(二选一,待定):

方案 A — 预打包挂载:Chromium 二进制打包进 server image,随 /opt/memoh/playwright/ 一起 mount。

  • 优势:零下载延迟,无网络依赖,开箱即用
  • 代价:server image 增大 ~350MB,所有 bot 不论是否使用都携带浏览器,版本更新需重新发布 image

方案 B — 按需下载/opt/memoh/playwright/ 只挂载 Playwright CLI,Chromium 由 playwright install chromium 在容器内按需下载,保存在容器可写层(~350MB)。

  • 优势:server image 保持精简,只有使用浏览器的 bot 承担存储开销
  • 代价:首次使用需下载,需要网络连通

方案 B 的安装触发与进度汇报

  1. 用户在前端为 bot 启用浏览器 → 触发后台安装(主路径)
  2. Agent 在安装未完成时调用 browser tool → tool result 返回 playwright install chromium 的 stdout/stderr 原样输出,agent 自行理解进度并反馈给用户
  3. Agent 在未启用浏览器时调用 browser tool → 触发开始安装,同样返回 stdout/stderr
  4. 安装完成后,后续 browser tool call 正常执行

2.2 Chromium 启动与生命周期

Bridge 直接 exec 启动 Chromium,不经过 Playwright server,不需要 Node 运行时参与浏览器操控:

chromium --remote-debugging-port=9222 --no-first-run --disable-default-apps

headless vs headful

  • 默认 --headless=new(Chromium 新版 headless 模式)
  • 若 Xvnc 已启动(Part 1),加 DISPLAY=:99 并去掉 headless flag → headful 模式,浏览器窗口在 noVNC 中可见,兼容 Computer-Use

生命周期:由 bridge 管理,首次 browser tool call 时拉起 Chromium 进程。Bridge 监控子进程,异常退出后下次调用重新拉起。Container 停止时 bridge 作为 PID 1 自然回收所有子进程。

2.3 Go 侧 Browser 操控(chromedp + CDP)

Go server 通过 Tunnel dial 127.0.0.1:9222 获取 CDP WebSocket 连接,使用 chromedp 库实现所有浏览器操控。

替代现有 BrowserProvider 中通过 HTTP 调用 Browser Gateway 的全部逻辑。Tool 定义(browser_action / browser_observe)对 agent 保持兼容。

chromedp 原生支持多 BrowserContext 和多 Tab 管理,替代原 Gateway 中基于内存 Map 的 storage + activePage 跟踪逻辑。

2.4 Agent Tools

保持现有 tool 定义兼容:

browser_action:navigate、click、dblclick、focus、type、fill、press、hover、select、check/uncheck、scroll、drag、upload、go_back/go_forward、reload、wait、tab_new/tab_select/tab_close

browser_observe:screenshot、screenshot_annotate、snapshot(accessibility tree)、get_content、get_html、evaluate、get_url、get_title、pdf、tab_list

实现从 HTTP Gateway 调用迁移到 chromedp CDP 调用,行为不变。

删除项

删除内容 说明
apps/browser/ 整个 Browser Gateway 应用
docker/Dockerfile.browser Gateway Docker 构建文件
devenv/Dockerfile.browser 开发环境 Gateway 构建文件
Browser Gateway 相关 docker-compose 配置 服务定义、端口、profile
internal/browsercontexts/ DB browser_contexts 表及 service(上下文配置迁入 bot settings)
db/migrations/0027_browser_contexts.* 对应数据库迁移
scripts/install.sh 中 Browser Gateway 相关部分 安装脚本中的 browser 选项、BROWSER_TAG 等

实现顺序

  1. Bridge Tunnel RPC — 共享基础设施,所有后续工作的前提
  2. Browser 迁移(Part 2) — 价值最高,替换现有 Gateway,用户可见功能不变
  3. Display bundle + Xvnc(Part 1.1-1.2) — VNC 基础能力
  4. Computer-Use RFB client + tools(Part 1.3-1.4) — agent GUI 操控
  5. noVNC 前端(Part 1.5) — 用户可视化控制台
  6. 桌面环境镜像变体(Part 1.6) — 可选增强

Metadata

Metadata

Assignees

No one assigned

    Labels

    featNew feature or requestsize-xl

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions