它做什么
dsh-ios 把一块真实的 iOS 模拟器放进对话里——并把画面交给你。Agent 可以启动设备、构建并运行 Xcode 工程或 Swift 包、按无障碍标识或 OCR 文本驱动界面、读取统一日志、检查进程、调用栈与内存泄漏。你在一个常驻侧边面板里实时看着这台设备,还能直接在视频上点按、拖拽、旋转、按 Home。
同一套动作也能作用于 USB 连接的真机 iPhone:插件在手机上构建并启动 WebDriverAgent,把它的控制端口与画面端口经回环隧道转发,并把设备投进同一个面板、同一批卡片与工具。
开头就值得注意的设计决定:没有图像 block,也没有录屏文件。视觉字节只通过 DSH webserver 提供的、带签名且会过期的 URL 到达界面。
| 能力 | README 记录的内容 |
|---|---|
| 实时模拟器 | serve-sim 的 MJPEG 流经带签名的 /_dsh/dsh-ios/* 路由代理进右侧面板——浏览器从不接触 serve-sim 的端口 |
| USB 真机 | ios_real_start_wda 构建并启动 WebDriverAgent,把控制(REST)与画面(MJPEG)经回环隧道转发;设备必须解锁,且每次真机点按都受「先识别再点按」规则约束 |
| 22 个 Agent 工具 | 设备、启动/关机、截图、交互、构建并运行、统一日志、AXe 支撑的 UI 树与按元素点按、列表/信息流行操作、Vision OCR 查找/点按、SwiftUI 预览热重载、进程、调用栈、内存泄漏、应用信息 |
| 可交互面板 | 直接在视频上点按与拖拽;Home / 旋转 / 截图 / 刷新 图标工具栏;尺寸模式(适应 · 50–125% · S/M/L);边框样式(无框 / 边框 / 真机框);拖拽调整到 960 px、双击复位;横屏自动加宽 |
| 列表与信息流 | ios_sim_ui_rows 把深层无障碍快照转成带索引的行;ios_sim_tap_row 在行内按相对坐标点按,并以计数器预期的 ±1 变化确认动作 |
| SwiftUI 预览 | ios_sim_preview 在包外生成一次性宿主 App,把你的预览构建成 dylib,并把改动热替换进正在运行的模拟器(约 2–5 秒),无需重启 |
Agent 如何驱动界面
三层,按优先级排列,README 对各自适用场景说得很清楚:
- 按标识。
ios_sim_ui_tree导出前台 App 的无障碍元素树(label、identifier、value、以设备点为单位的 frame);ios_sim_tap_element先精确匹配、再对identifier/label做大小写不敏感的子串匹配,嵌套重复会折叠成一个目标,歧义匹配会列出全部候选。 - 按文本。 当元素树为空或退化时——无无障碍支持的 App、徽标计数、烘进图片里的价格——
ios_sim_find_text用插件自带的assets/ocr.swift编译出的 Vision helper 对当前屏幕做 OCR,ios_sim_tap_text点按最佳匹配。ios_sim_wait_for轮询同一管线直到文本出现或消失,超时返回matched: false而不是报错。 - 按行。
ios_sim_ui_rows与ios_sim_tap_row之所以存在,是因为列表类 App 唯一可靠的确认就是计数器变化了 1。
输出是被刻意限界的:UI 树约 40 KB 封顶并设置 truncated、裁掉最深层级;OCR 约 40 KB 封顶、丢掉置信度最低的尾部;日志约 300 行 / 30 KB;调用栈约 200 行。每个上限都附带上收窄范围的提示,而不是静默截断。
调试工具以只读为主,并如实说明降级。 ios_sim_backtrace 是一次性批量 LLDB(attach → 调用栈 → detach,绝不进入交互模式),在 macOS 未开启开发者模式时降级为 Xcode 的非挂起 sample 引擎,ios_sim_leaks 则给出开启提示。两者都会带理由拒绝真机,并且始终确认目标已恢复运行。
要求与安装
-
macOS 且安装完整 Xcode——不是只有 Command Line Tools。
xcodebuild、xcrun simctl与模拟器运行时都由 Xcode 提供,且必须至少安装一个 iOS 模拟器运行时。 -
Node
>=24.11.0。 -
DSH ≥ 0.1.0-rc.6 且带 web bundle(面板所需)。这条下限即本条目记录的
dshTarget,取自 README 的要求章节;发布说明同时注明插件已针对0.1.5-rc.1测试。无头 profile 也能用——22 个工具全部正常,只是没有实时画面。本条目为 L3。 -
非 macOS 宿主会加载插件并注册全部 22 个工具,但每次调用都返回解释性错误。 唯一例外是
ios_sim_preview的status,它会如实返回{ running: false }。 -
安装:
dsh plugin --profile web add @zseven-w/dsh-ios@latest -
serve-sim作为依赖随包提供(^0.1.45),真实安装时本地解析;npx -y serve-sim兜底覆盖开发树,首次使用需要联网。这是插件唯一的运行时依赖。 -
可选组件,每一项都以安装提示降级、而不是静默失败:
- AXe(UI 树与行操作工具需要)——
brew install cameroncooke/axe/axe,或让插件下载钉死版本(v1.8.0,SHA-256 校验)到~/Library/Caches/dsh-ios/bin。DSHPLUGIN_IOS_AXE_BIN可覆盖解析路径,DSHPLUGIN_IOS_AXE_OFFLINE=1可禁用下载。 - Vision OCR——首次使用时用
swiftc从自带的 Swift 源码编译到~/Library/Caches/dsh-ios/bin/ocr(zh-Hans + en-US)。 - lldb attach 需要开发者模式:执行一次
sudo DevToolsSecurity -enable。
- AXe(UI 树与行操作工具需要)——
-
真机 iPhone 还需要: 支持数据的 USB 线(仅 Wi-Fi 配对的连接无法承载端口转发)、解锁的屏幕、设备上开启开发者模式,以及一份放在
~/Library/Caches/dsh-ios/wda/src的 WebDriverAgent 检出——插件从那里构建,从不下载或克隆任何东西。首次构建会安装一个已签名的WebDriverAgentRunner;设备提示时请信任其证书;免费团队签名档期(7 天)到期后重新运行ios_real_start_wda。签名团队的选择顺序有文档,DSHPLUGIN_IOS_TEAM_ID优先。 -
无安装期生命周期脚本。 声明的脚本是
build与typecheck,外加一大批dev-*-smoke.mjs测试。
网络与数据
传输在构造上就是回环限定,README 把这道围栏写得很细:
- 浏览器从不接触
serve-sim的端口。每一个字节都经插件自有路由跨过 DSH webserver 源:/stream/(MJPEG 代理)、/screenshot/(缓存 PNG)、/ws?token=…(HID 控制中继),另有/grant、/capture与/status。 - 令牌是 HMAC-SHA256 capability(
base64url(payload).base64url(mac)),10 分钟内过期,用每 DSH home 一把密钥签名(/cache/dsh-ios/stream-access.key,权限 0600,原子创建)。 - 每条路由在查任何 capability 之前先过传输围栏:回环对端地址、回环
Host(拒绝 DNS rebinding)、Fetch-Metadata/Origin 检查。截图路由只提供插件缓存目录内的文件,拒绝符号链接并做realpath包含校验。 serve-sim以前台子进程运行且只绑回环,使用专用端口段(3181–3244),因此你自己跑在 3100 端口的serve-sim不会被碰到,且从不使用--host。- 真机场景下,WebDriverAgent 控制端口(设备 8100)与画面端口(设备 9100)的隧道是经 USB 链路的回环 usbmux 转发,位于同一套带签名路由围栏之后。
生命周期是被管理的,不是听天由命。 如果上一个 DSH 宿主被非正常杀掉而其 helper 存活,同一台设备会被接管(孤儿进程的握手是权威);占着另一台设备槽位的陈旧 helper 会被回收。崩溃的流约 5 秒后在后台重启;零消费者时 5 分钟后自动停止。真机运行器被刻意豁免空闲回收,因为重启它要花一次数分钟的 xcodebuild。
未记录遥测。 存在一次可选外呼且可关闭:钉死的 AXe 下载。本条目为 security: unverified——插件自己的安全章节是设计说明,不是外部审计。
已知限制
- macOS + 完整 Xcode 是硬性要求。 没有 Linux 或 Windows 路径;在那些宿主上工具会注册然后拒绝执行。
- iOS 26.2 模拟器可能让
ios_sim_leaks失效。 即使开启开发者模式,Xcode 的leaks也可能报Failed to get DYLD info或 minimal-corpse 之类错误。README 明确说没有插件侧修法,建议改用mode: "memgraph"或换运行时。 - 真机自动化有 7 天的时钟。 免费团队签名档期会过期,
profile-expired就是面板会报告的编码状态之一,处置办法是重跑ios_real_start_wda。 - 真机点按是真动作。 README 明确写道,真机上的每一次点按都有真实后果,绝不要为了弄清某个未识别控件的作用而去点它。
- 手机上深层 UI 快照代价很高——README 实测繁忙 App 的不限深快照约 32 秒 / 751 KB,而限深约 2 秒;真机上每加深一层约 15–25 秒。深度上限是性能特性,不是可以随手去掉的限制。
- AXe 与 OCR helper 是插件之外额外的安装(或一次网络下载)。没有它们,UI 树与 OCR 两族工具不可用。
- 一次完整的
ios_sim_build_run要几分钟,WebDriverAgent 冷构建同样。两者都不是快速循环。 - Star(292)代表关注度,不代表审查。 本站没有条目声称 L4/L5,本条目也不表示已在本机做过冒烟安装。
何时用 / 何时不用
适合在 Mac 上做 iOS 开发、并且想让 Agent 真正操作模拟器而不是告诉你该点哪里的人:构建并启动、按无障碍标识或 OCR 驱动、读日志、挂调试器,并在一个你也能亲手触摸的面板里看着这一切。SwiftUI 预览热重载与行操作工具是两处最不容易在别处获得的能力。如果你在做必须验证界面而不是假设界面的 Agent 工作流,「先识别再点按」的纪律就是正确的地基。
不要指望它做 Android 或 Web 自动化(同一作者另有 dsh-android)、不要指望它当无头 CI 设备农场,也不要指望在没有 Xcode 和数据线的情况下自动化 iPhone。并且不要在没装完整 Xcode 的机器上运行它——工具会加载然后拒绝,那比不装更糟。
FAQ
没有 Xcode 能用吗?
不能。需要 macOS 加完整 Xcode(不只是 Command Line Tools),因为 xcodebuild、xcrun simctl 与模拟器运行时都来自它。在非 macOS 宿主上插件会加载、22 个工具都会注册,但每次调用都返回说明该要求的错误。
Agent 能不用猜坐标就点按吗?
能,这正是工具拆分的意义。ios_sim_tap_element 按无障碍 label 或 identifier 点按,ios_sim_tap_text 在无障碍树看不到时点按 OCR 匹配到的文本,ios_sim_tap_row 在列表行内点按并以计数器预期的 ±1 变化验证。
浏览器会直连模拟器流吗?
不会。流经 DSH webserver 源在带签名、HMAC 会过期的路由上代理,并位于回环对端、回环 Host 与 Fetch-Metadata/Origin 围栏之后。README 详细记录了路由清单、10 分钟 capability 过期与截图路径包含校验。
它会自己下载东西吗?
只有一次且可选:钉死的 AXe 版本(v1.8.0,SHA-256 校验),可用 DSHPLUGIN_IOS_AXE_OFFLINE=1 禁用,或换成你自己的二进制。插件从不克隆 WebDriverAgent——它从你提供的 ~/Library/Caches/dsh-ios/wda/src 检出构建。
为什么我没看的时候流停了?
那是空闲策略:零消费者时——面板关闭、没有卡片挂载、没有活跃路由——流会在 5 分钟后停止,并在下次工具调用或打开面板时重启。崩溃的流约 5 秒后在后台重启。真机运行器被豁免,因为重建 WebDriverAgent 要几分钟。
