为什么phone-harness不抢你的屏幕:iPhone Mirroring后台输入与SkyLight私有API揭秘
2026/8/26 15:02:05 网站建设 项目流程

为什么phone-harness不抢你的屏幕:iPhone Mirroring后台输入与SkyLight私有API揭秘

【免费下载链接】phone-harnesslet your agent control your phone项目地址: https://gitcode.com/gh_mirrors/ph/phone-harness

phone-harness 是一个让 AI Agent 直接操控你 iPhone 的开源工具:它把 macOS 的 iPhone Mirroring 镜像窗口当作唯一"传输层",用系统截图 + Vision OCR 做眼睛,用 SkyLight 私有 API 投递后台输入事件做双手。全程无需越狱、无需 Xcode、无需 WebDriverAgent——而本文要揭秘的重点是:Agent 干活时为什么不会把镜像窗口抢到你眼前,你的焦点和屏幕始终留在你手里。

传统自动化为何总"抢屏幕":先看懂镜像窗口的真相

iPhone Mirroring(macOS Sequoia+)把手机渲染成 Mac 上的一个窗口,并把鼠标键盘输入转发为触摸。但这个项目踩过不少坑,总结出几条决定性的事实:

  • 窗口内部是一段视频流:macOS 辅助功能在里面什么都看不到,AppleScript 的click at会被静默忽略;
  • 只有HID 层级的 CGEvent能驱动它,且窗口必须处于最前,否则事件直接被系统吞掉;
  • 镜像只转发原始 HID 键码并丢弃修饰键标志位——所以Cmd+V发进 iOS 文本框,收到的只是一个光秃秃的v

于是经典后端(src/phone_harness/mirror.py)的做法是:每次动作前把镜像窗口activate()到最前,再screencapture截图、CGEventPost注入事件。副作用显而易见——整个任务期间你的屏幕被"占用"了。

不抢屏幕的秘诀:SkyLight 私有 API 事件记录

真正的答案藏在src/phone_harness/background.py。macOS 窗口服务背后有一个未公开的内部框架SkyLight,它接受一种"合成事件记录"(event record),可以直接投递给指定进程、指定窗口——这正是知名窗口管理工具 yabai 实现"聚焦但不置顶"所用的机制。

phone-harness 三步走:

步骤做了什么关键符号
1由镜像应用的 PID 换出进程句柄 ProcessSerialNumberGetProcessForPID
2向窗口服务声明该进程+窗口的操作来自用户_SLPSSetFrontProcessWithOptions
3把带真实坐标的鼠标事件记录直投给该进程SLPSPostEventRecordTo

事件记录是一个 0xf8 字节的固定布局缓冲区,字段含义如下:

偏移内容
0x04记录长度(0xf8)
0x08CGSEventType:1=按下,2=抬起,6=拖动
0x10全局坐标(屏幕点)
0x20窗口内局部坐标
0x3c目标窗口 ID

yabai 会把坐标置空来表示"仅聚焦";phone-harness 则写入真实坐标,同一条记录就变成了一次"带位置的点击"——落在 iPhone Mirroring 窗口里,而你最前面的应用全程纹丝不动。所有写入都限制在缓冲区之内,即使某个值写错也只是无效操作,不会崩掉窗口服务。

键盘输入走另一条小路:先用 yabai 风格的 "make-key" 记录(坐标置空的按下+抬起对)让窗口成为 key window,再经CGEventPostToPid把按键事件按 PID 直投进程——输入文字时焦点同样不切换。

后台"眼睛":不激活窗口也能截图

输入解决了,视觉呢?background.pyCGWindowListCreateImage按窗口 ID抓取镜像窗口:即使窗口不活动、甚至被其他窗口遮挡,也能拿到画面;取不到时退回screencapture -l兜底。

截图随后交给 Apple 的 Vision 框架做 OCR(src/phone_harness/ocr.py):每一段可见文字都带一个可直接点击的屏幕坐标,作者称之为"穷人的 DOM"。Agent 的典型闭环只有一句话:

OCR 找到目标文字坐标 → tap 点下去 → 再截图验证 → 完成。

滚动列表也有讲究:滚轮事件只路由给聚焦中的窗口(后台实测 0% 位移),慢速触摸拖动在 iOS 列表上几乎不动,于是后台后端改用快速惯性 flick模拟手指甩动,实测每次可滚动约一屏的 29%。

两条输入路径,一个环境变量开关

src/phone_harness/ios.py里有一行小开关决定走哪条路:

  • 默认(后台模式):鼠标、滚动全程不抢焦点;键盘也尽量"make-key + 按 PID 投递";
  • PHONE_HARNESS_BACKGROUND=0(经典模式):每次动作前激活窗口,依赖更少的私有机制,兼容性更好;
  • 后台模式依赖 SkyLight 私有符号,跨 macOS 版本不做保证——加载失败时自动回落经典后端,保证工具始终可用。

快速上手:clone 仓库到 --doctor 体检

1️⃣ 克隆并安装(需 Python 3.12+ 与 pyobjc):

git clone https://gitcode.com/gh_mirrors/ph/phone-harness ~/.phone-harness cd ~/.phone-harness pip install pyobjc-framework-Quartz pyobjc-framework-Vision pyobjc-framework-Cocoa pip install -e . --no-deps

2️⃣ 在"系统设置 > 隐私与安全性"给终端授予辅助功能(点击/按键)与屏幕录制(看屏幕)权限,细节见仓库的install.md

3️⃣ 运行phone-harness --doctor,它会沿"权限天梯"逐项检查:pyobjc → 辅助功能 → 屏幕录制 → 镜像应用已装 → 正在运行 → 找到窗口 → 截图成功 → OCR 成功(逻辑在src/phone_harness/admin.py),第一个 FAIL 就是你要修的。

边界与常见疑问

  • 手机解锁 = 会话暂停("iPhone in Use"):恢复镜像是物理动作,工具只提示,不代点 Connect;
  • 无多点触控、无相机/Face ID 流程,DRM 视频渲染为黑屏,一机一会话;
  • OCR 看到的是文字而非语义:无文字标签的图标需要视觉模型看截图再算坐标;
  • 日常用法看SKILL.md,Agent 可自由扩展的辅助函数在agent-workspace/agent_helpers.py

小结

不抢屏幕的 phone-harness,本质只做了两件事:眼睛用"按窗口 ID 截图",双手用"SkyLight 事件记录直投进程"。私有 API 自带不稳定风险,但项目用一个优雅的回退策略兜住了它——默认后台、失败回落经典路径。想让 AI Agent 安静、不打扰地操控真机 iPhone,这套"眼睛 + 双手"的设计就是最值得抄的作业。

【免费下载链接】phone-harnesslet your agent control your phone项目地址: https://gitcode.com/gh_mirrors/ph/phone-harness

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询