别再陪 AI 调 iOS 了:用 cmux + baguette,让 Claude 在你的模拟器里"自己动手"

一台 Mac,一个终端,一只 AI。模拟器从此不用你戳。

写在前面:让我愣了三秒的一幕

上周我让 Claude 帮我测一段地图 App 的行为,本来想自己截图喂给它。结果它问了我一句:

"你这边 baguette 装了吗?我自己跑就行。"

我愣了三秒。然后它真的把模拟器启动、截图、找坐标、点搜索框、粘贴中文、定位"新庄地铁站"全跑完了,从头到尾我没碰过模拟器。

这篇就是给当时和我一样懵的同学:cmux 是什么,baguette 是什么,怎么把它俩凑一起让 Agent 自己动手。


一、先认识两个工具

baguette:模拟器的命令行遥控器

baguette 是 iOS 模拟器的命令行控制工具。

平时玩模拟器,要打开 Xcode → Open Developer Tool → Simulator,弹出一个 iPhone 窗口,再用鼠标戳。

baguette 把这一整套塞进了 CLI:

bash 复制代码
baguette list                        # 列出所有模拟器
baguette boot --udid <UDID>          # 启动模拟器,不弹 GUI
baguette screenshot --udid <UDID>    # 拍一张屏幕
baguette tap --x 72 --y 455 ...      # 在指定坐标戳一下
baguette describe-ui                 # 把当前页面变成 JSON 树
baguette type "hello"                # 往焦点输入框打字(仅 ASCII)

最有意思的是这条:

bash 复制代码
baguette serve
# → http://localhost:8421/farm

打开浏览器你能看到所有运行中的模拟器实时推流,也能在网页上戳屏幕。

在 farm 里点对应设备就能进入单机视图,地址格式:

bash 复制代码
http://localhost:8421/simulators/3AC22B97-2407-4282-BC03-9F0335FA4387

cmux:给 AI Agent 用的终端

iTerm2、Warp、Ghostty 是给人用的终端。cmux 是给 AI Agent 用的。

它基于 libghostty(Ghostty 内核),多了几样东西:

  • 垂直标签页加分屏,左边 Claude Code 写代码,中间跑测试,右边盯模拟器,互不打扰
  • 监听 OSC 9/99/777 转义序列做通知,Agent 跑完任务能 ping 你
  • 内置可脚本化浏览器,Agent 能自己开网页、点按钮、填表单,不用再装 Playwright
  • 自带 cmux notify 命令,可以接到 Claude Code 的 Hook 里

简单说,cmux 是为 Agent 准备的工位。


二、为什么这俩配?

把它们拆开看:

痛点 单独使用 cmux + baguette
AI 看不到屏幕 你截图喂给它 screenshot 直出 JPEG
AI 不知道点哪 你描述坐标 describe-ui 给完整 a11y 树
AI 操作完没反馈 你再截一张 自动通知 + 再截图
多任务切换累 一堆 Terminal 窗口 垂直标签 + 分屏
中文不能用 type 卡死 pbcopy + simctl pbsync 走系统粘贴

baguette 解决"看见和动手",cmux 解决"工位"。凑一起就能干活。


三、3 步装好

Step 1:装 cmux

cmux.com 下 dmg 双击装,也能用 brew:

bash 复制代码
brew install --cask cmux

cmux 只支持 macOS(基于 libghostty 私有 API),Windows 用不了。

Step 2:装 baguette

baguette 用 Swift Package Manager 编译,需要 Xcode 26 + Apple Silicon(arm64e-apple-macos26.0):

bash 复制代码
git clone https://github.com/tddworks/baguette
cd baguette
swift build -c release
sudo cp .build/release/baguette /usr/local/bin/
baguette --version    # 验证

仓库 README 如果提供了 Homebrew tap,也可以直接:

bash 复制代码
brew install tddworks/tap/baguette

Step 3:启一个模拟器

bash 复制代码
# 列设备
baguette list

# 选一个 UDID 启起来(不会弹 GUI)
baguette boot --udid 3AC22B97-2407-4282-BC03-9F0335FA4387

# 看一眼
baguette screenshot --udid <UDID> -o /tmp/now.jpg && open /tmp/now.jpg

或者直接 baguette serve,浏览器开 http://localhost:8421/farm。

在 farm 里点对应设备就能进入单机视图,地址格式:

bash 复制代码
http://localhost:8421/simulators/3AC22B97-2407-4282-BC03-9F0335FA4387

四、实战:让 Claude 自己打开地图 App 搜"新庄地铁站"

下面是我真实跑过的命令序列,照样输到 cmux 里你也能复现。

1. 找地图 App 图标坐标

bash 复制代码
baguette describe-ui --udid <UDID> | jq '.. | objects | select(.label == "地图")'

返回(节选):

json 复制代码
{
  "role": "AXButton",
  "identifier": "地图",
  "frame": { "x": 38, "y": 410, "width": 68, "height": 90.67 }
}

中心点 (72, 455)

2. 戳开它

bash 复制代码
baguette tap --udid <UDID> --x 72 --y 455 --width 440 --height 956

第一坑:tap 必须传 --width --height。这俩不是手势宽高,是整个屏幕的逻辑宽高。iPhone 16 Pro Max 是 440 × 956,从 describe-ui 顶层 AXApplication.frame 拿。

3. 点搜索框

bash 复制代码
baguette describe-ui --udid <UDID> | jq '.. | objects | select(.identifier == "MapsSearchTextField")'
# → frame: { x: 16, y: 869, width: 362, height: 36 }

baguette tap --udid <UDID> --x 197 --y 887 --width 440 --height 956

4. 输入中文(关键技巧)

baguette type 文档明写 only US-ASCII,中文打不进去。绕法是走系统粘贴板:

bash 复制代码
# 1) 把中文塞到 Mac 剪贴板
printf '新庄地铁站' | pbcopy

# 2) 同步到模拟器剪贴板
xcrun simctl pbsync host <UDID>

# 3) 长按搜索框唤出"粘贴"菜单(duration 至少 1 秒)
baguette tap --udid <UDID> --x 197 --y 107 --width 440 --height 956 --duration 1.2

# 4) 点"粘贴"按钮(坐标从 describe-ui 拿)
baguette tap --udid <UDID> --x 51 --y 148 --width 440 --height 956

跑完,搜索框里就是"新庄地铁站",地图自动定位过去:

苹果地图(iOS 18 数据源高德)按关键词模糊匹配,南京"新庄"实际官方站名是"南京林业大学·新庄",1 号线和 3 号线交汇站,所以首条命中的就是它。整个过程 Agent 自己跑完,我没点任何东西。

5. cmux 里怎么"看戏"

打开 cmux,竖着分两屏:

  • 左边:Claude Code 执行命令
  • 右边:浏览器开 http://localhost:8421/simulators/3AC22B97-2407-4282-BC03-9F0335FA4387,实时看模拟器

那个长串是 iPhone 16 Pro Max 的 UDID,换成你自己设备的就行。Agent 每点一下,你眼看着 iPhone 自己动。


五、几个进阶玩法

自动化 UI 测试

把上面的步骤包成一个 prompt,让 Claude 跑回归:

text 复制代码
帮我测试地图 App 的搜索功能:
1. 启动地图 App
2. 在搜索框输入"新庄地铁站"
3. 截图保存到 /tmp/test_新庄地铁站.jpg
4. 如果搜索结果首条命中"新庄",标记 PASS,否则 FAIL

要批量回归,第 2 步换成关键字数组循环跑就是了,同一套脚手架。

跑完 Claude 会把每次的截图和 PASS/FAIL 列给你,比你自己手点稳得多。

多设备 farm 并行

bash 复制代码
baguette boot --udid <iPhone-16-UDID>
baguette boot --udid <iPad-Pro-UDID>
baguette boot --udid <iPhone-Air-UDID>

/farm 看板一次显示三台,Agent 可以并发对每台跑同一套用例。适配测试一次过。

cmux notify 闭环

在 Claude Code 的 Stop Hook 里加:

bash 复制代码
cmux notify "模拟器测试跑完啦,赶紧来看"

晚上让它自己跑一晚,第二天起来直接看通知。


六、新手会踩的坑

我都踩过了:

  1. baguette tap 报 "Missing argument --x"。参数顺序无所谓,但 --width --height 必传,看 --help 一眼漏掉是常事。
  2. 中文输入卡死。baguette type 不支持中文,老老实实走 pbcopy + pbsync + 长按粘贴。
  3. 长按变成普通点击。--duration 至少给 1.0,0.5 不够触发 iOS 的 long-press 阈值。
  4. describe-ui 抓不到列表项。iOS 的 UICollectionView 有时懒加载 a11y 节点,看不到具体行。这种情况只能根据图像估算坐标(屏幕逻辑大小 / 显示像素 = 比例尺)。
  5. Xcode 版本不匹配。baguette 当前要求 Xcode 26 + macOS 26 + Apple Silicon,老 Intel Mac 用不了。

写在最后

cmux 给 Agent 一张工位,baguette 给 Agent 一双手,剩下"该让它干嘛"是我们自己的活。

跑完那次"新庄地铁站",我第一反应不是"AI 真厉害",而是"我下班是不是更难了"。话又说回来,重复点屏幕的活本来就不该人来干,能让 Agent 自己跑,何必自己加班。

跑通了的同学欢迎评论区贴你的截图,看看你的 Claude 在 iPhone 里点了啥。


参考资料:

相关推荐
MonkeyKing71551 小时前
iOS 开发 Block 底层结构、循环引用及解决方案
ios·面试
文件夹__iOS1 小时前
Swift 5.9 被严重低估的特性:参数包,一次性干掉重复泛型重载
ios·swiftui·swift
薛定猫AI2 小时前
【技术干货】用 AI + Expo 打通 iOS / Android / Web 跨端应用开发:从架构到代码生成实战
android·人工智能·ios
文心快码BaiduComate2 小时前
Comate Spec模式实践:电商视频自动化生产数据库eDB-MCP服务开发
前端·后端·架构
page_qiu2 小时前
高并发&大数据量&毫秒级响应系统设计方案
java·前端·数据库·高并发·高响应
皮皮大人2 小时前
agent设计系统-大模型意图识别
前端·人工智能
三维搬砖者2 小时前
挑战AI辅助从零构建3D模型编辑器:01基于Vue3 + Three.js的现代化架构设计
前端·vue.js·github
GinoWi2 小时前
Python 集合
前端·python
时光足迹2 小时前
Tiptap之标注组件
前端·javascript·react.js