iPhone-use 工具回退指南:在 MCP 绑定失效时通过 phone.py 与 Runtime 继续操作真实 iPhone 【免费下载链接】iPhone-use让 Codex 通过 USB 操作真实 iPhone引导安装、App 自动化、实时屏幕与截图回退。项目地址https://gitcode.com/gh_mirrors/ip/iPhone-use点击查看免费下载本篇聚焦 iPhone-use 这个 Codex 插件在“MCP 工具绑定缺失、宿主转发错误或封装层已确认异常”时的工程化回退路径。读完后你将能够准确区分“工具名找不到”“参数写错”“手机通道故障”这三类问题用scripts/phone.py或直接导入server/iphone_use.py的Runtime继续同一已授权动作并保证回退入口复用相同的配置、会话、操作锁与异常语义而不是另起一套并行控制。本篇内容以仓库内的 工具故障与代码调用 为主线并结合 server/iphone_use.py、scripts/phone.py、server/wda_client.py、server/wda_controller.py 与 tests/test_fallback.py 的实际实现进行印证。回退的第一原则绑定缺失不等于 PUA 不可用参考文档给出的总原则非常克制优先调用封装好的工具默认乐观执行常规动作下一步需要信息时顺带判断上一动作。工具绑定缺失、宿主转发错误或已确认的封装问题都不等于底层 PUAPhone Use Agent不可用此时可对同一已授权动作使用插件代码入口。关键约束有两条代码回退保留相同的配置、会话、操作锁和明确异常处理不新增每步验收回退不是“换一套更宽松的通道”而是换一个入口走同一份共享实现。第二条在源码里可以直接看到server/iphone_use.py 中的Runtime._dispatch对绝大多数工具都会先打开state_dir/operation.lock以fcntl.LOCK_EX | fcntl.LOCK_NB抢占独占锁抢不到就抛出device_busy。pua_screen、pua_screen_frame、pua_screen_action等预览相关路径才走旁路。也就是说无论请求来自 MCP stdio 进程还是phone.py子进程同一台手机的控制权都被这把文件锁串行化。先看本回合实际可调用的工具绑定参考文档明确要求普通版提供18 个模型工具要判断“工具是否可用”应当查看本回合实际可调用的工具绑定宿主支持时用ALL_TOOLS或工具搜索不能只从tools/list、文档或先前聊天推断已经绑定。源码层面也能解释这个要求server/iphone_use.py 基于SCHEMAS生成TOOLS列表其中screen_frame与screen_action是APP_TOOLS通过_meta.ui.visibility[app]只暴露给预览 App模型不可见因此SCHEMAS的键数不等于模型工具数。此外还有两个容易误判的细节0.1.6 的install.sh同时注册同名标准 MCP避免 Codex 插件共享说明预算隐藏工具并保留完整 batch schema。见 scripts/register_mcp.py它以安装结果中的installedPath定位server/iphone_use.py执行codex mcp add iphone_use -- python3 server并只退役自己此前注册过的同名旧条目。新版安装后需要重新连接聊天实际仍缺少所需工具时才使用代码入口继续不要默认把已封装操作都改成 CLI。CHANGELOG 的 0.1.6 条目同样记录了这次改造的三件事同名标准 MCP 注册、batch 发布 schema 精简selector/observe提取为$defs保持低于宿主 5KB 有损压缩门槛、以及运行时闭集参数校验保持完整。对应实现在 published_schema其 docstring 明确说明Codex 会压缩超过 5KB 的 schema可能丢参数分支因此发布版复用$ref瘦身而运行时校验仍使用完整的SCHEMAS不做$ref解析。先区分错误八类典型场景的处置口径参考文档的核心价值在于把“看起来都像失败”的结果拆成可执行的分支。下表在其基础上补充了源码中可直接核对的错误码与字段。错误 / 信号含义正确处置schema / 参数错误invalid_argument参数与当前闭集 schema 不符action_executedfalse表示动作未发出按argument_path、unknown_fields、allowed_fields修正一次调用MCP 绑定仍不可用时可用代码入口不要自动忽略未知参数device_busy另一操作持有共享锁等待它结束不另建 Runtime 配置或 state directory 绕过锁stale_observation提供的 ID 不属于当前 Runtime或 App / 视口上下文已改变用下一步所需的新信息定位不为整图哈希、文本数字刷新或固定 30 秒期限增加读图ID 是可选上下文不是每个坐标动作的必填许可界面异常selector / 焦点失败、滚动后找不到可点击目标、无进展、区域被挡、上下文改变、输入不符、预期页面未出现语义定位已不可靠需要视觉判断统一先看截图先用结果里已附的截图缺图才补一次screenshot再选择可见目标、处理浮层或决定是否继续滚动不先重读树、不换标签写法、不原样重试input_continuation_expired长文本续传已失效本次没有输入任何内容读取字段实际文字用replacefalse只补缺少的部分occluded_target/offscreen_target目标点击没有执行不能推断“未执行的点击打开了浮层”occluded_target按附带截图判断目标可见就按tap_point用坐标点击确有遮挡先处理遮挡offscreen_target先向目标滑动。自绘半屏面板可能没有原生 Alert / Sheet 节点按真实截图判断显式验收的postcondition_failed动作已接收但验收条件未满足查看实际状态再决定剩余步骤不因验收失败自动重放动作action_executedtrue, action_completefalse、action_uncertain、uncertaintrue或动作后 timeout至少部分动作可能已生效先用action_uncertain.transport_error区分传输类型按 recovery 等待或检查连接再观察真实字段 / 记录不能切换代码入口后盲目再做输入、发送、提交、删除或整个批次pua_timeoutsocket 等待或调用预算耗尽不证明断连按retry_after_seconds在宿主侧等待当前为 2 秒再取一次pua_observe(modescreenshot)不立即重试整树或动作也不用pua_wait它会新增 AX 查询。截图仍超时最多再延迟尝试一次之后用pua_setup(actionstatus)诊断重复超时本身不授权重启。若原调用是 READY用相同参数延迟重试 READYpua_unreachable连接拒绝、连接重置、管道关闭或 EOF 等 HTTP 通道连接失败调用pua_setup(actionstatus)复用当前 start / recover 工作确认服务未运行、已配置、没有活动工作且用户允许启动时才 start 一次连接失败本身不证明物理 USB 已拔出pua_transport_error其他传输或协议异常按返回细节检查 setup status / 日志不当作超时或确定断连不自动重启或重放动作local.pid.0、pua_foreground_unavailable或 XCTestCode 41通道故障调用 READY 恢复不要通过更改 selector 或重复手机按钮处理锁屏、签名与信任问题按 setup skillApp 密码 / Face ID 按认证接管这张表与源码的分类函数一一对应WDAClient.transport_error 把传输异常拆成pua_timeout、pua_unreachable、pua_transport_error三类只读请求返回pua_timeout等原始码并补一句“本次读没有执行手机动作”写请求统一包装为action_uncertain且uncertainTrue并附带transport_error供上层区分。Runtime.channel_fault 识别 XCTest 授权失败not authorized for performing ui testing actions、XCTDAEMONERRORDOMAINcode41与前台不可用pua_foreground_unavailable、local.pid.搭配stale element reference据此决定进入恢复流程还是直接返回原始错误。validate 在对象出现未知字段时抛出invalid_argument并在details中带上action_executed: false、argument_path、unknown_fields、allowed_fields与recovery.next_step——这正是“按字段修正一次”而非“猜着重试”的依据。还有一条容易被忽略的口径查询失败不证明手机动作已执行POST 查询接口不应按 mutation 处理查询超时也不自动立即重读遵循上述有界延迟恢复。截图的转发不要把图片变成 base64 文本参考文档专门用一段说明通过functions.exec调工具时的转发方式逐个内容块转发文字用text(block.text)图片用image(block)不要text(result)把截图变成 base64 文本。图片无法转发时用view_image打开image.path/error.observation.image.path坐标按image.pixel_to_point换算。这不是风格问题而是正确性问题。见 result_content返回值是“一个紧凑 JSON 文本块 命中时追加一个 image 块”并且刻意不发送structuredContent因为宿主一旦只看 structured 对象就可能丢弃包括图片在内的所有 content 块。同理pua_screen_frame走structuredContent且不产生模型文本预览帧不应变成上下文负担。image.pixel_to_point的生成位置在 PhoneController.capture用真实视口尺寸除以图像像素尺寸并保留 4 位小数因此“图像像素 × pixel_to_point iPhone 点”是由实现保证的换算关系而不是经验值。READY 的有界恢复先重试再重启恢复章节是整份文档中最需要“按字面执行”的部分因为它直接决定会不会误杀一个其实健康的服务。常规路径正常任务用pua_ready(recovertrue)或省略recoverfalse仅用于用户明确禁止重启或只读诊断并应保留该限制。暂时失效的前台只清理旧观察 / 会话并重读一次持续故障才重启已核验归属的本插件服务并复用有效构建。整个过程不会重放导航、输入或提交。源码中的两段式处理正是如此Runtime.ready 在首次失败后若判定为foreground_unavailable会关闭 client、清空session_id、调用phone.reset()再执行一次ready_once并在结果里标注recovery{state:read_recovered,session_recreated:True,replayed_action:False}。这个replayed_action: false是给上层模型看的强约束信号。READY 的pua_timeout不进入重启流程按retry_after_seconds在宿主侧等待再用相同参数重试最多两次延迟尝试仍超时只查询 setup status 与日志。不得用pua_wait代替宿主等待不因累计超时自动 start / recover。源码给出的 guidance 字段写得很具体retry_after_seconds2、max_wait_retries2、next_toolpua_ready、replay_actionFalse并要求“最多两次延迟尝试后仅做诊断性 setup(status)”iphone_use.py。pua_unreachable才进入连接 / 启动检查此时原始错误会被补上initialization_requiredTrue与recovery.next_toolpua_setup、recovery.next_arguments{action:status}并明确写着“连接失败或not_ready不证明服务已停止”iphone_use.py。恢复中的状态查询readyfalse, staterecovering携带recovery.job_id和查询参数。status 返回jobs数组需要找到id与job_id相等的工作检查recovery_phase从stopping/starting到serving后重验 READY。Runner 可以长期 running不等 succeeded、不重复 start。按阶段、日志和retry_after_seconds查询不写固定 15 秒 × 20 次的等待循环也不能读取不存在的单个 job 并吞掉解析异常。当readyfalse, staterecovery_required, reasonrecovery_disabled时只有用户指令允许才按next_tool/next_arguments调用recovertrue明确禁止则说明阻塞。注意这两种状态没有error、MCPisErrorfalse但仍不能继续手机动作——这一点在 tests/test_fallback.py 里被显式断言结果里assertNotIn(error, result)同时 CLI 退出码为 1。归属与冷却恢复要求配置、endpoint、worker 和监听端口归属匹配归属不明、外部 PUA、端口冲突或缺少构建时按返回步骤处理不按端口杀进程。120 秒冷却按retry_after_seconds诊断不连续重启。信任、解锁与 UI 自动化确认由用户在手机完成。冷却常量RECOVERY_COOLDOWN_SECONDS 120在 server/wda_setup.py 中定义。README 与 skill 也反复强调一个边界recovertrue是运行时恢复不是冷启动pua_timeout不等同于断连action_uncertain必须保留transport_error以区分超时与连接失败永不重放不确定的动作。直接执行提供的脚本scripts/phone.py当宿主无法走 MCP 工具时最轻的回退是直接运行脚本。参考文档给出的常规 Home 示例如下python3 PLUGIN_ROOT/scripts/phone.py pua_press_button {name:home,observe:none,verify:false}其中PLUGIN_ROOT要从当前项目或已安装插件确定不要照抄某人的版本 cache 路径。仓库内该脚本位于 scripts/phone.py。参数与行为第一个位置参数是工具名取值范围由SCHEMAS派生argparse的choices[pua_ name for name in SCHEMAS]因此不会出现脚本支持但运行时未知的工具名第二个位置参数是与 MCP 工具 schema 一致的 JSON 对象默认{}--state-dir、--url只在原通道确实使用非默认目录 / URL 时才传不得为避开 busy 换目录或并行控制同一手机。默认状态目录的解析见 state_directory优先--state-dir其次环境变量IPHONE_USE_STATE_DIR或旧名WDA_STATE_DIR否则~/.local/share/iphone-use若新目录不存在而旧目录~/.local/share/iphone-use-wda/config.json存在则复用旧目录以保留既有安装状态。为什么它“看起来像 CLI行为却像工具”脚本本体只有 36 行但每一步都对齐 MCP 路径先把server目录插入sys.path再from iphone_use import PUAError, Runtime, SCHEMASJSON 解析使用parse_constant拒绝NaN/Infinity避免非有限数字进入数值校验构造Runtime(args.state_dir, args.url)后调用runtime.call(tool, arguments)——与 MCP 调用同一个Runtime.call因此共享参数校验、operation.lock、session.json会话恢复与异常语义PUAError转成{error: exc.as_dict()}ValueError/TypeError转成invalid_argumentfinally中调用runtime.close()关闭预览与 client、刷新分析缓冲。输出与退出码命令输出只有 JSON截图位于image.path已缩放的 JPEG同名.png是原始截图需要查看时用可用的图片工具打开像素乘以image.pixel_to_point得到 iPhone 点。退出码语义在 phone.py 一行内return 1 if error in result or (args.tool pua_ready and result.get(ready) is not True) else 0即有error字段或pua_ready的ready不是严格的True都返回 1。这是“诊断状态也是非零”的刻意设计ready1或readytrue都不算就绪tests/test_fallback.py的test_fallback_ready_requires_explicit_true对这三种取值逐一断言。被测试钉住的行为tests/test_fallback.py 用假 client 覆盖了回退入口最容易出错的几处默认一次动作、无前台回读{name:home}返回action_executedtrue、foreground_verifiedfalse、verification_deferredtrue且请求序列中没有/wda/activeAppInfo动作序列只有/wda/homescreen显式验收复用同一控制器{name:home,verify:true}才出现foreground_verifiedtrue不能绕过他人的操作锁外部先持有operation.lock时回退返回device_busy且client.calls []不能绕过锁屏lockedtrue时返回phone_locked且没有任何动作不重放不确定的 mutationhome_errorWDAError(action_uncertain, ...)时退出码 1、error.uncertainTrueHome 只发出一次schema 先于手机访问[]与{name:home,extra:true}都在invalid_argument处终止client.calls []畸形 / 非有限 JSON 在 Runtime 之前被拒{broken、{x:NaN}、{x:Infinity}的Runtime构造次数为 0锁文件权限为0600verifytrue用例顺带断言了operation.lock的 mode。写最小调用代码直接导入 Runtime宿主无法调用脚本时可以导入同一 Runtime。参考文档给出的最小示例如下保持默认配置和结构化错误import json import sys from pathlib import Path root Path(sys.argv[1]).expanduser().resolve() sys.path.insert(0, str(root / server)) from iphone_use import PUAError, Runtime runtime Runtime() try: result runtime.call(pua_press_button, {name: home, observe: none, verify: False}) print(json.dumps(result, ensure_asciiFalse)) except PUAError as error: print(json.dumps({error: error.as_dict()}, ensure_asciiFalse)) raise SystemExit(1) finally: runtime.close()围绕这段代码有几个必须理解的事实两个入口绕过的是 MCP 绑定不是共享实现。Runtime.call会做 schema 与语义双重校验validatevalidate_semantics再进入_dispatch的加锁段。observation ID 不跨进程共享。若选择使用 ID必须在同一 Runtime 内取得观察并传给动作否则会因为不在当前 Runtime 已知集合而得到stale_observation。已知坐标或 region 可不提供 ID不为获得许可额外读图或树。这直接呼应文档中“ID 是可选上下文”的表述。使用 iPhone 点坐标不使用 Mac 屏幕坐标或截图像素。共享实现自身故障不会因换入口而修复遇到异常应按实际错误调整不要另建外部 PUA session也不要盲目重复 mutation。Runtime.close()必须执行。它关闭预览 hub 与 HTTP client并关闭 analytics否则连接与后台线程可能悬挂。关于默认配置的来源Runtime.__init__的取值顺序是构造参数base_url→ 环境变量WDA_URL→state_dir/config.json中的local_port拼成http://127.0.0.1:port→http://127.0.0.1:18100iphone_use.py。这也是为什么回退时不应该随手改--url它与原通道不一致时操作锁虽在同一state_dir却可能指向不同服务。此外WDAClient 对 URL 本身有硬约束必须是http且 host 属于127.0.0.1/localhost/::1路径为空或/且不允许用户名、query、fragment——错误信息就是“PUA URL must be a local HTTP endpoint; forward USB to loopback first.”。这意味着任何“绕过”的想法在构造阶段就会失败而不是变成一次隐蔽的远端调用。会话与原生设置也会被回退入口继承_dispatch在拿到锁之后会尝试从state_dir/session.json读回session_id并校验其中的url与当前base_url一致、ID 形如[A-Za-z0-9-]{1,128}若存在settings_restore例如snapshotMaxDepth/snapshotMaxChildren会调用reapply_settings在下次请求前重放iphone_use.py。save_session使用临时文件 os.replace原子写入权限0600并在首次写入原生限制前先记账——这解释了为什么“回退后还应该保持原来的树深度设置”而不是回到库默认值。滚动与批次默认乐观但有明确停止点回退入口既然是同一实现滚动与批次的语义也完全一致。swipe 的默认值默认verifyfalse, observenone执行一手势不读 XML 验进展也不自动 fallback。已知region可直接使用不强制树、锚点或 ID。下一步本就需要读取列表时在该次动作直接返回所需观察并顺带判断是否移动默认未验证返回不等于没有移动。源码 PhoneController.swipe 与此一致显式verifytrue才做一次几何检查无进展时附带截图并返回no_scroll_progress上下文变化则返回scroll_context_changedrecovery.same_gesture_retryFalse、replay_actionFalse明确禁止自动换手势重试。verifytrue 的边界显式verifytrue才检查滚动进展、原生浮层和变化上下文需要调试时在该次调用选tree/both复用返回观察不重复另读。进展以列表几何变化判断数字或轮播文本刷新不应单独作为滚动成功 / 拒绝动作的依据。原生浮层的验证保护不能识别所有自绘面板明确遮挡后按真实状态选区域或关闭面板。verifytrue发现第一次手势无进展就附截图停止不自动换手势旧参数max_attempts2仍可传入但不导致自动重试。scroll_find每次最多滑一次仍找不到可点击目标就先返回截图遮挡和歧义在滑动前即停止。应先实际查看图片再判断继续、区域、方向和目标不直接增加max_swipes重跑。三个状态码的准确含义no_scroll_progress不证明空列表或到底异常结果即使请求none/tree也附截图应检查入口、边界、浮层及自绘内容。scroll_context_changed表示手势后上下文变化先看截图再处理已执行部分不能当作未执行而重放。截图读取失败时保留原错误与执行证据并提示单独取图不自行继续动作。batch 的继续规则batch 可连续执行普通 unverified 导航和输入明确 error、uncertain 或未验收的 submit 才停止按completed_steps/stopped_at继续剩余步骤。launch 默认只激活一次显式verifytrue/expect才有界等前台 / 页面。关键验收失败先看实际状态不自动再次 activate。PhoneController.batch 把停止条件写死为几种显式情况input_complete is False→stop_reasoninput_continuessubmitted但submission_verified为假 →submission_requires_verificationWDAError→stop_reasonexc.code超时 →time_budget。返回体固定携带completed_steps与非complete的complete:false并在next_step里写明“从stopped_at继续已完成步骤不得重复”。Home 的专用端点Home 使用手机端专用主屏幕端点。默认不单独等待 SpringBoard准备下一步时的观察会显示实际状态。需要确认主屏时显式verifytrue它有界核对前台通用按键接口返回 HTTP 200 而界面未变的问题不能据此推定 iOS / XCTest 的内部原因。对应实现 press_buttonname ! home走/wda/pressButtonHome 走/wda/homescreensessionFalse不需要会话且注释直接写明“XCTest pressButton 可能确认 Home 但前台未变因此使用专用端点”。这与回退脚本的测试用例互相印证。阶段结果不等于任务完成参考文档结尾强调代码调用成功、Home 完成、某 App 读完或一个 batch 完成都只是阶段结果应继续剩余工作最终关键结果与交付完成后才结束整项任务。同时普通verifiedfalse不是错误不要求 observe 或停止 batchHTTP accepted 只描述请求边界常规路径继续最终关键状态显式验收。pua_metrics提供 HTTP 与工具耗时、每个工具返回的字节数以及两次工具调用之间的等待宿主、模型与用户时间之和但评估要以实际任务耗时和交付完整度衡量不能把“省略每步验收”说成“业务已被证明”。回退决策清单在真正落到代码入口之前按顺序自检是否只看过tools/list或旧聊天先确认本回合的实际工具绑定新版安装后是否已重连聊天。是参数问题还是通道问题有action_executedfalseargument_path/unknown_fields就修正一次参数有device_busy就等待不要换目录。是否 READY 未达readytruerecovering查同一job_idrecovery_required看用户是否允许重启pua_unreachable先 setup statuspua_timeout先宿主等待再重试。是否有不确定的动作action_uncertain、action_executedtrue, action_completefalse一律先读真实状态绝不因为“换个入口”就重放。界面异常是否先看了截图缺图补一次screenshot坐标点用x/y需要换算时乘image.pixel_to_point。是否只用了同一份配置默认~/.local/share/iphone-use或旧目录复用--state-dir/--url只在与原通道一致时使用。结束后是否runtime.close()脚本会自动执行自写代码需要finally。只要这七步都通过代码回退就与 MCP 调用等价同样的锁、同样的会话、同样的错误码与同样克制的验收标准——这正是 工具故障与代码调用 想要传达的工程边界。赞分享【免费下载链接】iPhone-use让 Codex 通过 USB 操作真实 iPhone引导安装、App 自动化、实时屏幕与截图回退。项目地址https://gitcode.com/gh_mirrors/ip/iPhone-use点击查看免费下载相关推荐iPhone Use 实战指南用 Codex、WorkBuddy 与豆包工作通过 USB 操作真实 iPhoneiPhone Use 实战指南用 Codex、WorkBuddy 与豆包工作通过 USB 操作真实 iPhone iPhone Use 是一个把真实 iPho豆包工作接入 iPhone Use用自定义 STDIO 连接器让豆包通过 USB 操作真实 iPhone豆包工作接入 iPhone Use用自定义 STDIO 连接器让豆包通过 USB 操作真实 iPhone iPhone Use 原本是给 Codex 写的 MiPhone Use 技能实战用 PUA 协议驱动真实 iPhone 的观察、操作与验收iPhone Use 技能实战用 PUA 协议驱动真实 iPhone 的观察、操作与验收 本篇技术指南聚焦 iphone use 技能文档 https://l上一篇10倍效率提升PowerToys图片尺寸批量处理从新手到高手的智能优化指南下一篇如何用giotto-tda检测引力波拓扑数据分析在天体物理中的革命性突破 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考