Offsider
A hand for your agent on the iOS Simulator and the Android Emulator.
A command-line tool that reads and drives iOS Simulators, Android Emulators, and iPhones, iPads and Android phones over USB. It's built for terminals, scripts and AI coding agents, and it runs entirely on your Mac.
brew install michael-palmes/tap/offsiderOpen source under the MIT licence.
From install to first tap
It needs an Apple silicon Mac with macOS 26 or later and Xcode 26 or later. Android also needs the Android SDK.
# List simulators, emulators and USB devicesoffsider list-devices# Bind this shell to one deviceexport OFFSIDER_DEVICE=<ID># Check the host and the device before driving itoffsider doctor# One line per labelled element on screenoffsider describe-ui --summary# Tap, and exit 5 if nothing changesoffsider tap --id LoginButton --verifyoffsider screenshot --output screen.pngOn Android
offsider boot Pixel_9 starts the emulator and prints its serial, such as emulator-5554. Bind that, and the same commands work.
export OFFSIDER_DEVICE=$(offsider boot Pixel_9)For your agent
offsider init installs the bundled skill: add --client claude for Claude Code, or --client agents for ~/.agents/skills. offsider guide lists its topics, matched to the installed version.
Rather check the binary yourself? Each release also ships a tarball with SHA256SUMS and a build provenance attestation. Verify a release tarball
A hand that checks its work
Offsider reads the screen through accessibility, then taps, types, swipes, presses buttons and captures screenshots or video. Each step is careful enough to hand to an agent.
Checks the effect, not just the dispatch
Most input commands only confirm that the input was sent. Add --verify to tap, type, key or button and Offsider waits for the screen to change. If nothing changes, it exits 5.
Refuses taps that would land somewhere else
A tap by selector checks what a touch at its point would reach. When the target is off screen, covered by another view or under the keyboard, Offsider refuses and sends nothing.
Safe with more than one agent
Input commands lock the device while they run, so two agents can't interleave their input. A second command exits 8 at once, or waits its turn with --wait-lock. A lease marks a device as taken by one session.
--wait-lockexit 8Reads the screen compactly
describe-ui --summary prints one line per labelled element on screen, within a 16 KB budget by default. --diff prints only what changed since the last command.
# ios <ID> 402x874 @3x portrait 0°application "Playground" (0,0 402x874) button "Save" id=save-button (170.7,313.3 61x34.3) group id=rows (0,400 402x474) button "Inbox, 3 unread" (0,400 402x56) [off-screen below] 34 items: id=rows-item-9 to id=rows-end# folded 2 repeated labelsOne set of commands
The same commands drive iOS Simulators, Android Emulators, and iPhones, iPads and Android phones over USB. list-devices shows each one with its ID, and --device or OFFSIDER_DEVICE picks one.
At home in React Native
In debug builds, rn open loads an Expo dev client's bundle from Metro, rn devmenu drives the dev menu and rn logbox reads and dismisses LogBox toasts. logs --rn prints console output, in release builds too.
Errors an agent can act on
Every exit code is documented, so a script can branch on it. With --json, a failure prints one object with a reason, a hint naming the next command to run, and whether any input was dispatched.
- 0
- Success
- 2
- Nothing matched the selector
- 5
- A condition was not met
- 7
- The device wasn't found or isn't booted
- 8
- Another command holds the device
- 64
- Invalid arguments
How it compares
Each tool takes its own route to the same screens, and some do more than Offsider. e2e is a test framework rather than a device tool, but it drives the same screens through agent-device. Offsider's column comes from its repo; the others come from each project's own documentation.
| Offsider | AXe | idb | Maestro | agent-device | e2e | |
|---|---|---|---|---|---|---|
| iOS Simulator | Yes | Yes | Yes | Yes | Yes | Yes |
| Android Emulator | Yes | No | No | Yes | Yes | Yes |
| Physical devices | iPhones, iPads and Android phones, over USB | No | iOS devices attached to the Mac, for some commands | Android phones; iPhones not yet | iOS over USB or Wi-Fi; Android phones over adb | iPhones and Android phones plugged into the machine |
| Host OS | macOS 26 or later, Apple silicon | macOS 14 or later, Apple silicon or Intel | Companion on Apple silicon macOS 15 or later; Python client on any OS | macOS, Linux or Windows; iOS needs a Mac | macOS; Android also under WSL | macOS for iOS; Linux or WSL for Android |
| Interface | CLI with an agent skill; no MCP server, by design | CLI, batch step files and an agent skill | CLI, Python client and gRPC | CLI, YAML flows and an MCP server | CLI, MCP server and a Node.js API | TypeScript tests, CLI and an MCP server |
| Checks that input took effect | Yes: --verify exits 5 when nothing changed | Only slider; other input is fire and forget | Only before a tap, with --expected- | Waits for the screen to settle; can retry a tap that changed nothing | Yes, with --verify | Through the test's assertions, which retry until they pass |
| Refuses covered or off-screen taps | Yes, and sends nothing | Not documented | No: a tap aims at the element's frame centre | Not documented | Yes, the tap fails at once | Not documented |
| Device lock for several agents | Yes: a second command exits 8 or waits with --wait-lock; leases | Not documented | Not documented | Not documented | Yes: device claims per workspace | Within one run: workers never share a device |
| Installs and launches apps | No, by design: xcrun simctl and adb do that | No | Yes | Launches apps that are already installed | Yes | Yes |
| Test flow files | No: batch runs a whole case in one call | Batch files, one step per line | No; it runs XCTest bundles | Yes, YAML flows | Yes, .ad replay scripts | Yes, TypeScript tests with plain-language steps |
| Telemetry | None | Not documented | Hooks only; the open source build reports nothing | On by default; MAESTRO_ turns it off | Not documented | On by default; E2E_ turns it off |
| Licence | MIT | MIT | MIT | Apache 2.0 | MIT | Apache 2.0 |
Checked October 2026.
Coming from idb, Maestro or agent-device? offsider guide migrate maps each command.
Nothing leaves your Mac
- No telemetry, no accounts and no update checks.
- It never connects to non-loopback addresses and never resolves hostnames. It may use Unix sockets and loopback to local developer daemons: the adb server, the Android Emulator and usbmuxd.
- Password fields read as bullets, logs are redacted by default, and screenshots can mask password fields, labels, text, emails and regions.
- Test credentials and unlock codes live in your login Keychain, never synchronised.
- Physical devices connect over USB only. Offsider never pairs a device, accepts a prompt on one or types an iPhone passcode.
- Releases are signed with a Developer ID and notarised, and ship with SHA256SUMS and a build provenance attestation.
One caveat. A device's screen reaches whatever your agent sends to its model provider. Offsider itself sends nothing.
Found a vulnerability? Report it privately through GitHub, as the security policy describes.
This site has no analytics, no cookies and no third-party requests.
Open source, on GitHub
Offsider is free and open source under the MIT licence. Read the code, open an issue or send a pull request.
github.com/Offsider is a fork of AXe v1.8.0 by Cameron Cooke, used under the MIT licence, and is not endorsed by AXe's author. Its simulator frameworks are built from the michael-palmes/idb mirror at a pinned revision.