跳到主要内容

这是什么 · 全景 · 阅读地图

30 秒导读: open-computer-use(仓库名 open-codex-computer-use)把"让 AI 操作电脑"(Computer Use)封装成一个 MCP 服务。任何支持 MCP 的 agent(Codex、Claude Code、Gemini CLI…)装上它,就能在 macOS / Linux / Windows 上用无障碍树(Accessibility)+ 截图去点按、输入、滚动真实应用——而且是后台非抢占式的:它操作 App 的时候,你还能继续用别的窗口。


1. 这是什么(零基础也能懂)

一句话定义

它是一个"手脚服务":给已经会思考的 AI 补上"在真实电脑上动手"的能力,并用 MCP 这个通用插头对外暴露。

大模型本身只会输出文字。要让它真的去"点那个按钮、在输入框里打字",需要有人把它的意图落到屏幕上的真实控件。这个仓库就是干这件事的,而且把它做成任何 agent 都能即插即用的形态。

给谁用 / 解决什么问题

设想你在用一个 AI 编码助手(如 Codex CLI),你说"帮我在 TextEdit 里新建一个文档并写一段话"。模型知道该做什么,但它没有鼠标键盘。open-computer-use 就是补上这一环的服务:

  • 对 agent 开发者: 不用自己写一套跨平台的 UI 自动化,npm i -g 装上就有 9 个标准工具。
  • 对终端用户: 一条命令把它装进你已有的 agent,AI 从此能替你操作桌面 App。

一个关键取舍:非抢占式(non-intrusive)

这是本项目最核心的一个立场。对比两种做法就清楚了:

  • 传统视觉 CUA: 截图给模型看 → 模型说"点坐标 (x, y)" → 合成一次物理鼠标点击。问题是侵入:鼠标会真的动、窗口要抢到前台、坐标一错就点歪。
  • 本项目(非抢占式): 除了截图,还把应用的无障碍树(操作系统为读屏软件暴露的结构化 UI 树)给模型看,每个可操作元素都编了号(element_index)。模型说"操作 3 号元素",服务就直接调用系统的语义化无障碍动作(如 AXPress「按下」)——不移动物理鼠标,应用甚至不用在前台。物理指针合成只作可选兜底,默认关闭。

所以它能在后台操作 App,你的前台工作不被打断。README 明确写了这一点:"操作 App 的时候,用户可以继续用电脑上的其它 App"(见 packages/OpenComputerUseKit/Sources/OpenComputerUseKit/MCPServer.swiftcomputerUseServerInstructions 给模型的说明)。

它从哪来:复刻 Codex Computer Use

作者的灵感来自 OpenAI 的 Codex Computer Use——官方证明了"基于 Accessibility 就能做出非抢占式 CUA"(Computer Use Agent,计算机操作代理)。作者于是照着逆向观察做了一个开源版(仓库属主 iFurySt,见 README.md:16)。设计源头被记录在 docs/references/codex-computer-use-reverse-engineering/baseline-architecture.md:那份文档里观察到官方的 SkyComputerUseService 暴露出的能力面(Accessibility 树、ScreenCaptureKit 截图、EventTap 输入、ComputerUseCursor 光标、MCP/StdioTransport),以及一模一样的一组 tools(list_apps / get_app_state / click / …)。本仓库就是对这套设计的开源实现,而非 OpenAI 官方仓库。

对外的 9 个工具(一句话看清能力面)

都是"一个动作"级别的原子能力,细节见 02:

工具干什么
list_apps列出可操作的应用(运行中的 + 近期用过的)
get_app_state拉当前应用的 UI 快照(无障碍树 + 截图),每回合动手前必须先调
clickelement_index 或像素坐标点击
type_text往聚焦的输入框敲文字
press_key敲按键 / 组合键(支持 xdotool 的 key 语法)
scroll把某个元素往某方向滚动若干页
drag从一个坐标拖到另一个坐标
set_value直接给一个可写元素设值(无障碍层面)
perform_secondary_action触发元素暴露的次级动作(如 Raise/展开菜单)

用起来什么样(最小示例)

安装(npm 包同时提供短命令 ocu,见 README.md:43-46):

npm i -g open-computer-use
# macOS 首次运行需授权 Accessibility 和 Screen Recording;Windows/Linux 不需要
open-computer-use # 或 ocu

装进你的 agent(以 Codex 为例,一键写入 ~/.codex/config.toml):

open-computer-use install-codex-mcp

或手动加进任意 MCP 客户端(README.md:65-74):

{
"mcpServers": {
"open-computer-use": {
"command": "open-computer-use",
"args": ["mcp"]
}
}
}

也能不经 agent、直接命令行调一个工具试水(README.md:126-128):

open-computer-use call list_apps
open-computer-use call get_app_state --args '{"app":"TextEdit"}'

一句话直觉/类比

把它想成"给 AI 的一副遥控手套"。 手套自己不思考;模型说"点第 7 个元素",手套就精确地替它按下去。而"第 7 个元素"是谁,来自上一步拿到的一张当前界面清单(无障碍树 + 截图)——而不是让 AI 举着鼠标去戳屏幕。


2. 顶层全景(它大概怎么转)

2.1 一张顶层图:从 agent 到真实应用

怎么读这张图: 从上到下是一次工具调用穿过的层。上半段是"通用外壳"(MCP/CLI 入口),下半段是"真正干活的核心"(拿快照 + 落动作)。核心闸门在 InputSimulation 那格的「AX 优先 / 指针兜底」。

┌─────────────────────────────────────────┐
│ MCP 客户端 / agent(Codex、Claude…) │ ← 模型在这里决策
│ 或 CLI:open-computer-use call … │
└───────────────┬─────────────────────────┘
│ stdio 上的 JSON-RPC(MCP 协议)

┌─────────────────────────────────────────┐
│ StdioMCPServer │ 通用外壳:收发 JSON-RPC
│ (MCPServer.swift) │ initialize / tools/call …
└───────────────┬─────────────────────────┘
│ callTool(name, arguments)

┌─────────────────────────────────────────┐
│ ComputerUseToolDispatcher │ 路由:9 个工具名 → 服务方法
│ (ComputerUseToolDispatcher.swift) │ 顺带把 element_index 规整成字符串
└───────────────┬─────────────────────────┘


┌─────────────────────────────────────────┐
│ ComputerUseService │ 业务核心:回合状态 + 动作编排
│ (ComputerUseService.swift) │
└──────┬───────────────────────┬──────────┘
│ │
▼ ▼
┌───────────────┐ ┌───────────────────┐
│ SnapshotBuilder│ │ InputSimulation │
│ 拿 UI 快照: │ │ 落动作:AX 语义 │
│ 无障碍树+截图 │ │ 优先,退回坐标合成 │
│(AccessibilitySnapshot.swift) (InputSimulation.swift) │
└───────────────┘ └───────────────────┘
(软件光标 SoftwareCursorOverlay 叠加在动作发生处,让后台操作"看得见")

2.2 部件一句话职责

部件干什么在哪个文件
StdioMCPServer逐行读 stdin 的 JSON-RPC,处理 initialize / tools/call,把结果写回 stdoutpackages/OpenComputerUseKit/Sources/OpenComputerUseKit/MCPServer.swift:26
ComputerUseToolDispatcher按工具名把调用分派到 service 的方法;规整 element_index 入参.../ComputerUseToolDispatcher.swift:38(callTool)
ComputerUseService核心:管每回合的快照状态,编排每个动作.../ComputerUseService.swift:355 起的一组 public func
SnapshotBuilder构建当前 App 的无障碍树 + 截图快照.../AccessibilitySnapshot.swift:97(SnapshotBuilder)
InputSimulation把动作合成为真实输入(点击/输入/滚动/拖拽).../InputSimulation.swift:45(InputSimulation)
SoftwareCursorOverlay在操作处画一个软件光标,让后台动作可见.../SoftwareCursorOverlay.swift
ToolDefinitions9 个工具的名字、描述、JSON Schema(对外契约).../ToolDefinitions.swift:31(ToolDefinitions.all)

2.3 三平台的代码布局

同一套对外契约,三种实现。Swift kit 是参考实现,Linux/Windows 是"Go 壳 + 脚本 runtime"。

平台形态无障碍/输入底座位置
macOSSwift 库 OpenComputerUseKit + 可执行 OpenComputerUse(参考实现)系统 Accessibility API、ScreenCaptureKitEventTappackages/OpenComputerUseKit/apps/OpenComputerUse/
LinuxGo 二进制外壳 + 内嵌 Python runtimeAT-SPI2 语义动作(gi/Atspi)apps/OpenComputerUseLinux/(main.go//go:embed runtime.py)
WindowsGo 二进制外壳 + 内嵌 PowerShell runtimeUI Automationapps/OpenComputerUseWindows/(main.go + runtime.ps1)

Go 壳负责说 MCP/JSON-RPC(和 Swift 版同一套协议、同一组工具名),把具体的"读树/落动作"下放给内嵌脚本执行。npm 包按 os/cpu 分发对应平台的二进制(见 scripts/npm/build-packages.mjs:31-65:darwin/linux/win32 × arm64/x64)。跨平台细节见 05

2.4 主线一句话走一遍

核心节奏就一句话:拿快照 → 选下标 → 动作 → 再拿快照。

  1. 拿快照: agent 每个回合先调 get_app_state,得到当前 App 的无障碍树(每个可交互元素带一个整数下标 element_index)+ 一张截图。
  2. 模型选下标: 模型读这张清单,决定"要点第 7 个元素"。
  3. 动作: agent 调 click(或 type_text / scroll / …),把 element_index 传进来,服务复用刚才那张快照定位到真实控件,优先尝试无障碍动作(AXPress 等一串级联),只有全失败且开了兜底开关,才合成物理鼠标点击。
  4. 刷新快照: 界面变了,下一回合再 get_app_state 看结果——回到第 1 步。

这套"每回合先取状态再行动"的纪律,是服务写给模型的硬性说明(MCPServer.swiftcomputerUseServerInstructions:"Begin by calling get_app_state every turn … this step is required")。代码上也能看出这层配合:getAppState刷新并缓存快照(ComputerUseService.swift:363,调 refreshSnapshot),而 click 等动作复用当前缓存的快照来查 element_index(ComputerUseService.swift:367,调 currentSnapshot)——所以动作前必须先有一张快照。这条主线的两半分别在 01 状态快照03 动作落地 展开。


3. 阅读地图(各章讲什么、怎么读)

建议顺序:先读本章建立全景,再顺着"契约 → 状态 → 动作"往深走。

顺序章节讲什么什么时候读
0本章 index.md这是什么、顶层全景、阅读地图入口,先读
102-mcp-tools-surface.md对外契约:9 个工具的语义、MCP/JSON-RPC 报文、CLI 子命令想知道"agent 到底能调什么"时
201-state-snapshot.md状态模型:每回合的 UI 快照如何构建——无障碍树 + 截图、element_index 从哪来想懂"模型看到的界面清单是什么"时
303-action-execution.md动作落地引擎(最深一章):AX 优先的点击级联、退回坐标的输入合成想懂"一次点击到底怎么落到控件上"时
404-software-cursor.md软件光标:后台操作也"看得见"的视觉叠层关心可视化/用户感知时
505-cross-platform-and-packaging.md跨平台一致性、权限、分发:三平台如何对齐、npm/skill 安装关心 Linux/Windows 实现与打包时

三条推荐主线:


4. 巧妙之处(可借鉴的技术)

先白话点出"妙在哪",再给锚点。

  • 把"操作 UI"从「点像素」抽象成「操作编号元素」。 快照给每个可交互元素编了个稳定的 element_index,模型只需说编号;服务再把编号翻译成语义动作或坐标。这让模型不必做像素级视觉定位,错误率和对截图分辨率的依赖都大幅下降。(索引→元素的登记与回查见 01)

  • AX 优先、物理指针兜底,且兜底默认关。 点击不是"移动鼠标点下去",而是先跑一整套无障碍动作级联;只有全失败才考虑合成 CGEvent,且需要环境变量 OPEN_COMPUTER_USE_ALLOW_GLOBAL_POINTER_FALLBACKS=1 才允许全局物理点击。这正是"非抢占式"的落点。(详见 03)

  • postToPid 定向投递事件。 即便走合成路径,默认也用 event.postToPid(pid) 把事件只投给目标进程,而不是 cghidEventTap 全局投递——这样才可能"后台操作而不抢当前窗口"。

  • 每回合强制先 get_app_state 服务指令里写死这条约定,且动作类工具会在内存里缓存上一次快照,没有快照就报错要求先拉状态——保证 element_index 永远对应"这一回合看到的树"。

  • 合成可见的软件光标。 后台 AX 操作本来"看不见",项目专门画一个软件光标叠层并做点击脉冲动画,让人类旁观时仍能看懂 AI 在点哪——把可观测性当一等公民。(详见 04)

  • 一套契约,三个 OS。 三个平台的工具名、schema、JSON-RPC 行为逐字对齐,底层各用本地无障碍栈(AX / AT-SPI2 / UI Automation),对上游 AI 客户端完全一致。(详见 05)


5. 代码地图(导航索引)

一张表让人/agent 直接跳进源码。路径均相对克隆根,行号 as-of sourceCommit;行号可能漂移,优先用符号名 grep

主题文件路径符号名
项目定位 / 最小用法README.mdREADME.zh-CN.md
设计源头(逆向观察)docs/references/codex-computer-use-reverse-engineering/baseline-architecture.md
MCP 入口(JSON-RPC over stdio)packages/OpenComputerUseKit/Sources/OpenComputerUseKit/MCPServer.swift:26StdioMCPServer / computerUseServerInstructions
工具分派packages/OpenComputerUseKit/Sources/OpenComputerUseKit/ComputerUseToolDispatcher.swift:38ComputerUseToolDispatcher.callTool
业务核心(回合状态 + 动作)packages/OpenComputerUseKit/Sources/OpenComputerUseKit/ComputerUseService.swift:363getAppState / click / refreshSnapshot / currentSnapshot
UI 快照(无障碍树 + 截图)packages/OpenComputerUseKit/Sources/OpenComputerUseKit/AccessibilitySnapshot.swift:97SnapshotBuilder / AppSnapshot
输入合成packages/OpenComputerUseKit/Sources/OpenComputerUseKit/InputSimulation.swift:45InputSimulation
软件光标叠层packages/OpenComputerUseKit/Sources/OpenComputerUseKit/SoftwareCursorOverlay.swiftSoftwareCursorOverlay
9 个工具的 Schema/契约packages/OpenComputerUseKit/Sources/OpenComputerUseKit/ToolDefinitions.swift:31ToolDefinitions.all
CLI 子命令(mcp/doctor/call/install…)packages/OpenComputerUseKit/Sources/OpenComputerUseKit/OpenComputerUseCLI.swift:3OpenComputerUseCLICommand
macOS 应用运行时apps/OpenComputerUse/Sources/OpenComputerUse/MCPAppRuntime.swift:5MCPAppRuntime
Swift 包结构(参考实现)Package.swiftOpenComputerUseKit / OpenComputerUse
Linux runtime(Go 壳 + 内嵌 Python)apps/OpenComputerUseLinux/main.goruntime.py//go:embed runtime.py
Windows runtime(Go 壳 + 内嵌 PowerShell)apps/OpenComputerUseWindows/main.goruntime.ps1
npm 平台分发映射scripts/npm/build-packages.mjs:31-65darwin/linux/win32 × arm64/x64

脚注:本页引用的 path:line(as-of 8fada12)

  • 项目定位 / 灵感来源 / 属主 iFurySt:README.md:14README.md:16
  • MCP 服务与"每回合先 get_app_state"的指令:MCPServer.swift:26(StdioMCPServer)、:3(computerUseServerInstructions)。
  • 工具路由与 element_index 归一化:ComputerUseToolDispatcher.swift:38(callTool)、:3(normalizedElementIndexArgument)。
  • 核心引擎:ComputerUseService.swift:363(getAppState)、:367(click)、:649(currentSnapshot)、:658(refreshSnapshot)。
  • 工具定义(9 项):ToolDefinitions.swift:32(ToolDefinitions.all)。
  • 快照与树渲染:AccessibilitySnapshot.swift:97(SnapshotBuilder)。
  • 打包 / 平台:Package.swift:7(.macOS(.v14))、README.md:43(npm i -g open-computer-use)、scripts/npm/build-packages.mjs:31-65(平台分发映射)。