agent-droid
v0.1.0
Published
An agent-browser for Android: drive a real device or emulator over adb with a snapshot → ref → act loop. Animation-proof UI snapshots, text or ref targeting, JSON output — built for AI agents and humans.
Maintainers
Readme
agent-droid
An agent-browser for Android. Drive a real device or emulator over adb
with a tight snapshot → ref → act loop: snapshot the on-screen accessibility
tree into ref-tagged interactive elements, then tap/type/swipe by ref — or by
visible text. Built to be driven by AI agents (machine-readable --json, single
self-contained commands) and pleasant for humans too.
agent-droid snapshot
# @e5 [input] "[email protected]" (720,2180)
# @e6 [input] "********" [password] (720,2426)
# @e7 [button] "Sign In" (720,2612)
agent-droid type @e5 "[email protected]"
agent-droid type @e6 "hunter2"
agent-droid tap "Sign In" # target by visible text — no ref lookup neededWhy
Raw adb shell input tap X Y is brittle: you eyeball pixels off a scaled
screenshot, and the tap misses the moment the soft keyboard or a re-layout moves
things. agent-droid instead resolves every action against the live view
hierarchy and re-finds the target by node identity (class / content-desc /
text) right before acting — so taps survive layout shifts. Two more things make
it agent-friendly:
- Animation-proof snapshots. Plain
uiautomator dumpwaits for the UI to go idle and fails withcould not get idle stateon any screen with a continuous animation (common in React Native / Reanimated / game loops).agent-droid setupinstalls a tiny on-device UiAutomator instrumentation that dumps with the idle-wait disabled, so snapshots work mid-animation. It emits the identical XML schema, so nothing else changes. - One call, not four.
tap "Sign In"resolves visible text against a fresh snapshot and taps it — no snapshot → read → pick-ref → tap round-trip.--jsongives exact, parseable output.--then(opt-in) echoes the next screen.
Requirements
- Node.js ≥ 18
adbon yourPATH(Android Platform Tools), with a device/emulator connected (adb devicesshows it asdevice).- For the animation-proof dumper: nothing to build — prebuilt APKs are bundled
and installed by
agent-droid setup. (To rebuild them yourself you need a JDK 17 + Android SDK; see MAINTAINING.md.)
Works on macOS, Linux, and Windows. The driver is app-agnostic — it dumps whatever app is in the foreground.
Install
npm install -g agent-droid # provides `agent-droid` and the short alias `droid`
agent-droid setup # one-time per device: installs the dumper
agent-droid doctor # sanity check: device, foreground app, dumper statusOr run without installing:
npx agent-droid doctorThe loop
agent-droid snapshot— print the interactive elements as@eNrefs.- Act on one:
tap @e7,type @e5 "…",key ENTER,swipe up. agent-droid snapshotagain after every screen change — refs are per-snapshot.- Verify with
wait "text"(blocks until it appears) orlogs.
Commands
| Command | Purpose |
|---|---|
| doctor | Devices + foreground app + dumper status. Never throws. |
| setup | One-time per device: install the animation-proof dumper. |
| snapshot [query] | Dump the tree as @eN refs; query filters by name substring. |
| tap <@eN \| "text" \| X Y> | Tap a ref, a visible-text match, or raw coordinates. |
| type <@eN \| "text"> "value" | Focus the target, clear it, then type. |
| key <NAME\|code> | Keyevent: BACK ENTER TAB HOME DEL SEARCH ESCAPE or a raw keycode. |
| swipe <up\|down\|left\|right> | Scroll/swipe gesture. |
| wait "text" [ms] | Poll until text appears (default 8000ms). |
| current | Foreground package/activity. |
| logs [filter] [tail] | Tail app logcat. --tag <T> (default ReactNativeJS; '*' = all). |
| screenshot [file] | Save a PNG (default droid-shot.png). |
| --help | Full reference. |
Targeting: refs vs. text
@eN refs are the primary, exact handles — they carry node identity and
survive layout shifts. Visible-"text" targeting is a convenience addon: it
resolves against a fresh snapshot in one call (picking the most specific match
when several share the text). Reach for a ref when the text is ambiguous or
duplicated.
Flags
--serial <id>— target a specific device (default: first online).--tag <T>— logcat tag forlogs(defaultReactNativeJS;'*'for all).--json— machine-readable output (no human text); ideal for agents.--then— off by default. After an action, re-snapshot and print the new screen. Opt in only when you want the result echoed — it adds a full snapshot to the output.
Example: machine-readable, one call per step
agent-droid tap "Sign In" --json
# {"action":"tap","ref":"e7","name":"Sign In","cx":720,"cy":2612,"stale":false}
agent-droid snapshot --json | node -e 'JSON.parse(require("fs").readFileSync(0)).forEach(r=>console.log(r.ref,r.name))'Limitations
- iOS is not supported —
adbis Android-only. - Soft-keyboard occlusion: when the IME covers a field, the dump may return
only the keyboard window;
typethen falls back to cached coordinates. Dismiss the keyboard or scroll the field into view if atypedoesn't land. logsdefaults to theReactNativeJStag (handy for RN apps). For other apps pass--tag <YourTag>or--tag '*'.
How the animation-proof dumper works (short version)
agent-droid setup installs two tiny APKs: an empty host app and a UiAutomator
instrumentation test. The test sets Configurator.setWaitForIdleTimeout(0) and
calls dumpWindowHierarchy(...), returning the XML through an am instrument
status Bundle (the one transport that works on every device — file writes are
blocked by scoped storage, and test stdout isn't forwarded). UiAutomation reads
the whole screen system-wide, so it dumps any foreground app. Full rationale,
build steps, and the debugging guide are in MAINTAINING.md.
