Added
-
Physical iPhones and iPads are now first-class targets of the top-level verbs:
sim-use ui,sim-use tap(#<id>/--id/--label/--label-contains/--element-typeforms) andsim-use screenshotroute a physical UDID to the audit/CoreDevice backends automatically, instead of rejecting it with a pointer at theios-devicenamespace. Every other verb and form (coordinates,@N/#Naliases, swipe/gesture/touch/multi-touch/long-press, type/paste, button, keyboard-state, app-state, recording,--value/--label-regex/--frame/--duration/--wait-timeout) rejects per verb with aTargetCapabilityErrorstating why the channel cannot carry it and the nearest alternative (thehintin--json) —Platform.iOSDevicejoinsPlatformRouter, so the compiler enumerates every top-level capability decision — 15 device-scoped verbs in all (3 routed, 12 rejected). Routed physical results state their restricted shape explicitly:describe-uicarries"kind":"physical"with noraw/entries/lists/screen(the outline text,#ids included, is the payload), andtapcarries"kind":"physical"with the matched element instead ofx/y. Physical targets always run in-process — no per-UDID daemon is spawned for them (daemon session persistence is tracked separately). Theios-devicenamespace stays as the physical-only peer ofios/android(ECID addressing, tree-tuning flags);sim-use ios <verb>remains simulator-only and still fast-fails on a physical UDID, now pointing at the routed top-level verbs. The docs gained an explicit verb × target capability matrix. (#126) -
sim-use devicesnow covers every target: USB-attached physical iPhones/iPads are listed alongside simulators and Android devices (FBDeviceControl discovery, ~0.4 s with a device attached / ~1 s with none, run in parallel with the simctl and adb listers), and every row carries a newkind—simulator/emulator/physical— orthogonal toplatform, as aKINDtable column and an additivekindkey in--json(older payloads without the key decode with an inferred kind). Android's emulator-vs-physical distinction is now stated instead of being inferable only from the serial shape. A freshly attached iPhone may be listed by ECID until a session opens; theios-deviceverbs accept either identifier.--no-physical-iosopts out of the FBDeviceControl side entirely; the Viewer passes it (its/api/devicesalso forwardskindnow), since its UI is built on coordinate taps, frames and video streaming — capabilities the physical channel doesn't carry (the routedui/tap/screenshotsubset isn't enough to drive the SPA). (#122) -
sim-use ios-device(experimental): drive a development-signed app on a physical iPhone or iPad.deviceslists attached devices,uiprints the foreground app's accessibility tree, andtap --label/--label-containssends Activate to one unambiguous match. sim-use installs and signs no runner and needs no Developer Disk Image; the device must be unlocked and the target app must haveget-task-allow=true. This channel intentionally omits coordinate tap, swipe and gesture because the daemon exposes no element geometry. (#93, #99 — thanks @subdiox!) -
sim-use ios-device uinow renders each element's accessibility identifier as#id, andsim-use ios-device tapaccepts it as a positional#<id>or--id(mirroring the simulator tap). This is a stable handle to prefer when a label is dynamic — a navigation-bar back button is labelled with the previous screen's title but keeps#BackButton. The@Nalias and coordinate forms remain unavailable on this channel (handles expire between processes; the daemon exposes no geometry). Label and identifier matching go through the same case-sensitiveSelectorTextMatcherpolicy the simulator and Android surfaces already use, so a selector behaves identically across all three. (#114) -
sim-use ios-deviceverbs now support--json, emitting the same{ok, data}envelope as the simulator and Android surfaces so the agent-side parser stays shared:devicesreturns unified device rows (thedeviceId/kind/runtimeschema of top-levelsim-use devices --json),uireturns the outline text plus structured rows (depth/role/label/ optionalidentifier) with element/node counts and timing,tapreturns the matched element (action/role/label/ optionalidentifier, the id trimmed exactly as the outline shows it), andscreenshotreturns the saved path. Theuischema deliberately carries no@Naliases — element handles expire between processes. Structurally the four verbs adoptedSimUseExecutableCommand(the sharedexecute()/format()split); they still run fully in-process — daemon session persistence is tracked separately (#120). Error output moved its recovery advice to the shared hint channel: the--jsonfailure envelope carries a machine-readablehintfield, and the text path prints the same advice as aHint:line after the error instead of embedding it in the message. (#123) -
sim-use ios-device screenshot: capture a PNG of a connected iPhone or iPad display. Capture runs over CoreDevice (xcrun devicectl device capture screenshot) rather than the accessibility audit channel, so — unlikeuiandtap— it is not limited to development-signed foreground apps: whatever is on screen is captured, SpringBoard and system apps included. Device selection matches the otherios-deviceverbs (--deviceoptional with exactly one attached), and--outputfollows the shared path semantics with aDevice Screenshot - <device name> - <timestamp>.pngdefault. A rejected path or a capture that fails mid-flight never removes an existing file at--output: the image lands in a temporary sibling and replaces the target only on success. The--outputpath resolution shared by the simulator screenshot and the video verbs is now factored into oneOutputFilePathhelper instead of two per-target copies. (#118)
Fixed
screenshot/ios screenshot: a simulator name containing path separators (simctl accepts any free text, e.g.My iPhone/Work) no longer turns the default output filename into a directory hierarchy — the name is collapsed into a single path component, matchingios-device screenshot. The Android default filename embeds the adb serial, whose accepted charset already excludes separators; it is now sanitised too as defence in depth. Video default filenames embed no user-controlled text and were already safe. (#119)sim-use ios-device uino longer drops the navigation-bar back button (and any other element whose token the daemon aliases with the root). On a pushed screendeviceFetchSpecialElement: 0returns the back button as the root, so seeding the walk's visited set with the raw root token silently discarded it; the walk now dedups on(token, summary, role), so the back button appears in the outline and is tappable with the ordinarytap. (#114)sim-use ios-devicediscovery bails in ~1 s when no iPhone is attached, instead of waiting the full 5 s timeout. An empty attachment set never satisfies the quiescence rule (it needs a non-empty, unchanged set), so the discovery loop used to run to the deadline on every device-less host; it now gives up after a short grace once nothing has appeared. A device that is present still settles in ~0.4 s, and a multi-device attach burst still coalesces (the grace only applies until the first device is seen). (#117)sim-use ios(simulator-only) verbs reject a physical iOS device UDID at resolution time — pointing at the routed top-level verbs — instead of misclassifying it as an Android serial and diagnosing a plugged-in iPhone as "not reachable via adb". (#99, #126)- Wait for physical-device attachment notifications to settle so multiple USB-connected iOS devices are all discovered. (#99)
- Reject empty physical-device hierarchies, missing or ambiguous tap targets, and invalid hierarchy concurrency instead of reporting success or silently choosing an element. (#99)
- Correlate DTX replies by both identifier and conversation index so unsolicited device events cannot satisfy an unrelated pending request. (#99)
record-video --gif-markers(all three surfaces): bracket a GIF with START/END marker cards (~1 s each) so the forever-looping clip has a visible boundary. Opt-in — the default output remains a faithful capture of the screen. A failed card render degrades to a marker-less GIF instead of failing the transcode. (#100, #101 — thanks @kws0210!)