关于 serve-sim
serve-sim 是一项指导 Agent 通过 serve-sim CLI 操作 Apple Simulator 的技能,涵盖模拟器控制、视频流、摄像头输入、权限与无障碍工作流。
主要功能
- 01点击、滑动、拖动并旋转模拟器
- 02向浏览器传输模拟器画面
- 03注入文件或摄像头画面
- 04管理应用权限与调试叠层
- 05读取无障碍树并模拟内存警告
适用场景
- 01自动操作已启动的模拟器应用
- 02分享模拟器画面以供审核
- 03测试依赖摄像头的应用行为
- 04检查无障碍界面元素与权限
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ expo/ serve-sim --skill serve-sim安装后在 Claude Code 中输入/serve-sim确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdevals/evals.jsonreferences/buttons-rotation.mdca-debug.mdcamera.mdendpoints.mdgestures.mdpermissions.mdworkflows.mdscripts/check-prereqs.shensure-running.sh详细介绍
serve-sim
使用 serve-sim CLI,通过 Agent 操控 Apple 模拟器(iOS、iPad、Apple Watch)。serve-sim 通过进程内原生插件捕获模拟器的 IOSurface,提供 HTTP 或 WebRTC 视频流及 WebSocket 输入,并提供 React 预览界面。本指南将向 Agent 介绍准确的 CLI 接口、手势 JSON 格式、注意事项和推荐工作流。
适用场景
- 用户希望 Agent 对正在运行的 Apple 模拟器执行点按、滑动、拖动、捏合或发送硬件按键。
- 用户希望将模拟器串流到浏览器(本地、局域网或通过隧道),以供查看或远程控制。
- 用户希望将合成摄像头画面(文件、网络摄像头或动画占位内容)注入模拟器上的指定应用。
- 用户希望为性能相关工作切换 CoreAnimation 调试叠加层(离屏渲染、混合图层、慢速动画)。
- 用户希望以编程方式模拟内存警告或旋转设备。
- 用户希望读取模拟器的无障碍树,无需逐像素查找即可定位 UI 元素。
- 用户希望授予、撤销或重置应用的隐私权限——摄像头、照片、位置、联系人或推送通知。
不适用场景
- Android 模拟器 → 使用
adb shell工具。 - 构建或安装 iOS 应用 → 使用
xcodebuild或xcrun simctl install。 - React Native 应用内运行时调试(Redux 状态、网络检查、组件树)→ 使用 rn-debugger 工具。
- 真实 iOS 硬件设备 → 使用
xcrun devicectl或 Xcode。
前置条件
执行任何其他操作前,请确认主机满足以下条件。如果缺少任何条件,请明确告知用户需要安装什么——不要继续操作。
| 要求 | 检查命令 | 原因 |
|---|---|---|
| macOS 主机 | uname -s 返回 Darwin | serve-sim 只能在 macOS 上运行 |
| Apple 芯片 | uname -m 返回 arm64 | 捆绑的原生组件仅支持 arm64 |
| Xcode CLI 工具 | xcrun --version 退出码为 0 | simctl 是底层模拟器驱动程序 |
| Node.js ≥20 | node --version ≥20 | serve-sim 支持处于维护期的 Node.js LTS 版本 |
| macOS 14+(可选) | sw_vers -productVersion ≥14 | 仅 camera 子命令需要 |
提供了捆绑的辅助脚本:scripts/check-prereqs.sh。运行该脚本;如果退出码非零,请将提示信息告知用户。
大多数子命令都需要已启动的模拟器。使用 xcrun simctl list devices booted 检查。如果没有已启动的模拟器,请告知用户打开 Xcode → Simulator,或运行 xcrun simctl boot <UDID>。
概念模型
┌───────────────┐ IOSurface ┌────────────────────┐ HTTP/WebRTC ┌─────────┐
│ iOS Simulator │ ──────────► │ serve-sim process │ ────────────► │ Browser │
└───────────────┘ │ + native N-API │ WebSocket └─────────┘
└────────────────────┘
▲
state files in
$TMPDIR/serve-sim/
Agent 必须遵守的关键约束:
- 所有坐标都归一化到 0..1,以显示区域左上角为
(0, 0),右下角为(1, 1)。切勿传入像素坐标。 - 每台设备只能注册一个服务。一个服务进程可以捕获多个模拟器,并且捕获 API 本身不具有排他性。
- 状态保存在
$TMPDIR/serve-sim/server-{udid}.json中。使用serve-sim --list查询;除非你清楚自己在做什么,否则不要直接读取 JSON。 - 通过
rotate设置的方向会由辅助程序记住,后续手势会在客户端侧旋转。旋转后发送原始坐标的 Agent 无需手动进行补偿。
常见操作
| 目标 | 命令 | 说明 |
|---|---|---|
| 启动预览服务 | npx @expo/serve-sim [device] | 默认预览地址为 http://localhost:3200;辅助路由与其同源。以前台进程运行。 |
| 启动 WebRTC 预览 | npx @expo/serve-sim --transport webrtc [device] | 使用 WebRTC 媒体和用于输入的有序辅助 WebSocket。 |
| 无头模式/守护进程启动 | npx @expo/serve-sim --detach [device] | 返回包含 pid、port、url 的 JSON。适用于 Agent 循环。 |
| 在宿主的预览中显示流 | npx @expo/serve-sim --detach -q → 将 url 交给宿主预览工具 | 请参阅“在 Agent 的预览中显示流”一节。 |
| 列出正在运行的流 | npx @expo/serve-sim --list | 添加 -q 以仅输出 JSON。 |
| 停止所有辅助进程 | npx @expo/serve-sim --kill | 传入 [device] 可停止特定设备的辅助进程。 |
| 单次轻点 | npx @expo/serve-sim tap <x> <y> | <x> <y> 的取值范围为 0..1。**普通轻点请使用此命令,而非 gesture。**请参阅下文的“关键注意事项”。 |
| 多步手势 | npx @expo/serve-sim gesture '<json>' | 请参阅 references/gestures.md。 |
| 硬件按钮 | npx @expo/serve-sim button <name> | 名称:home、swipe_home、app_switcher、lock、siri、side_button。请参阅 references/buttons-rotation.md。 |
| 旋转设备 | npx @expo/serve-sim rotate <orientation> | portrait、portrait_upside_down、landscape_left、landscape_right。 |
| 模拟内存警告 | npx @expo/serve-sim memory-warning | 等同于“调试”→“模拟内存警告”。 |
| CoreAnimation 调试 | npx @expo/serve-sim ca-debug <option> <on|off> | 选项:blended、copies、misaligned、offscreen、slow-animations。请参阅 references/ca-debug.md。 |
| 注入摄像头画面 | npx @expo/serve-sim camera <bundle-id> [--file <path|--webcam [name]] | 使用附加的摄像头动态库重新启动应用。仅限 macOS 14 及更高版本。请参阅 references/camera.md。 |
| 热切换摄像头源 | npx @expo/serve-sim camera switch <placeholder|webcam|file> [arg] | 不会重新启动应用。 |
| 管理应用权限 | npx @expo/serve-sim permissions <grant|revoke|reset|list> <permission> <bundle-id> | 摄像头、照片、位置、推送通知、联系人等。请参阅 references/permissions.md。 |
| 读取无障碍树 | 从 streamUrl 中的辅助服务基地址推导 /ax | 返回 axe 风格的 JSON。请参阅 references/endpoints.md。 |
多个模拟器已启动时,大多数子命令都支持 -d <udid|name>,用于指定目标设备。
关键注意事项:轻点优先使用 tap,而非 gesture
每次调用 serve-sim gesture 都会打开各自的 WebSocket。如果连续调用两次 gesture——一次使用 {"type":"begin",...},另一次使用 {"type":"end",...}——模拟器收到两者时会有足够的延迟,导致触摸被识别为长按,而非轻点。这是协议有意设定的限制,并非需要绕过的错误。
规则:对于任何单次轻点,请使用 serve-sim tap <x> <y>。只有在拖动、滑动或多步交互中必须在 begin → move × N → end 之间共用同一个套接字时,才使用 gesture。
指定目标设备
多个模拟器已启动时,每个子命令都支持 -d <udid|name>。名称匹配不区分大小写,依据是 xcrun simctl list devices booted 返回的设备名称。示例:
npx @expo/serve-sim tap 0.5 0.5 -d "iPhone 16 Pro"
npx @expo/serve-sim button home -d ABC12345-...
npx @expo/serve-sim --list # show all running streams
如果用户只启动了一个模拟器,请完全省略 -d。此技能应优先自动检测,而不是硬编码设备名称。
输出模式
默认情况下,serve-sim 会将人类可读的状态信息打印到 stdout。对于 Agent 循环,建议使用 JSON 输出:
npx @expo/serve-sim --list -q # JSON object; one stream is top-level, many are under .streams
npx @expo/serve-sim --detach -q # JSON with pid/port/url after spawn
npx @expo/serve-sim camera status -q # JSON with {alive, source, mirror, ...}
请以编程方式解析 -q 输出。切勿解析非 -q 的人类可读输出,因为它可能会随版本变化。
在 Agent 的预览中显示视频流
当用户要求“在这里查看模拟器”“在预览中查看”“在此工具中打开”或类似内容时,目标是将模拟器视频流传输到用户正在聊天的同一界面。serve-sim 会返回一个普通的 HTTP URL,Agent 需要显示该 URL;如果宿主提供预览工具,则将 URL 交给该工具。
步骤:
-
启动 serve-sim 并获取 URL:
npx @expo/serve-sim --detach -q这会返回类似
{"pid":..., "port":3100, "url":"http://localhost:3100", "streamUrl":"http://localhost:3100/helper/<udid>/stream.mjpeg", ...}的 JSON。url字段是面向用户的预览界面;streamUrl是原始 MJPEG 端点。 -
始终在回复中明示 URL,以便用户可以选择在任意浏览器中手动打开。
-
检查宿主提供的预览工具,如果有,则将 URL 交给该工具。你可能会在工具集中看到以下工具名称:
preview_start(Claude Code)——使用{ url: "<url>" }调用。mcp__Claude_Preview__preview_start(某些 MCP 配置)。browser_open、open_url或类似的 URL 打开工具——传入 URL。- Cursor / Codex CLI / 其他工具可能不会向 Agent 提供预览工具。此时,只需输出 URL,并告知用户如何打开它(使用浏览器、IDE 内置浏览器窗格等)。
-
不要假定一定存在特定的预览工具。 检查当前会话中可用的工具。如果有符合上述描述的工具,就使用它。如果没有,则回退到第 2 步(醒目地输出 URL)。
视频流会一直保持运行,直到执行 npx @expo/serve-sim --kill。多个客户端(宿主的预览、用户的浏览器和隧道)可以同时读取同一个 URL。
完整操作步骤请参阅 references/workflows.md 中的工作流“在宿主的预览中显示模拟器视频流”。
工作流
有关完整的端到端操作步骤(UI 自动化、相机测试、由无障碍功能驱动的点击、深层链接流程和预览交接),请参阅 references/workflows.md。该参考文档涵盖了 serve-sim 自身 AGENTS.md 中记录的模式。
清理
完成后始终停止辅助进程,除非用户明确要求它们继续运行:
npx @expo/serve-sim --kill # stop all
npx @expo/serve-sim --kill "iPhone 16 Pro" # stop one
遗留服务会占用其记录的端口,并阻止新的服务启动。
反模式
- 不要传入像素坐标。 所有坐标都已归一化到
0..1。如果用户提供的是像素值,请除以GET /config报告的屏幕尺寸。 - 不要将
gesture用于普通点击。 请使用tap。参见上文“关键注意事项”。 - 不要假定
npx @expo/serve-sim已在运行。 使用--list或检查$TMPDIR/serve-sim/server-{udid}.json进行验证。如果文件不存在,请显式启动。 - 首次在会话中调用时,不要跳过先决条件检查。 macOS 版本不兼容、缺少 Xcode CLI 工具或 Node 版本低于 20,都会导致后续出现令人困惑的错误。
- 不要编造按钮名称。 只有以下六个名称有效:
home、swipe_home、app_switcher、lock、siri、side_button。权威列表请参阅 references/buttons-rotation.md。 - 不要解析非静默模式下的人类可读输出。 使用
-q获取 JSON。 - 不要让相机辅助进程在无关任务期间继续运行。 完成后使用
npx @expo/serve-sim camera --stop-webcam停止它们。 - 如果无障碍查询未找到匹配项,不要猜测坐标。 如果你获取了 AX 树(例如使用
GET /ax)来查找目标元素,而查询未返回结果,请明确报错——点击猜测的位置几乎总是不如向用户报告“未找到目标”。有关防护模式,请参阅 references/workflows.md 中的工作流 1。
参考索引
- references/gestures.md — 手势 JSON 的精确格式、边界值、多点触控以及拖动/滑动操作示例。
- references/buttons-rotation.md — 六种有效按钮和四种方向,以及行为说明。
- references/camera.md — 合成摄像头注入:占位符、文件、网络摄像头、镜像模式和热切换。
- references/permissions.md — 授予/撤销应用隐私权限,包括推送通知权限。
- references/ca-debug.md — 五个 CoreAnimation 调试标志及其适用场景。
- references/endpoints.md — 适用于绕过 CLI 的 Agent 的 HTTP 和 WebSocket 端点。
- references/workflows.md — UI 自动化、摄像头测试和深层链接流程的端到端示例。