overview
Keystrokes, cast anywhere.
omni sends text, keys, combos and scrolls from one command line to wherever the omni keyboard is typing: an Android phone, an iPhone, a watch, or a desktop. Every keystroke becomes one line of the same small wire, so scripts, macros and agents can drive a device the same way a person would.
- text "hello"
- enter
- ctrl,a,g
- scroll -3
- wait 150
4transports: adb, TCP, a relay hub, a desktop daemon
1wire: versioned NDJSON, shared by every receiver
10MCP tools, including send and devices
5replay backends: xdotool, wtype, ydotool, osascript, and a log-only mode
What it is for
- Typing on a device you are not holding
- Fill a login on a phone, scroll a document on a tablet, or press Enter on a watch from a terminal, a script or a keyboard shortcut on another machine.
- Devices that cannot listen
- watchOS cannot host a server, and iOS keyboard extensions only run while open. They dial out to a relay hub, which
omni serveruns anywhere, and senders cast through it with a delivery receipt. - Repeatable sequences
- Macro files mix semantic keystrokes with
waitdelays for user-interface transitions, and preview exactly what they would do with--dry-run. - Agents with hands
- An MCP server exposes
sendanddevices, so an agent can type into a device and see which devices are connected, not just read status. - Events that are not keys
- Applications can publish named button events (
down,up,trigger,cancel) over the same routes without ever injecting a keystroke.
A first cast
With no target configured, omni prints the wire line instead of sending it, so you can see what every receiver gets:
omni send text "hello"{"v":1,"type":"text","text":"hello"} omni send key enter{"v":1,"type":"key","key":"enter"} omni send combo "ctrl,a,g" {"v":1,"type":"combo","combo":"ctrl,a,g"} omni send scroll -3{"v":1,"type":"scroll","amount":-3}
Point it at an Android device and the same commands become broadcasts to the omni keyboard. --dry-run shows them without running anything. A combo becomes one key event per letter, with the Ctrl modifier held:
omni send --adb --dry-run combo "ctrl,a,g" adb shell 'am broadcast -a com.harryaskham.omni.INJECT --es type key --ei keycode 29 --ei meta_state 4096 --es sender_package com.harryaskham.omnicli'adb shell 'am broadcast -a com.harryaskham.omni.INJECT --es type key --ei keycode 35 --ei meta_state 4096 --es sender_package com.harryaskham.omnicli'
Every example on this site was run against a scratch configuration, with a relay and daemon on local ports and the daemon in log-only mode.
Where to go
- GuideInstall, send and cast, write macros, name targets and groups, check health.
- RelayThe hub devices dial into, delivery receipts, the replay daemon and its subscribers.
- WireThe NDJSON commands, Android intents, application events and the SDK.
- AgentsThe MCP server, feedback that opens issues, doctor checks and self-update.
- ReferenceCommands and options, configuration, exit codes, service modules, development.
- APIThe Rust API documentation, generated from the crate.