mcpField
S
serve-sim

serve-sim

github.com/expo测试与质量1039 次安装5 周前更新
代码仓库

关于 serve-sim

serve-sim 是一项指导 Agent 通过 serve-sim CLI 操作 Apple Simulator 的技能,涵盖模拟器控制、视频流、摄像头输入、权限与无障碍工作流。

主要功能

  1. 01点击、滑动、拖动并旋转模拟器
  2. 02向浏览器传输模拟器画面
  3. 03注入文件或摄像头画面
  4. 04管理应用权限与调试叠层
  5. 05读取无障碍树并模拟内存警告

适用场景

  1. 01自动操作已启动的模拟器应用
  2. 02分享模拟器画面以供审核
  3. 03测试依赖摄像头的应用行为
  4. 04检查无障碍界面元素与权限

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/expo/serve-sim --skill serve-sim

安装后在 Claude Code 中输入/serve-sim确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
evals/
evals.json
references/
buttons-rotation.md
ca-debug.md
camera.md
endpoints.md
gestures.md
permissions.md
workflows.md
scripts/
check-prereqs.sh
ensure-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 返回 Darwinserve-sim 只能在 macOS 上运行
Apple 芯片uname -m 返回 arm64捆绑的原生组件仅支持 arm64
Xcode CLI 工具xcrun --version 退出码为 0simctl 是底层模拟器驱动程序
Node.js ≥20node --version ≥20serve-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 交给该工具。

步骤:

  1. 启动 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 端点。

  2. 始终在回复中明示 URL,以便用户可以选择在任意浏览器中手动打开。

  3. 检查宿主提供的预览工具,如果有,则将 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 内置浏览器窗格等)。
  4. 不要假定一定存在特定的预览工具。 检查当前会话中可用的工具。如果有符合上述描述的工具,就使用它。如果没有,则回退到第 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。

参考索引

评论

支持 Markdown