跳到主要内容

跨平台一致性、权限与分发

30 秒导读: 前四章讲的是"一台机器上,一个 computer-use agent 怎么看见 UI、怎么把动作落地"。 这一章讲的是工程收尾:同样这 9 个工具、同样的 MCP 契约,怎么在 macOS、Linux、Windows 三套 完全不同的无障碍后端上都跑起来;跑起来之后,操作系统的权限该挂在谁身上;最后这一坨东西怎么打成 一个 npm install -g 就能用的包。

本章不再讲"怎么点一个按钮"(那是 03-action-execution 的活),而是讲 "同一份契约如何铺到三平台并落地安装"。


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

一句话: 这个项目对外只承诺一件事——一个叫 open-computer-use 的 MCP 服务器,提供 9 个工具 (list_appsget_app_stateclicktype_text……详见 02-mcp-tools-surface)。 本章讲的是:这一份承诺,在三个操作系统上是三套实现,但对上层 agent 看起来必须长得一样

为什么这很难? 三个系统"读 UI + 动 UI"的原生技术栈毫不相干:

平台无障碍技术语言/形态
macOSAccessibility (AX) API + CoreGraphics 截图Swift 原生 .app
LinuxAT-SPI2(经 D-Bus)+ GDKGo 壳 + 内嵌 runtime.py
WindowsUI Automation (UIA) patternsGo 壳 + 内嵌 runtime.ps1

三套后端,却要给上层同一套工具名、同一套参数 schema、几乎同一套 serverInstructions做到"看起来一样" 本身就是一项工程,这是本章第一条主线。

第二条主线是权限。 computer-use 要读别人窗口、要截屏,操作系统天生把这类能力锁得很死。三平台 各有各的门槛:macOS 要 TCC 授权(Accessibility + Screen Recording),Linux 要能连上登录用户的桌面 会话(D-Bus / Wayland / X11),Windows 要跑在登录桌面会话里。光有代码不够,得让权限"挂得住"。

第三条主线是分发。 最终交付物是一个 npm 包,npm install -g open-computer-use 之后,一条 open-computer-use mcp 就能给任意 MCP 客户端(Claude / Codex / Gemini / opencode)当后端。

用起来什么样(macOS):

npm install -g open-computer-use
open-computer-use doctor # 查权限,缺了就弹引导窗
open-computer-use install-codex-mcp # 把自己写进 ~/.codex/config.toml
open-computer-use mcp # 启动 stdio MCP 服务器

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

2.1 一张图:同一份契约,三条落地路径

下面这张图从左到右是"上层 agent 看到的东西 → 平台分叉 → 各自的原生后端"。关键:虚线框里三支 共享同一套工具定义和 serverInstructions,只是执行体不同。

MCP 客户端 (Claude / Codex / Gemini / opencode)
│ stdio JSON-RPC

┌───────────────────────────────────────────┐
│ 同一份对外契约(9 工具 + serverInstructions)│
│ tools/list 三平台字段对齐,措辞仅按平台微调 │
└───────────────────────────────────────────┘
│ │ │
┌───────┘ ┌──────┘ ┌──────┘
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│ macOS │ │ Linux │ │ Windows │
│ Swift │ │ Go 壳 │ │ Go 壳 │
│ 原生.app │ │+runtime.py│ │+runtime.ps1│
│ AX API │ │ AT-SPI2 │ │ UIA │
└────┬────┘ └────┬─────┘ └────┬─────┘
│ │ │
截屏走 CG 发现桌面会话 跑在登录
AX 读树 (/proc environ) 桌面会话里
│ 起 python3 起 powershell
▼ ▼ ▼
TCC 授权 D-Bus/Wayland UIA 只读也
(App 代理) /XDG 环境变量 要会话可达

2.2 部件一句话职责

部件干什么在哪
macOS 主程序Swift .app,内含全部 AX 逻辑apps/OpenComputerUse/Sources/OpenComputerUse/OpenComputerUseMain.swift
Linux Go 壳解析 CLI/MCP,内嵌并调用 runtime.pyapps/OpenComputerUseLinux/main.go
Windows Go 壳解析 CLI/MCP,内嵌并调用 runtime.ps1apps/OpenComputerUseWindows/main.go
App 代理macOS:把 CLI 请求代理进稳定 .app 实例apps/OpenComputerUse/Sources/OpenComputerUse/MacOSAppAgentProxy.swift
权限诊断查 AX / 截屏 授权,读 TCC.dbpackages/…/OpenComputerUseKit/Permissions.swift
引导窗macOS 缺权限时弹窗引导apps/…/PermissionOnboardingApp.swift
npm 打包器把三平台二进制打成一个包scripts/npm/build-packages.mjs
install-*把自己写进各 agent 的 MCP 配置scripts/install-*.sh

3. 核心机制一:同一份契约,三份实现如何对齐

3.1 它要解决的小问题

三份代码库(一份 Swift、两份 Go),都要向 MCP 客户端返回同样的 tools/list——同样的工具名、 同样的参数、同样的注解。字段一旦漂移,同一个 agent 换个平台就用不了了。

3.2 三份 serverInstructions 的对齐与差异

三平台的 serverInstructions 骨架一致(先要求每回合先 get_app_state、再列出同样 9 个工具、 再劝"优先按 element index 而非坐标点击"),但结尾一段各写各的"non-intrusive"(不打扰用户)承诺, 因为每个系统"不抢焦点"的技术手段不同:

平台结尾"non-intrusive"措辞要点源码
macOS强调"在后台操作 app,用户可继续用别的;别覆盖剪贴板等破坏其活动会话"MCPServer.swift:12
LinuxAT-SPI2 语义动作优先;坐标/按键合成只是尽力而为,不是通用的 Wayland 后台输入模型apps/OpenComputerUseLinux/main.go:28
WindowsUIA patterns 优先、无 pattern 才退回窗口消息;不 auto-launch、不 SetFocus、默认不用 UIA 文本兜底,所以后台动作不故意抢前台焦点apps/OpenComputerUseWindows/main.go:25

一句话:"9 工具 + 先取状态"是三家硬对齐的公共骨架;"我怎么做到不打扰你"是各平台按自己能力 写的差异化脚注。 macOS 的措辞最丰富(还提到"有专用 skill 优先用 skill、别退回 AppleScript"), Linux/Windows 的更聚焦于"我这套后台输入到底靠不靠得住"。

3.3 toolDefinitions 逐字段对齐

Go 侧把工具定义写死成一个函数,和 Swift 侧的字段一一对应。看 Linux 的 click 定义 (apps/OpenComputerUseLinux/main.go:993-1005):它的 app / element_index / x / y / click_count / mouse_button 参数、readOnlyAnnotations vs defaultAnnotations 的用法,都刻意和 macOS 版对齐。 Windows 的 toolDefinitions() 结构完全相同——这是"手工保持一致"而非共享代码,所以对齐是纪律, 不是机制(工具面详见 02-mcp-tools-surface)。

3.4 Go 壳的"套娃"结构

Linux/Windows 不是把无障碍逻辑写在 Go 里,而是:Go 负责 MCP/CLI 协议 + 进程编排,真正读写 UI 的脚本用 //go:embed 编进二进制

  • Linux://go:embed runtime.py(apps/OpenComputerUseLinux/main.go:25-26),每次调用把脚本 写到临时目录、连同一个 operation.json 一起交给 python3 跑,30s 超时(runPython,main.go:438-489)。
  • Windows://go:embed runtime.ps1(apps/OpenComputerUseWindows/main.go:22-23),同理交给 PowerShell。

runtime.py 里真正干活的是 AT-SPI2:gi.require_version("Atspi", "2.0") 后用 Atspi.get_desktop(0) 拿桌面树、Atspi.Component.get_extents(...) 取坐标 (apps/OpenComputerUseLinux/runtime.py:16/67/114)。runtime.ps1Add-Type -AssemblyName UIAutomationClient,用 AutomationElement.FromHandle 拿窗口、按 InvokePattern / TogglePattern / ExpandCollapsePattern 分发动作(apps/OpenComputerUseWindows/runtime.ps1:13/332/364)。

为什么这么套? Go 编译出的单文件二进制好分发、跨架构好交叉编译;但 Linux 的 AT-SPI2 和 Windows 的 UIA 都是"脚本语言 + 系统运行时"最顺手。于是 Go 只当"协议壳 + 进程管家",把脏活 外包给系统自带的 python3 / powershell。


4. 核心机制二:Linux 怎么找到"那个正确的桌面会话"

4.1 它要解决的小问题

Codex/MCP 客户端启动这个进程时,很可能不带桌面环境变量——没有 XDG_RUNTIME_DIR、没有 DBUS_SESSION_BUS_ADDRESS、没有 WAYLAND_DISPLAY/DISPLAY。可 AT-SPI2 必须连到登录用户那个 具体桌面会话的 D-Bus才能读到 UI。缺了这些变量,runtime.py 一连就报"没有可见窗口"。

4.2 思路:从 /proc 反查桌面进程的环境

既然自己没有,就去同一个 uid 名下正在跑的桌面进程身上"偷"这些环境变量。Linux 每个进程的 /proc/<pid>/environ 是它启动时的完整环境快照——读它就能拿到真实会话地址。

4.3 打分挑最可信的会话

desktopProcessEnvironments(apps/OpenComputerUseLinux/main.go:673-719)遍历 /proc,只看本 uid 拥有的进程,给每个进程按"像不像桌面核心进程"打分(desktopProcessRank,main.go:733-773):

命中的进程名分数(越高越可信)
gnome-session100
gnome-shell / plasmashell / kwin_wayland / sway95
Xorg80
各种终端(konsole / kitty / alacritty…)65
codex50
dbus-daemon45

再叠加"这个进程的 environ 里带了多少会话信号"的加分(sessionEnvRank,main.go:775-791: 每有 XDG_RUNTIME_DIR/DBUS_... +20,DISPLAY/WAYLAND_DISPLAY +10)。排序后,得分最高的会话 胜出。

4.4 组装最终环境

拿到候选会话后,linuxRuntimeEnvironmentFrom(main.go:496-529)按优先级填补缺失变量:

  • XDG_RUNTIME_DIR:自己的 → 候选进程的 → /run/user/<uid>,逐个 validRuntimeDir 校验属主(chooseRuntimeDir,main.go:531-554)。
  • DBUS_SESSION_BUS_ADDRESS:优先用 <runtimeDir>/bus 这个 socket(sessionBusAddress,main.go:556-587)。
  • WAYLAND_DISPLAY:规范化后校验对应 socket 真的存在,否则扫 wayland-0/1(waylandDisplay,main.go:601-656)。
  • 其余(DISPLAY / XAUTHORITY / XDG_CURRENT_DESKTOP / AT_SPI_BUS_ADDRESS…)缺则从候选会话补齐(main.go:510-526)。

这套"从 /proc 发现会话"正是 doctor 命令里那句提示所描述的行为 (apps/OpenComputerUseLinux/main.go:1154)。macOS/Windows 没有这一步:macOS 的 AX 直接跑在 用户会话里,Windows 要求"跑在登录桌面会话"即可。


5. 核心机制三:macOS 的 App 代理——把权限钉在稳定的 bundle id 上

5.1 它要解决的小问题

macOS 的 TCC 权限(Accessibility、Screen Recording)是按"谁来请求"授权的。npm 装出来的 CLI 可执行文件路径每次升级都可能变;而 TCC 授权一旦掉了,用户就得重新拖一次、重新勾一次。如果权限 挂在"这个随时会变的 CLI 二进制"上,升一次级授权就没了。

5.2 思路:让稳定的 .app 来干活,CLI 只当传声筒

项目把真正的执行体放进一个有固定 bundle id com.ifuryst.opencomputeruseOpen Computer Use.app(Permissions.swift:112)。CLI 收到请求后不自己执行,而是通过 LaunchServices 启动/ 连接这个 .app 实例,把请求代理进去。这样权限永远挂在稳定的 bundle id 上,CLI 怎么升级都不影响

5.3 什么时候走代理

入口在 OpenComputerUseMain.run():解析出命令后先问一句 MacOSAppAgentProxy.shouldProxy(command:) (OpenComputerUseMain.swift:36)。它转调纯函数 shouldUseMacOSAppAgentProxy (OpenComputerUseCLI.swift:22-40),决策表:

条件结果
代理被环境变量禁用,或找不到 .app bundle不代理(降级为直接跑)
mcp / doctor / list-apps / snapshot / call代理
launchOnboarding不是已经从 LaunchServices .app 实例跑的代理
help / version / turn-ended不代理(纯本地)

.app 是否可用由 PermissionSupport.currentAppBundleURL() 判定;是否"我自己已经就是那个 .app 实例"由 isRunningFromLaunchServicesAppInstance 判定——后者要求既是本项目的 bundle id、父进程又是 launchd(getppid() == 1,MacOSAppAgentProxy.swift:64-66)。

5.4 代理怎么连:Unix socket + 版本自愈

connectOrLaunchAgent(MacOSAppAgentProxy.swift:75-108)的流程:

1. 尝试连接固定路径的 Unix socket
(temp/open-computer-use-agent.sock)
2. 连上了?→ 问它 agentInfo,确认 bundleURL 一致
且进程启动时间 ≥ 可执行文件修改时间(isCurrentAgent)
├─ 是当前版本 → 直接复用
└─ 是旧版本 → 发 terminate,unlink socket,重启
3. 连不上 → 用 NSWorkspace.openApplication 起一个
新实例(activates=false,不抢焦点),
带上隐藏参数 __open-computer-use-app-agent + socket 路径
4. 轮询 10s 等 socket 出现,超时报错

关键细节:configuration.activates = false(MacOSAppAgentProxy.swift:94)——.app 但不激活、 不抢用户前台焦点,呼应第 3 节的 "non-intrusive" 承诺。isCurrentAgent (MacOSAppAgentProxy.swift:517-536)用"进程启动时间 vs 可执行文件修改时间"判断代理是不是旧版本, 升级后旧代理会被自动换掉——版本自愈

5.5 socket 两端说什么

.app 里的 MacOSAppAgentRuntime.accessory 激活策略常驻(不进 Dock),监听 socket (MacOSAppAgentProxy.swift:154-209)。每个连接由 AppAgentConnection 处理,按 kind 分四种请求 (MacOSAppAgentProxy.swift:313-353):agentInfo(报身份)、terminatemcp(把一行 JSON-RPC 喂给内部 StdioMCPServer)、cli(在环境覆盖下跑一次 CLI)。于是无论 CLI 还是 MCP,最终都在那个 稳定 bundle id 的 .app 进程里执行——权限自然就挂在它身上。

5.6 MCPAppRuntime:非代理路径下的等价物

不走代理、但要跑 MCP + 软件光标时(见 04-software-cursor),走的是 MCPAppRuntime.run(server:)(MCPAppRuntime.swift:14-26)。它同样把进程设成 .accessory 的 NSApplication(好让 AppKit 事件循环转起来,叠层窗口才画得出),另开线程跑 stdio server.run(),并订阅 turn-ended 分布式通知在回合结束时复位光标(MCPAppRuntime.swift:28-39)。 OpenComputerUseMain 里只有 VisualCursorSupport.isEnabled 时才套这层 AppKit 外壳,否则纯 stdio 直跑(OpenComputerUseMain.swift:44-50)。


6. 核心机制四:权限模型与 doctor

6.1 三平台的门槛对比

平台门槛检测/引导手段
macOSTCC:Accessibility + Screen RecordingPermissionDiagnostics + 引导窗 + doctor
Linux能连上登录用户桌面会话的 AT-SPI2/D-Busdoctor 打印会话发现说明(无 GUI 引导)
Windows跑在登录桌面会话里(UIA 可达)doctor 打印运行环境说明

三家的 doctor 命令都存在,但只有 macOS 会真的弹 UI 去修;Linux/Windows 的 doctor 只是 打印一段"你得跑在正确的会话里"的文字提示(apps/OpenComputerUseLinux/main.go:1154apps/OpenComputerUseWindows/main.go:737)。

6.2 macOS 权限怎么查(双通道)

PermissionDiagnostics.current()(Permissions.swift:71-86)对每项权限走两条通道取并集:

  • 运行时 API:AXIsProcessTrusted()(Accessibility)、CGPreflightScreenCaptureAccess()(截屏)。
  • 持久化 TCC.db:直接以只读打开 /Library/Application Support/com.apple.TCC/TCC.db,查 kTCCServiceAccessibility / kTCCServiceScreenCaptureauth_value(TCCDatabase.authorization, Permissions.swift:499-546;auth_value == 2 记为已授权,tccAuthorizationGranted,Permissions.swift:464-466)。

permissionGranted(Permissions.swift:468-470)取"持久化==true 运行时==true"。为什么要 读 TCC.db? 因为运行时 API 只反映"当前这个进程"的授权,而权限是挂在 .app bundle id 上的;通过 currentPermissionClients() 枚举候选客户端(canonical bundle id、正在跑的 bundle、几条路径, Permissions.swift:145-214)去查库,才能判断"那个稳定 .app"到底有没有被授权。

暴露给上层的两个布尔就是任务里点名的 accessibilityTrustedscreenCaptureGranted (Permissions.swift:68-69);summary 把它们渲染成 doctor 的输出行(Permissions.swift:88-90)。

6.3 缺权限就弹引导窗

doctor 命令:打印 summary,若 missingPermissions 非空就调 PermissionOnboardingApp.launch() (OpenComputerUseMain.swift:51-56)。引导窗 (PermissionOnboardingApp.swift:10-37)以 .accessory 策略起一个 NSWindow,逐项展示 Accessibility / Screenshots 的说明、系统设置深链(SystemPermissionKind.settingsURL, Permissions.swift:29-36)和"把 App 拖上去授权"的引导文案。走代理路径时,doctor/launchOnboarding 也会在 .app 代理进程里弹这个窗(MacOSAppAgentProxy.swift:365-381)——保证弹窗的是那个稳定 bundle id 的 .app,拖进设置里的也正是它


7. 核心机制五:npm 分发

7.1 一个包,捆三平台

打包器 scripts/npm/build-packages.mjs 产出三个同名义的 meta 包 (open-computer-use / open-computer-use-mcp / open-codex-computer-use-mcp,build-packages.mjs:24-28)。

注意实际实现: 该脚本没有用 optionalDependencies 拆分平台子包,而是把六个 os-arch 目标的原生运行时全部塞进同一个包(runtimeTargets,build-packages.mjs:29-70:darwin arm64/x64 的 .app,linux 与 windows 的 arm64/amd64 二进制),再由 Node 启动器在运行时按 process.platform-process.arch 选中对应可执行文件。(inferred:任务描述提到的 "optionalDependencies 多平台"在本 commit 的脚本里并未采用,当前是单包捆绑。)

7.2 启动器:运行时选平台 + 命令分流

renderLauncher()(build-packages.mjs:214-374)生成的 bin/ 脚本做两件事:

  1. 选原生运行时:resolveNativeExecutable()process.platform-process.archplatformPackages 表,取对应 executablePath,不存在就报"缺 bundled runtime,重装" (build-packages.mjs:318-335)。
  2. 命令分流:若命令在 installCommands 映射里(install-claude-mcpinstall-codex-mcp…, build-packages.mjs:224-231),就去 scripts/ 下跑对应 .sh;否则把参数原样转发给原生二进制 (build-packages.mjs:367-372)。

7.3 四个命令别名,含 ocu

同一个启动器被写进四个 bin 别名:open-computer-useocuopen-computer-use-mcpopen-codex-computer-use-mcp(build-packages.mjs:515-520bin 字段,616-619 写四份可执行)。 ocu 就是 open-computer-use 的短别名,四者行为完全一致。

7.4 install-* 各 agent 安装脚本

copyInstallerScripts(build-packages.mjs:547-564)把一组安装脚本拷进包里,它们把 open-computer-use mcp 这一条写进各家 agent 的配置:

命令写到哪脚本
install-claude-mcp~/.claude.json(本项目)scripts/install-claude-mcp.sh
install-codex-mcp~/.codex/config.tomlscripts/install-codex-mcp.sh
install-gemini-mcpGemini CLI 配置(可 --scope)scripts/install-gemini-mcp.sh
install-opencode-mcp~/.config/opencodescripts/install-opencode-mcp.sh
install-codex-plugin本地 Codex 插件缓存scripts/install-codex-plugin.sh

这些脚本幂等:同一条 MCP 条目已存在就原样不动(见 install-codex-mcp.sh 的 usage 说明)。它们 共享一个 install-config-helper.mjs 做实际的配置读写。Windows 上这些 install-* 命令会拒绝执行、 提示"手动配置 command=open-computer-use / args=[mcp]"(build-packages.mjs:305-308,因为它们是 POSIX shell 脚本)。

7.5 postinstall 提示

renderPostinstall()(build-packages.mjs:376-406)生成的 postinstall.mjs 在安装后打印:四个命令 别名、"macOS 记得跑 doctor 授权"、以及可直接粘贴的 mcpServers JSON。这就把本章三条主线串成 用户的第一分钟体验:装包 → doctor 授权 → install-* 写配置 → mcp 跑起来。


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

  • 契约对齐靠纪律 + 纯函数,不靠共享代码。 三份实现分属 Swift/Go,toolDefinitions 手工对齐; 但决策逻辑抽成可测纯函数(如 shouldUseMacOSAppAgentProxy,OpenComputerUseCLI.swift:22), 用单测锁住行为,避免三处逻辑各自漂移。

  • 权限锚点与执行体解耦。 让稳定 bundle id 的 .app 当权限锚点,CLI 只当传声筒——升级 CLI 不 丢授权。这是 macOS TCC 场景下很值得抄的一招(MacOSAppAgentProxy.swift 全篇)。

  • "从 /proc 偷会话环境"是 headless 启动的通用解法。 任何"被无桌面环境的父进程拉起、却要操作 GUI"的 Linux 工具都会遇到同一个坑;这里的"打分挑最可信桌面进程 + 逐变量校验 socket 真存在" (main.go:673-719 / 531-656)是一套可复用的稳健实现。

  • Go 壳 + //go:embed 脚本。 用 Go 拿到"单文件、好交叉编译"的分发优势,又把系统相关的脏活 外包给自带的 python3/powershell——两全其美(main.go:25 / apps/OpenComputerUseWindows/main.go:22)。

  • 单 npm 包捆全平台二进制。 牺牲一点包体积,换来"装一次、任意 os-arch 都能跑"和"启动器 运行时选平台"的简单心智(build-packages.mjs:214-374)。


9. 边界与局限(诚实)

  • 契约一致是"人肉维护"的。 三份 toolDefinitions / serverInstructions 没有单一真源,靠开发者 同步;字段漂移不会被编译器拦住,只能靠测试和 review 兜。

  • App 代理是 macOS 独有。 Linux/Windows 没有"稳定 bundle id 权限锚点"这套东西;它们的门槛是 "会话可达",一旦进程跑在错误会话里就直接失败,没有等价的自愈/引导。

  • Linux 后台输入并不通用。 serverInstructions 自己声明:AT-SPI2 语义动作优先,坐标/按键合成只是 尽力而为,不是通用的 Wayland 后台输入模型(main.go:28)。Wayland 下很多合成输入会被拒。

  • Windows install-* 不可用。 那些安装脚本是 POSIX shell,Windows 上会主动拒绝并提示手动配置 (build-packages.mjs:305-308)。

  • 打包依赖预先构建好的 dist 产物。 copyBundledRuntimesassertFileExists 每个 os-arch 的 可执行文件,缺一个就整包构建失败(build-packages.mjs:566-595)——三平台产物必须都先 build 出来。


10. 横向对比(同组其它章)


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

主题文件路径符号名
macOS 主入口 / 命令分发apps/OpenComputerUse/Sources/OpenComputerUse/OpenComputerUseMain.swiftOpenComputerUseMain.run
macOS serverInstructionspackages/OpenComputerUseKit/Sources/OpenComputerUseKit/MCPServer.swiftcomputerUseServerInstructions
是否走 App 代理(纯函数)packages/OpenComputerUseKit/Sources/OpenComputerUseKit/OpenComputerUseCLI.swiftshouldUseMacOSAppAgentProxy
App 代理:启动/连接/复用apps/OpenComputerUse/Sources/OpenComputerUse/MacOSAppAgentProxy.swiftMacOSAppAgentProxy.shouldProxy / connectOrLaunchAgent / isCurrentAgent
App 代理:socket 服务端apps/OpenComputerUse/Sources/OpenComputerUse/MacOSAppAgentProxy.swiftMacOSAppAgentRuntime / AppAgentConnection.handle
非代理 MCP + 光标外壳apps/OpenComputerUse/Sources/OpenComputerUse/MCPAppRuntime.swiftMCPAppRuntime.run
权限诊断(双通道)packages/OpenComputerUseKit/Sources/OpenComputerUseKit/Permissions.swiftPermissionDiagnostics.current / accessibilityTrusted / screenCaptureGranted
TCC.db 读取packages/OpenComputerUseKit/Sources/OpenComputerUseKit/Permissions.swiftTCCDatabase.authorization / currentPermissionClients
引导窗apps/OpenComputerUse/Sources/OpenComputerUse/PermissionOnboardingApp.swiftPermissionOnboardingApp.launch / present
macOS 应用发现(Spotlight)packages/OpenComputerUseKit/Sources/OpenComputerUseKit/AppDiscovery.swiftAppDiscovery
Linux Go 壳 / 嵌入 runtime.pyapps/OpenComputerUseLinux/main.goserverInstructions / runPython / toolDefinitions
Linux 会话发现apps/OpenComputerUseLinux/main.godesktopProcessEnvironments / desktopProcessRank / linuxRuntimeEnvironmentFrom / chooseRuntimeDir
Linux AT-SPI2 后端apps/OpenComputerUseLinux/runtime.pyAtspi.get_desktop
Windows Go 壳 / 嵌入 runtime.ps1apps/OpenComputerUseWindows/main.goserverInstructions / toolDefinitions
Windows UIA 后端apps/OpenComputerUseWindows/runtime.ps1AutomationElement.FromHandle
npm 打包器scripts/npm/build-packages.mjsrenderLauncher / runtimeTargets / copyInstallerScripts / renderMetaPackageJson
各 agent 安装脚本scripts/install-*.shinstall-codex-mcp.sh / install-claude-mcp.sh / install-gemini-mcp.sh / install-opencode-mcp.sh / install-codex-plugin.sh