Luotsi is a host-driven CLI for Android device automation, inspection, and live view. It runs on the engineer or CI machine, talks to real devices over ADB, and returns structured JSON results plus artifacts. Orchestration, policy, and diagnostics stay on the host; the on-device helper stays thin and purpose-built.
- Run a command — every command returns one JSON envelope with
ok,data,artifacts, anderror. No third-party device server required. - Run a scenario — drive multi-step device flows from a small JSON playbook. Steps are validated, templated, and timed; failures produce artifact bundles automatically.
- Inspect mode — open a JSONL session for agent-driven exploration. Luotsi emits screen snapshots and diffs so an agent can reason about the UI and act without a scenario file.
- Live view — stream a mirrored device display to a local SDL window with an operator control layer, hotkeys, and JSONL events for agents consuming stream state.
- Telemetry — parse structured
LUOTSI_DEVICE_TELEMETRYevents from logcat for semantic waits and assertions. - CI-friendly — same binary, same output shape for engineers, CI pipelines, and agent-driven flows.
Download a self-contained archive from GitHub Releases. Each archive contains a single luotsi executable (luotsi.exe on Windows) with no separate .NET runtime required.
# macOS / Linux
./luotsi devices
# Windows (PowerShell)
./luotsi.exe devicesSource builds. The repo is pinned to .NET SDK 10.0.300 (see global.json):
dotnet run --project Luotsi.Cli -- devicesBuild and test:
dotnet build Luotsi.sln
dotnet test Luotsi.sln| Path | Contents |
|---|---|
Luotsi.Cli/Cli/ |
Entrypoint, command dispatch, help text, inspect-mode session loop |
Luotsi.Cli/Hosts/Android/ |
Android transport and device interaction runtime |
Luotsi.Cli/Artifacts/ |
Artifact session management |
Luotsi.Cli/Models/ |
Shared records, envelopes, screen and scenario data models |
Luotsi.Cli/Scenarios/ |
Scenario execution flow and failure plumbing |
Luotsi.Cli/Telemetry/ |
Telemetry parsing contracts and logcat parser |
Luotsi.Cli/Errors/ |
Typed command and wait exceptions |
Luotsi.Cli/Infrastructure/ |
Interfaces and default system-backed implementations |
Quick reference. See docs/commands.md for flags, retry behavior, and wireless pairing details.
| Command | Description |
|---|---|
devices |
List adb-visible devices |
adb server-status |
Host ADB server status |
adb version |
ADB binary version |
adb features --device <serial> |
ADB feature set for a device |
adb mdns check |
mDNS availability check |
wait-for-device --device <serial> |
Wait for device readiness |
adb reconnect offline |
Reconnect an offline ADB transport |
preflight --device <serial> --package <app.id> |
Device preflight check |
screen-state --device <serial> |
Dump current screen state |
| Command | Description |
|---|---|
view --device <serial> [options] |
Open live streaming mirror (JSONL session) |
view --profile <name> |
Open view using a saved profile |
view --last |
Reopen the last successful view session |
reconnect |
Reconnect using the last successful profile |
view-doctor --device <serial> |
Diagnostic report without opening a stream |
profile-list |
List saved profiles |
profile-delete --name <name> |
Delete a saved profile |
| Command | Description |
|---|---|
wireless --device <usb-serial> |
Switch a USB device to TCP/IP mode (Android ≤10) |
wireless-scan |
Discover TLS pairing and connect services via mDNS |
wireless-pair --endpoint <host:port> --code <code> |
Pair a device for wireless debugging (Android 11+) |
wireless-connect --service <name> |
Connect to a paired device and return its selector |
| Command | Description |
|---|---|
forward --local <endpoint> --remote <endpoint> |
Forward host port → device port |
forward-list |
List active forwards |
forward-remove --local <endpoint> |
Remove a forward |
reverse --remote <endpoint> --local <endpoint> |
Forward device port → host port |
reverse-list |
List active reverses |
reverse-remove --remote <endpoint> |
Remove a reverse |
| Command | Description |
|---|---|
start-app --package <app.id> [--activity <activity>] [--wait] |
Launch an app |
start-uri --uri <uri> [options] |
Launch a URI intent |
force-stop --package <app.id> |
Force-stop an app |
clear --package <app.id> |
Clear app data |
wait-for-activity --activity <pattern> |
Wait for activity in the foreground |
wait-for-not-activity --activity <pattern> |
Wait for activity to leave the foreground |
is-app-installed --package <app.id> |
Check if a package is installed |
list-installed-packages [--third-party] |
List installed packages |
grant-permission --package <app.id> --permission <permission> |
Grant a runtime permission |
revoke-permission --package <app.id> --permission <permission> |
Revoke a runtime permission |
| Command | Description |
|---|---|
telemetry-tail --device <serial> --tail <n> |
Snapshot recent telemetry from logcat |
telemetry-watch --device <serial> --timeout-sec <n> |
Collect telemetry over a bounded window |
wait-log --device <serial> --contains <text> --timeout-sec <n> |
Wait for a matching logcat line |
tap-text --device <serial> --text <text> |
Tap a UI element by visible text |
wait-step --device <serial> --step <name> |
Wait for a semantic step telemetry event |
wait-action-ready --device <serial> --action <name> [--step <name>] |
Wait for a semantic action-ready telemetry event |
| Command | Description |
|---|---|
run --device <serial> --file <path> |
Execute a JSON scenario playbook |
inspect --device <serial> |
Open an agent-driven JSONL inspection session |
view is a long-lived JSONL session that mirrors a connected device to a local SDL window. See docs/view-session.md for the full reference.
Key flags: --preset <name> (low-latency / balanced / high-quality / safe), --capture-backend <auto|screenrecord|mediaprojection>, --save-profile <name>, --record <file>, --share-bind <host:port>, --read-only.
The SDL window has a clickable toolbar, multi-device shelf, and hotkeys (F1–F12, Ctrl+V, drag-and-drop). Full hotkey and JSONL event tables are in docs/view-session.md.
inspect opens a JSONL session for agent-driven exploration without a scenario file. Startup emits session_started and an initial screen_snapshot; state-affecting commands emit command_result followed by a screen_delta.
luotsi inspect --device 192.168.0.134:5555Send one JSON command per line:
{"id":"1","command":"refresh"}
{"id":"2","command":"tap_text","text":"Sign in","timeout_sec":10}
{"id":"3","command":"telemetry_tail","tail":200}
{"id":"4","command":"exit"}Available inspect commands: refresh, tap, tap_text, wait_visible, type_text, keyevent, telemetry_tail, telemetry_watch, exit.
Scenarios are JSON playbooks. See docs/scenarios.md for the full format, template syntax, and action reference.
{
"name": "android-home-smoke",
"steps": [
{ "name": "go home", "action": "keyevent", "code": "KEYCODE_HOME" },
{ "name": "let launcher settle","action": "sleep", "milliseconds": 750 },
{ "name": "capture screenshot", "action": "takeScreenshot", "label": "android-home-smoke" }
]
}Template syntax: ${env:NAME}, ${env:NAME|fallback}, ${var:name}, ${now:HHmmss}.
Generic examples: examples/scenarios/android-home-smoke.json, examples/scenarios/android-navigation-smoke.json.
Every command returns a single JSON envelope:
{
"schema": "luotsi-command.v1",
"ok": true,
"command": "screen-state",
"data": {},
"artifacts": {
"artifact_root": "/tmp/luotsi/..."
},
"error": null
}Scenario run commands return the scenario result inside data, including per-step timing and top-level overhead:
{
"scenario": "android-home-smoke",
"status": "passed",
"timing": {
"total_ms": 86361.4686,
"prologue_ms": 655.9714,
"steps_ms": 85701.7421,
"non_step_ms": 659.7265
},
"steps": []
}Every command that reaches the device writes artifacts to a dedicated artifact root. Failures produce a bundle automatically:
device-fingerprint.json— written bypreflightand scenario runswait-log.txt/wait-log.json— log streaming waitstelemetry-tail.txt/telemetry-tail.json— telemetry snapshotstelemetry-watch.txt/telemetry-watch.json— bounded telemetry collection- Failure bundles — screenshot, logcat, screen-state, hierarchy, and metadata when a runtime command fails after reaching the device
| Doc | Contents |
|---|---|
| docs/commands.md | Full command reference with flags, retry behavior, wireless pairing |
| docs/view-session.md | Presets, backends, profiles, hotkeys, JSONL events, sharing |
| docs/scenarios.md | Playbook format, template syntax, all actions |
| docs/architecture.md | System architecture and component flow |
| docs/subsystems.md | CLI, host automation, scenario, view, and telemetry subsystems |