Unified Python facade for mobilerun cloud and local device connections, providing high-level automation actions, human-in-the-loop approval hooks for destructive operations, and structured capability-aware error handling.
Project description
mobilerun-core is the programmatic Python API behind Mobilerun.
One sync facade — `Mobilerun()` — for driving Android (and iOS, on cloud) devices, whether they live in the Mobilerun cloud or on a USB cable. Pick a device id, get a `Device`, call tap_text, scroll_until, wait_for_app. No async/await ceremony, no SDK juggling.
- 🧰 One API for two transports — same helpers run against a cloud device (over
mobilerun-sdk) or a local Android phone (overmobilerun-core-cli). - 🪄 Auto-detection — pass any device id, the library figures out cloud vs local. Or override explicitly.
- 🎯 High-level helpers —
tap_text,tap_node,scroll_until,wait_for_app,open_and_settle,find_nodes,assert_on,screen_size, … built on top of the raw verbs. - 🛡️ HITL gate — destructive verbs (
uninstall, localinstall_apk) route through a callable you control. Default denies; you opt in per turn. - 🧭 Agent-friendly errors — when a backend doesn't support a verb, you get a structured
UnsupportedOperationwithverb,backend, and analternativefield instead of an opaque traceback. - 🪶 Pure library — sync, heredoc-friendly, no daemon, no server, no agent loop.
Use the library when you want to script a device directly from Python — locally or in the cloud. Use Mobilerun Framework when you want a full LLM agent driving the device. Use Mobilerun Cloud when you want hosted devices and managed infrastructure.
📦 Installation
Note: Python 3.14 is not currently supported. Please use Python
>=3.11,<3.14.
# cloud-only is enough to start
uv add mobilerun-core
# add local-Android (ADB) support
uv add mobilerun-core mobilerun-core-cli
Cloud credentials are picked up lazily — Mobilerun() does not touch the environment until you make a cloud call. For local-only use, no env vars are required.
# only needed for cloud
export MOBILERUN_CLOUD_API_KEY=...
export MOBILERUN_API_BASE_URL=https://api.mobilerun.ai/v1
🚀 Quickstart
from mobilerun_core import Mobilerun
m = Mobilerun()
# auto-detect cloud vs local from the device id
d = m.connect("550e8400-e29b-41d4-a716-446655440000") # cloud (UUID)
d = m.connect("R5CT123456") # local (ADB serial)
d = m.connect(some_id, cloud=True) # explicit override
# drive the device
d.open_and_settle("com.instagram.android")
d.tap_text("Search")
d.type("droidrun")
d.key("enter")
png_b64 = d.screenshot()
Cloud-side discovery is supported too:
m = Mobilerun()
d = m.ensure_device(filters={"name": ["pixel - test"]}) # one matching ready device
all_devices = m.list_devices(filters={"state": ["ready"]})
🧱 Concepts
Mobilerun() single user-facing facade
│
│ .connect(id) → Device helpers (tap_text, scroll_until, …)
│ │
│ ▼
│ Connection (Protocol)
│ │
│ ┌───────┴────────┐
│ ▼ ▼
│ MobilerunCloud MobilerunFramework
│ (mobilerun-sdk) (mobilerun-core-cli)
│ id = UUID id = ADB serial
Mobilerun— the only class users construct. Lazy cloud creds; framework-only use needs no env vars.Device— what you get back from.connect()/.ensure_device(). All the helpers live here.Connection— sync per-device contract (tap,swipe,type,ui,screenshot,app_*). One implementation per transport.
🪄 Backend selection
Explicit override always wins. Otherwise:
device_id shape |
adb devices shows it? |
Result |
|---|---|---|
| UUID | no | cloud |
| UUID | yes, cloud creds set | error (pin it) |
| UUID | yes, no cloud creds | framework |
ADB serial / emulator / IP:port |
yes | framework |
| anything else | no | error |
adb devices lookups filter state=="device" — offline / unauthorized rows don't count.
🛡️ HITL gate
Destructive verbs route through a HitlGate callable. Default is deny_all — pass your own gate to allow specific actions per turn.
Gated today:
device.uninstall(package)device.install_apk(path, …)(framework / local APK installs only)
Not gated: tap, swipe, type, key("power"), app_stop. Those are either non-destructive or trivially reversible.
from mobilerun_core import Mobilerun, HitlDenied
def my_gate(action: str, args: dict) -> None:
if not user_approved(action, args):
raise HitlDenied(action)
m = Mobilerun(hitl_gate=my_gate)
🧭 Agent-friendly unsupported verbs
The Connection Protocol is one surface, but each backend supports only a subset by design. When a verb isn't supported on the active backend, UnsupportedOperation is raised — subclasses NotImplementedError for back-compat, but carries structured fields an agent can branch on:
from mobilerun_core import UnsupportedOperation
try:
device.install_apk("/tmp/app.apk") # not supported on a cloud device
except UnsupportedOperation as e:
payload = e.as_dict()
# {
# "error": "unsupported_operation",
# "verb": "app_install_apk",
# "backend": "cloud",
# "reason": "...",
# "alternative": null,
# "hint": null,
# }
Introspect without calling:
UnsupportedOperation.is_supported(MobilerunCloud, "app_install_apk") # False
UnsupportedOperation.describe(MobilerunCloud, "app_install_apk")
⚙️ Features
- Two interchangeable transports —
MobilerunCloudwrapsmobilerun-sdk;MobilerunFrameworkwrapsmobilerun-core-clifor USB / wireless Android. - Sync API — heredoc-friendly. No
await, no event loop. - Helpers, not just verbs —
tap_text,tap_and_wait,scroll_until,wait_for_app,wait_for_idle,open_and_settle,find_nodes,assert_on,screen_size, … - Normalized return shapes —
ui()always returns a plaindict;screenshot()always returns base64 PNG. - Lazy cloud credentials —
Mobilerun()doesn't touch the env until you make a cloud call. - Back-compat —
Device(device_id, sdk_client, hitl_gate)constructor still works for callers pinned to 0.2.x.
☁️ Framework vs Cloud vs Core
| mobilerun-core (this lib) | Mobilerun Framework | Mobilerun Cloud | |
|---|---|---|---|
| What | Programmatic device-control API | Full LLM agent + CLI | Hosted devices + REST + dashboard |
| Best for | Code-level scripting, custom tools, custom agents | Natural-language tasks, reasoning, vision | Managed phones, fleet workflows, APIs |
| Where it runs | Wherever your Python runs | Wherever your Python runs | Managed by Mobilerun |
| LLM included? | No (you bring it) | Yes (OpenAI / Anthropic / etc) | N/A |
Most users start with the Framework. Reach for mobilerun-core when you want to build something the Framework doesn't ship — a custom agent loop, a test runner, a recording / replay tool, or batch automation.
💡 Example use cases
- Mobile app QA and regression testing.
- End-to-end flows in CI that target a real device or an emulator.
- Hybrid dev/CI workflows: same script targets your phone over USB locally, and a cloud device in CI.
- Building higher-level agent frameworks on top of a stable device API.
- Recording / replaying user flows for benchmarking.
🤝 Contributing
Issues and PRs welcome. The library aims to stay small and sharply-scoped — please open an issue before adding new surface.
git clone https://github.com/droidrun/mobilerun-core.git
cd mobilerun-core
uv venv
uv pip install -e . pytest ruff
.venv/bin/python -m pytest tests/test_abstraction.py -v
.venv/bin/ruff check mobilerun_core tests
📄 License
Apache-2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mobilerun_core-0.3.0.tar.gz.
File metadata
- Download URL: mobilerun_core-0.3.0.tar.gz
- Upload date:
- Size: 35.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0992a37631df5067b84b77230855584345ec0759b6b0901ce319ad0c0f0c4a86
|
|
| MD5 |
8c18b1796098f929fdacaf67b1696d92
|
|
| BLAKE2b-256 |
0b1292a8c84b9e00d849fba9b042ddf70ba19b64a96ec36ba87f8461699e7b23
|
Provenance
The following attestation bundles were made for mobilerun_core-0.3.0.tar.gz:
Publisher:
publish.yml on droidrun/mobilerun-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mobilerun_core-0.3.0.tar.gz -
Subject digest:
0992a37631df5067b84b77230855584345ec0759b6b0901ce319ad0c0f0c4a86 - Sigstore transparency entry: 1703961671
- Sigstore integration time:
-
Permalink:
droidrun/mobilerun-core@8cf8d0bf00fe3c798062e0cddcda72837d0a31d6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/droidrun
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8cf8d0bf00fe3c798062e0cddcda72837d0a31d6 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file mobilerun_core-0.3.0-py3-none-any.whl.
File metadata
- Download URL: mobilerun_core-0.3.0-py3-none-any.whl
- Upload date:
- Size: 27.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
36f72d6aacf8122cfef694637ff4dea060423b0ce89afe223b5ea12ae60c308c
|
|
| MD5 |
88602a51868dee0130a6d89d78c7344d
|
|
| BLAKE2b-256 |
f708dda80f19cc7ee1c1a973d6731fea0468bba2b0fc49b087c219494c6a3ded
|
Provenance
The following attestation bundles were made for mobilerun_core-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on droidrun/mobilerun-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mobilerun_core-0.3.0-py3-none-any.whl -
Subject digest:
36f72d6aacf8122cfef694637ff4dea060423b0ce89afe223b5ea12ae60c308c - Sigstore transparency entry: 1703961691
- Sigstore integration time:
-
Permalink:
droidrun/mobilerun-core@8cf8d0bf00fe3c798062e0cddcda72837d0a31d6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/droidrun
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8cf8d0bf00fe3c798062e0cddcda72837d0a31d6 -
Trigger Event:
workflow_dispatch
-
Statement type: