airi 桌面操作通道(Desktop Lane)状态剖析:macOS 本机执行、Chrome 语义 DOM 与 Overlay 生命周期验证

发布时间:2026/10/15 2:53:08
airi 桌面操作通道(Desktop Lane)状态剖析:macOS 本机执行、Chrome 语义 DOM 与 Overlay 生命周期验证 人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手【免费下载链接】airi 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器目标是接近 Neuro-sama 的高度支持实时语音聊天、Minecraft 和 Factorio 游玩支持 Web / macOS / Windows。项目地址https://gitcode.com/moeru-ai/airi点击查看免费下载导读本文基于 airi 仓库中的状态备忘录 desktop-lane-status.md 展开系统梳理当前桌面操作通道desktop lane的既定方向、已经落地的代码基线、仍然存在的真实阻塞项以及「现在该做什么 / 以后该做什么」的优先级边界。读完本文你将掌握 airi 桌面自动化 v3 链路的完整形态desktop_ensure_chrome → desktop_observe → desktop_click_target → desktop_get_state、overlay 窗口的点击穿透与生命周期契约实现以及如何在本机重跑两条 smoke 验证通道是否仍然成立。桌面通道的既定方向四条不可动摇的基线备忘录开篇首先锁定的是方向层面的稳定性这四条约束决定了后续所有架构取舍macOS only桌面自动化目前只面向 macOS 主机Chrome-first浏览器场景优先通过 Chrome 语义 DOM 落地而非退化为纯截图 像素识别visual semantic tree OS input视觉截图/坐标、语义树AX/Chrome DOM、操作系统级输入注入三者并存、按需路由overlay 只是可视化层桌面 overlay 窗口只负责「展示」目标候选框、幽灵指针等视觉反馈绝不是第二个系统光标也不拦截任何真实输入。这四条方向在源码中可以直接验证macOS 本机执行器的执行目标被标记为mode: local-windowed、transport: local并在describe()中明确写出「desktop actions run on the current macOS host」「input injection uses Swift Quartz CGEvent」见 macos-local.tsoverlay 模块的头部注释则直接声明「The overlay is click-through (setIgnoreMouseEvents) so it never intercepts real user or OS-level click events」见 desktop-overlay/index.ts。已就绪的代码基线备忘录逐条对应的仓库证据备忘录声称以下基线「already exist in code」本文逐一在仓库中核实并给出精确位置。1. macOS 本机执行器与光标契约备忘录提到 macos-local.ts 实现了「保存真实光标位置并在操作后恢复」的save → act → restore模式。在当前仓库中这一契约以更细的粒度落地点击走轨迹点buildMacOSMoveAndClickScript()接收pointerTrace一组{x, y, delayMs}轨迹点逐点通过 QuartzCGEvent(mouseEventType: .mouseMoved)移动光标最后在终点按下/抬起鼠标事件点击后把真实光标留在目标上供后续指针相对操作使用macos-local.ts。键盘输入不碰光标buildMacOSTypeTextScript()与buildMacOSPressKeysScript()只投递键盘事件绝不移动鼠标。测试用断言expect(script).not.toContain(CGWarpMouseCursorPosition)固定了这一行为macos-local.test.ts。从源码结构看本机执行器已经从「瞬间 warp」演进出「带轨迹的逐点移动 键盘零光标副作用」的版本。窗口观察走 AppKitobserveWindowsScript()用NSWorkspace取最前台应用、CGWindowListCopyWindowInfo枚举屏幕上的窗口过滤透明/过小窗口后输出id / appName / title / bounds / ownerPid / layer并在内部做limit与app过滤macos-local.ts。键盘码映射keyCodeMap覆盖字母、数字、方向键与enter/tab/space/escape/delete等常用键修饰键通过modifierFlags映射到 Swift 的CGEventFlags表达式如command → .maskCommand、option → .maskAlternatemacos-local.ts。值得注意备忘录中提到的makeWindowPassThrough()在当前仓库中已被更完整的窗口契约函数替代见下一节这是 recut 分支演进后的命名变化。2. Overlay 窗口点击穿透 不可聚焦 内容保护备忘录提到的「ignore-mouse-events non-focusable overlay 行为」在 window-contract.ts 中被封装为applyDesktopOverlayInputIsolation()一次性施加四组隔离标志window.setIgnoreMouseEvents(true, { forward: true }) // 点击穿透但保留鼠标移动事件 window.setAlwaysOnTop(true, screen-saver) // 置于屏幕保护层级之上 window.setContentProtection(true) // 防止被截屏/录屏抓取 window.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })配套的createDesktopOverlayWindowOptions()还设置了transparent: true、frame: false、focusable: false、skipTaskbar: true、backgroundThrottling: false等选项window-contract.ts。这些标志的组合语义被 window-contract.test.ts 逐一断言属于无需启动 Electron 即可验证的单元级契约。同样的穿透思路也贯穿普通窗口聊天浮窗在 floating.ts 中对浮窗执行setIgnoreMouseEvents(true, { forward: true })渲染层则通过handleFadeOnHoverInteractionChange()根据光标位置、是否处于控件边界、Wayland 等条件动态切换穿透/交互状态index.vue。3. CDP 桥5 秒心跳 连续 3 次失败自动拆除备忘录提到的「5-second heartbeat with teardown after 3 consecutive failures」对应 cdp-bridge.ts 中的CdpBridge默认heartbeatIntervalMs 5000heartbeatFailureLimit 3cdp-bridge.ts每个心跳周期发送 WebSocketping若上次pong未归位则累加失败次数达到上限调用teardownAfterHeartbeatFailure()清空心跳定时器、reject 所有挂起请求、关闭 socket、把connected置为falsecdp-bridge.ts。该桥通过http://localhost:9222的 CDP 端点连接 Chrome启用Accessibility / DOM / Runtime三个域提供无障碍树快照、Runtime.evaluate、页面导航与截图能力——这正是「不依赖扩展也能读取语义 DOM」的直连通道cdp-bridge.ts。4. desktop v3 smoke验证 ensure / observe / click / state 整链备忘录提到的桌面 v3 smoke 是 smoke-chrome-grounding.ts它按 Phase 串起整条链路desktop_ensure_chrome启动或加入 agent Chrome 窗口并加载冒烟页desktop_observe抓取 grounding 快照includeChrome: truedesktop_get_state从runState.lastGroundingSnapshot提取候选目标要求source chrome_dom即必须经由扩展语义 DOM 捕获desktop_click_target对选定候选发起左键单击并校验backendResult.executionRoute以browser_dom开头smoke-chrome-grounding.ts。该脚本的用法写在文件头注释中pnpm -F proj-airi/computer-use-mcp smoke:desktop-v3并支持一组可覆盖的环境变量COMPUTER_USE_DESKTOP_V3_SMOKE_URL、COMPUTER_USE_DESKTOP_V3_SMOKE_CANDIDATE_ID、COMPUTER_USE_DESKTOP_V3_SMOKE_SETTLE_MS、COMPUTER_USE_SMOKE_EXECUTOR、COMPUTER_USE_SMOKE_APPROVAL_MODE默认 executor 为macos-local、approval 模式为actionssmoke-chrome-grounding.ts。配套的 smoke-chrome-grounding.test.ts 对候选选择、overlay 状态提取、路由证据等纯函数做了单元验证。5. Chrome 扩展桥与 iframe offset不再是假设备忘录强调「Chrome extension bridge and iframe offset work are no longer hypothetical」这在仓库中对应 extension-bridge.ts 的BrowserDomExtensionBridge它是一个 WebSocket 服务端桥默认127.0.0.1 配置端口接收扩展侧客户端连接并用SUPPORTED_ACTIONS白名单限定可执行动作getActiveTab / getAllFrames / readAllFramesDOM / findElement / findElements / getClickTarget / getElementAttributes / readInputValue / getComputedStyles / waitForElementextension-bridge.ts每个请求带randomUUID有独立超时requestTimeoutMs断连时会 reject 全部挂起请求——这是「fail-closed」的传输层基础extension-bridge.tsextension-bridge.test.ts 使用真实WebSocketServer验证握手hello、动作白名单与端口占用时的行为。至于 iframe DOM 候选的 frame offset 传播它保证「iframe 内元素的语义候选坐标」能正确换算到屏幕坐标系——这正是 overlay 能把框画到正确位置的前提。6. browser-dom 路由契约fail-closed备忘录明确非左键点击与多击留在 OS 输入且BrowserDomExtensionBridge对ok: false的响应按失败处理不再当作成功。这意味着当语义 DOM 通道无法可靠完成一次「非左键 / 多击」操作时系统会退回 OS 级输入注入而不是假装成功桥返回错误状态时挂起请求被 reject调用方收到异常而不是静默吞掉。仍然真实的阻塞项按严重度排序备忘录认为方向本身没有问题真正阻塞的是「少量正确性问题 一个尚未闭合的 overlay 生命周期验证步骤」按严重度排列如下。阻塞 1live v3 smoke 只是基线覆盖不是产品支持当前 smoke-chrome-grounding.ts 能证明的是整条工具链可运行desktop_ensure_chrome → desktop_observe → desktop_click_target → desktop_get_state。但它没有证明三件事Chrome 语义 DOM 路由chrome_dom候选 →browser_dom执行在复杂页面上始终正确真实 overlay 窗口的实时渲染幽灵指针、候选框、置信度徽标用户输入的隔离overlay 不拦截、不抢占焦点、OS 输入不被误触。也就是说smoke 是「通道可用性」证据不是「产品级支持」证据。阻塞 2overlay 生命周期 / RPC readiness 需要一次 live-window 复查备忘录列出的相关文件在仓库中对应 rpc/contracts.ts 与 rpc/index.electron.tsreadiness 契约定义在 shared/eventa/index.tsDesktopOverlayReadiness { state: booting | ready | degraded, error?: string }invoke 事件名为eventa:invoke:electron:windows:desktop-overlay:get-readiness主进程在setupDesktopOverlayElectronInvokes()中先注册 readiness 处理器再搭建基础窗口服务与 MCP 服务全部成功则readiness { state: ready }任何异常则置为degraded并携带错误信息且故意不抛出让窗口照常打开、渲染层通过轮询发现降级rpc/index.electron.ts一个关键顺序约束必须在加载渲染页之前完成 RPC 注册。因为 overlay 页面的onMounted会立刻通过callTool开始轮询若 handler 尚未注册首次 eventa invoke 会永久挂起后续轮询全部停摆desktop-overlay/index.ts。渲染侧的轮询控制逻辑位于 desktop-overlay-polling.ts它先bootstrapPoll()等待 readinessready后才进入正式轮询若为booting/degraded则按fallbackIntervalMs默认 500ms重试desktop-overlay-polling.ts。轮询本身还有三组防御每次callTool包一层超时默认 5000ms防止启动竞态下永挂callTimeoutMs超时的调用被记为「hung call」最多跟踪 2 个全挂时只允许每 10 秒发一次低频恢复探针避免 IPC 压力无限增长MAX_BACKGROUND_HUNG_CALLS 2、HUNG_CALL_RECOVERY_INTERVAL_MS 10_000成功提取到runState后通过extractOverlayState()产出hasSnapshot / snapshotId / candidates / staleFlags / pointerIntent作为「overlay 该显示什么」的唯一数据源desktop-overlay-polling.ts。这些纯逻辑在 desktop-overlay-polling.test.ts 中有完整覆盖状态提取、超时恢复、hung 槽位上限、readiness 等待与降级上报。因此阻塞 2 剩下的只是「在真实 Electron overlay 窗口里再做一次新鲜的运行确认」——属于运行期证明缺口而非设计缺口。阻塞 3本地 overlay live-window smoke 已接好但还需要在本分支跑一次备忘录提到的 smoke 已真实存在于 package.jsonsmoke:desktop-overlay-live-window: NODE_OPTIONS--experimental-websocket tsx scripts/desktop-overlay-live-window-smoke.tsdesktop-overlay-live-window-smoke.ts 的流程相当完整前置检查确认 Node 运行时带WebSocket否则提示用NODE_OPTIONS--experimental-websocket并校验三个 workspace 构建产物存在packages/electron-screen-capture/dist/main.mjs、packages/electron-vueuse/dist/main/index.mjs、packages/server-runtime/dist/server.mjs缺失时给出明确构建建议而非自动构建desktop-overlay-live-window-smoke.ts准备临时mcp.json以COMPUTER_USE_EXECUTORmacos-local、COMPUTER_USE_APPROVAL_MODEnever启动 computer-use MCP 服务器desktop-overlay-live-window-smoke.ts以APP_REMOTE_DEBUGtrue、AIRI_DESKTOP_OVERLAY1、AIRI_DESKTOP_OVERLAY_POLL_HEARTBEAT1启动 stage-tamagotchi dev通过 CDP 找到#/desktop-overlay页面目标desktop-overlay-live-window-smoke.ts等待页面挂载__AIRI_DESKTOP_OVERLAY_SMOKE__桥随后走一遍 MCP 工具链desktop_ensure_chrome→desktop_observe→desktop_get_state→desktop_click_target→desktop_get_state校验lastPointerIntent.candidateId与点击候选一致desktop-overlay-live-window-smoke.ts最后抓取渲染层打印的轮询心跳日志确认出现snapshotId... pointerIntentyes的行——这直接证明「overlay 渲染层确实在轮询 MCP 并拿到可消费状态」desktop-overlay-live-window-smoke.ts。心跳日志由 desktop-overlay-heartbeat.ts 定义标记[AIRI_DESKTOP_OVERLAY_POLL_HEARTBEAT]与查询参数pollHeartbeat是纯调试用途。候选选择辅助函数selectDesktopOverlaySmokeCandidateId()要求source chrome_dom且 label 包含AIRI Desktop Overlay Smoke Button在 desktop-overlay-live-window-smoke.ts 中独立成纯函数并有单元测试。阻塞 3 的本质与阻塞 2 相同候选选择逻辑已单测但「心跳标记与 MCP 轮询在真实 Electron overlay 窗口中的表现是否与旧分支一致」仍需一次本机运行来确认。什么不是当前阻塞避免把打磨混进主线备忘录明确圈出四类「真实但不应阻塞当前」的清理项防止范围蔓延eager overlay init 的整洁性overlay 窗口由 main/index.ts 通过 injeca 容器以windows:desktop-overlay提供并依赖一次「单独 invoke」保证急切构建。这段接线可用但不够优雅属于清理而非阻塞。嵌套 browser-dom 路由逻辑的可读性重构纯代码组织问题。把 macos-local.ts 改成 instant-warp-only 回退即放弃运动轨迹、只做瞬间跳转、零运动痕迹。这是一项未来 UX 决策涉及幽灵指针动画由谁承担不是当前正确性问题。overlay 视觉重写 / ghost pointer 打磨 / 额外渲染调试 UI明确归为「polish」禁止与正确性修复混在同一 PR。如何解读外部评审意见m13v comments备忘录给出了一条实用的评审拆解方法评审意见要按「是否与当前代码对齐」与「是否值得未来打磨」分桶不能一概而论。与当前代码已对齐的部分说明评审看懂了平台约束save → act → restore光标模式、overlay 不得拦截用户输入、CDP 会话崩溃后的心跳拆除。这三条在仓库中分别对应 macos-local.ts 的指针轨迹/零键盘光标副作用、window-contract.ts 的输入隔离、cdp-bridge.ts 的心跳拆除。仍可作为未来打磨方向的部分减少原生运动轨迹、让 UI 承担更多可见指针动画围绕会话生命周期建立更深的运行期纪律。结论是m13v 的运行时建议是好的但「每条建议都等于当前阻塞」的推断不成立。现在该做什么 / 以后该做什么备忘录给出的行动清单清晰且克制现在阻塞未清零前扩展「未知动作」契约无需处理——已经是 fail-closed保持 browser-dom 路由对非左键点击与桥错误的 fail-closed 行为维持「被覆盖」但暂不升级为「产品支持」在本 recut 分支上重跑本地 overlay live-window smokepnpm -F proj-airi/stage-tamagotchi smoke:desktop-overlay-live-window把运行期证明从「旧分支」刷新为「本分支」。之后仅当上述干净后可选跟进fix(stage-tamagotchi): validate desktop overlay lifecycle and RPC readiness in live window context可选跟进refactor(computer-use-mcp): evaluate instant-warp-only macOS fallback against ghost-pointer UX可选跟进强化高度相似的兄弟 iframe 之间的锚点匹配。底线方向没有被阻塞备忘录的最终判断值得原样保留桌面通道没有被方向问题阻塞只被「少量正确性问题 一个仍未闭合的 overlay 生命周期验证步骤」阻塞。因此接下来的纪律是三条不要重新打开架构方向已稳定不要混入打磨polish 归 polish不要往同一个 PR 上继续堆积无关改动。这正是把「桌面操作通道」从基线覆盖推向产品支持的路径先闭合运行期证明缺口再谈路由强化与视觉打磨。赞分享人工智能AI AgentMCP 服务工具调用GUI 自动化语音数字人交互助手【免费下载链接】airi 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器目标是接近 Neuro-sama 的高度支持实时语音聊天、Minecraft 和 Factorio 游玩支持 Web / macOS / Windows。项目地址https://gitcode.com/moeru-ai/airi点击查看免费下载相关推荐Parsley.js表单验证状态机状态转换与生命周期管理Parsley.js表单验证状态机状态转换与生命周期管理 Parsley.js作为前端表单验证库通过状态机模式实现了复杂的表单验证逻辑。本文深入解析其状态管前端Velero Backup API 类型完全指南字段语义、生命周期状态与备份执行机制Velero Backup API 类型完全指南字段语义、生命周期状态与备份执行机制 导读 本文围绕 Velero及其前身 Ark核心 API 类型 Ba云原生灾备存储后端DLSS Swapper 一键替换 DLSS 版本DLSS Swapper 一键替换 DLSS 版本 装好 DLSS Swapper它会扫描 Steam、GOG、Epic、Battle.net 的游戏库把每桌面应用上一篇终极ncmdump指南3步快速解密网易云NCM格式音频文件下一篇打破音乐格式枷锁ncmdump让网易云NCM文件重获自由创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询