Computer Use 与远程桌面¶
Computer Use¶
Computer Use 是 cua-driver,一个第三方 MIT 二进制程序,OpenBot 负责管理它的运行方式。主进程中的 cua-driver-runtime.ts 启动并持有一个长期存活的 serve 守护进程;每个 provider CLI 都会针对该守护进程生成自己的短生命周期 cua-driver mcp --socket 代理。所有屏幕捕获、辅助功能读取和输入投递都发生在守护进程内部,因此代理自身的身份并不重要。
守护进程是直接生成的,绝不通过 open(1) 或 NSWorkspace。macOS 通过向上遍历启动链来查找责任进程,因此直接生成会让 OpenBot 位于启动链顶端,用户会将屏幕录制和辅助功能权限授予 OpenBot,而不是其他某个辅助程序。CUA_DRIVER_EMBEDDED=1 告诉驱动保持在该路径上运行,而不是重新将自己启动为自己的应用程序。任何以其他方式启动守护进程的做法都会破坏这种归属关系,这正是早期 Codex 辅助程序被替换的原因。
启动时会调用 warmUp(),它读取一次状态,并且只有在两个权限都已授予时才保留守护进程。已授予权限的用户在重启后仍会保留这些工具,远程请求或计划任务——两者都不会打开窗口——同样可以访问它们。未授予任何权限的用户不会保留任何进程,两种情况下都不会弹出提示:只有实际使用驱动时才会请求授权。所有其他启动都是惰性的,基于从面板读取的状态。
控制套接字位于私有的每用户运行时目录中,模式为 0o700,而不是在 /tmp 下:任何能访问到它的人都可以操控整个桌面。它不能放在 userData 下,因为在 macOS 上 sockaddr_un.sun_path 只有 104 字节,而隔离的开发配置会将其中大部分字节用于工作树哈希。Windows 使用命名管道,没有这样的限制;其名称是随机的,因为 Windows 允许第二个进程向它能猜到的名称添加实例,并且该名称保存在配置文件中,因此只需随机一次,而不是每次启动都随机。端点必须保持不动:它以参数形式到达每个代理,而这些参数会被合并到存储的 Codex 工具指纹中,因此每次启动都会变化的名称会在重启后替换所有会话。命令也必须保持不动,原因相同:打包的 Linux 构建是 AppImage,其资源在每次启动时都会挂载到不同位置,因此在 Linux 上,代理会获得配置文件下方的一个链接,运行时通过该链接指向本次运行的驱动。
一个 MCP 条目会到达每个 provider。CuaDriverRuntime.mcpServerConfig() 仅在守护进程运行时返回配置,而 AgentService.enabledMcpServers() 会将其附加进去,这是 Codex、Claude 和 ACP 都会读取的唯一函数。两个属性让它保留在那里:名称不在 RESERVED_MCP_SERVER_NAMES 中——这是一个丢弃过滤器而非标记;workingDirectory 保持为空,因为 ACP 没有对应字段,Codex 也不接受该字段,因此带工作目录的条目会在两个 provider 中无声消失。Codex 的过期状态不需要单独的信号,因为 toolFingerprint 已经包含了 MCP 条目,指纹变化会强制替换会话。onMcpServerChanged 负责刷新 agent 运行时,这会停用所有已存储的 provider 会话:下一轮会启动一个新会话,新会话保留公共线程,但丢失 provider 私下持有的内容。因此它只报告两个时刻——条目在用户请求的启动时出现,以及守护进程在 OpenBot 下终止。它在以下情况下保持沉默:守护进程服务期间授予的权限(这改变的是状态而非工具集);启动预热(这确定了已存储会话已有的条目);以及拆除时的停止(这发生在顺序 55,早于顺序 110 的 agent service,否则每次退出都会停用下一次运行本应恢复的会话)。
capabilities.computerUse 由主进程根据守护进程自身的权限应答推送。它不再通过 Codex 的 plugin/list 探测,这就是为什么该能力现在对每个 provider 都报告相同的状态。
驱动是打包好的,而不是按需下载,因此它像其他原生运行时一样固定在 native-runtime.lock.json 中,而不是像 provider CLI 那样管理。固定的文件列表是一个白名单:scripts/install-cua-driver.ts 只复制指定的路径并检查每个摘要,因此上游布局变更会导致构建失败,而不是附带意外文件。每个安装程序只携带自己的目标。在 macOS 上,mac.signIgnore 保留供应商的 Developer ID 签名,因为在 OpenBot 继承的权利下重新签名会丢失驱动所需的自动化权利。打包构建只读取 resources/cua-driver 下的副本,仅此而已,因为发布版本是针对该构建固定并签名的,环境变量绝不能决定哪个程序操控用户的桌面;不包含该二进制的发布版本会报告没有驱动。在检出(checkout)中,resolveCuaDriver 还会读取覆盖路径、安装目录和 PATH,因此开发者可以将 OPENBOT_CUA_DRIVER_PATH 指向另一个构建。
OpenBot 在每种显示布局下都通过自己的每显示叠加层绘制 agent 光标。运行时使用 --no-overlay 启动 serve。驱动的叠加层只覆盖主显示器,无法跟随启动后连接的显示器。将光标所有权保留在 OpenBot 中,让高亮控制器可以添加、调整大小和移除显示叠加层,而无需重启守护进程或更改 provider 会话。
服务器上的 Agent 显示¶
自托管服务器和容器会启动两个 Xvfb 显示器,并在 OPENBOT_AGENT_DISPLAY 中命名 agent 显示器。boat 托管的服务器将桌面命名为 :0,OpenBot 运行在 Xvfb 显示器上。Electron 保留自己的 DISPLAY:内置浏览器在 OpenBot 窗口中绘制其标签页,而在 Xvfb 下,隐藏的视图无法捕获也无法点击,因此 OpenBot 不能仅仅隐藏窗口。在 Linux 上,agent-display.ts 将 DISPLAY 设置为 agent 显示器,作用于驱动守护进程、Sunshine,以及 agentEnvironment 中的每个 agent 进程。驱动和 Sunshine 打开 DISPLAY 所命名的显示器,因此截图、输入事件和远程桌面流都使用 agent 显示器,agent 启动的程序也会在该显示器上打开。每显示叠加层是 OpenBot 窗口,因此使用 agent 显示器时它们不会启动:那里不会绘制 agent 光标或边缘。capture_screen 和 list_capture_sources 使用 Electron 的 desktopCapturer,后者只能看到 OpenBot 显示器,因此使用 agent 显示器时它们不可用,agent 会改用驱动来截图。
openbot.service 也管理 Xvfb 显示;停止该单元会同时向单元内的每个进程发送信号。如果 Xvfb 先停止,它就会在 OpenBot 关闭其数据库之前结束 OpenBot。因此,该单元的 ExecStop 会运行 openbot-hosted-server --stop:它只向 OpenBot 发送 SIGTERM,脚本会将 OpenBot 的 PID 写入 $XDG_RUNTIME_DIR/openbot.pid,然后等待其退出。容器不需要这样做:tini 只会向 OpenBot 发送信号。
由于驱动不绘制光标,其运动样式不适用。渲染器自行实现驱动的 adaptive 样式(agent-cursor-motion.ts,由驱动的 trajectory.rs 移植而来):Fitts 最小加加速度滑行,或超过 900 点移动时的宽弧线扫动。点击不会延迟请求,因此光标会显示操作发生的位置,时间在驱动执行后 180 ms 到约 1.1 s 之间。
OpenBot 启动的每个驱动副本都会获得 CUA_DRIVER_RS_TELEMETRY_ENABLED=0 和 CUA_DRIVER_RS_UPDATE_CHECK=0。OpenBot 随附该驱动,因此其供应商分析并非用户选择的结果;OpenBot 固定了版本,因此版本检查只能提供一个 OpenBot 会拒绝的更新。
远程桌面权限检查与实时测试¶
RemoteScreenGateway 负责设置检查以及实时测试会话的所有权。可选的 remote-desktop-setup Team API 能力使用单独的 v4 适配器路由;已发布的编解码器保持不变。诊断信息包含主机/账户名称和权限结果,并且只会传输给经过认证的成员。它们不包含 Sunshine 凭据或屏幕内容。渲染器仅通过固定的本地 IPC 操作打开 macOS 设置。
Sunshine 会检查自身的 macOS 权限,并托管临时的原生测试面板。测试期间,原生输入被限制在该面板内,并带有测试标签。面板需要同时具备测试标签和 Sunshine 进程 ID,才会记录点击或键盘结果。网关会在测试期间拒绝额外的会话和显示切换,并在其所属流断开时关闭面板。它不会为了开始测试而中断其他成员的会话。
本地测试使用绑定到 127.0.0.1 的临时 HTTP 监听器,不发布主机,也不要求账户。同样的单次使用查看者授权和 cookie 检查会保护它。网关拥有该监听器,并在测试会话结束时将其关闭;其租约在三分钟后到期。本地测试 IPC 只能寻址为此目的创建的会话。
本地纯视频测试可以在没有原生诊断的情况下运行。其查看器 iframe 是惰性的,并被排除在键盘焦点之外;它不会启动原生输入测试,也不会报告输入成功。本地回环测试 cookie 使用 HttpOnly、Secure 和 SameSite=None,以便嵌入式查看器能在应用源内正常工作。
查看器中的粘贴操作会将成员的文本放到主机的剪贴板上,然后按下主机的粘贴键。Moonlight 会将 Cmd+V 或 Ctrl+V 作为按键发送并取消它们,因此浏览器不会触发 paste 事件,主机会粘贴自己的剪贴板内容。网关将 openbot-paste.js 添加到 Moonlight 的 stream.html 中。该脚本阻止 Moonlight 收到成员的粘贴键,读取 paste 事件,并将文本发布到 moonlight/openbot-clipboard。两个路由都位于 moonlight/ 下,因为已发布的 Team API 适配器只转发该系列查看器路由。相同的查看器 cookie 或 WebRTC 会话检查会保护它们,而剪贴板路由会拒绝来自其他站点的请求。在 paste 事件发生时,脚本会按下主机的粘贴修饰键(macOS 主机为 Cmd,其他主机为 Ctrl),并释放成员按下的其他修饰键(Shift 除外)。Win 或 Super 键如果在没有其他按键间隔的情况下被释放,会打开主机的开始菜单或 Activities。上传完成后,脚本会按下 V。主机从先前粘贴中已有的文本不会再次发送,因此在主机上进行的复制会保留在其剪贴板上。没有剪贴板写入器的主机不会提供这两个路由。
在 Linux 上,网关只接受 X11 会话(设置了 DISPLAY,没有 WAYLAND_DISPLAY,且 XDG_SESSION_TYPE 不是 wayland)。此时 Sunshine 以 X11 捕获和软件编码运行,并通过 XTest 发送输入,因此 Xvfb 下的托管服务器不需要 uinput 设备,也不需要额外能力。在具有代理显示的服务器上,Sunshine 会在该显示上运行(参见 服务器上的代理显示)。Linux 没有权限检查:X11 会话即表示就绪。
Codex 工具审批¶
Codex 仅在 MCP 服务器位于文件支持的配置层时,才会保存“始终允许”的工具决策。仅靠线程配置不满足此检查。在启动或恢复使用 Computer Use 的线程之前,OpenBot 会通过 config/batchWrite 在 Codex 用户配置中注册 computer_use。新条目仅包含占位命令(openbot-mcp)和 enabled = false。写入使用用户层的版本,已有条目保持不变。OpenBot 不会在该条目中保存驱动套接字、环境或凭据。
线程配置会启用正在运行的驱动,并提供其当前的命令、参数和环境。它还携带已保存的按工具设置,包括 set_value.approval_mode。已保存的工具设置也包含在会话指纹中。当审批发生变化时,下一轮会替换持有旧策略的已加载 provider 会话。具有完全访问权限的代理不会询问:OpenBot 对每个 Computer Use 工具同意只接受一次,并且不会保存任何内容,就像它对其他 MCP 工具所做的一样。仅工作区或先询问的代理会显示该卡片,并且 Codex 会按工具和按提供方账户保留“始终允许”。当 Computer Use 对代理关闭或驱动不可用时,线程会保持已保存的条目为禁用状态。注册失败会停止线程,并给出修复配置或关闭 Computer Use 的步骤。来自驱动的应用访问询问仍使用其现有的审批流程。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 openbo/openbot