platforms
Every desktop, and some that aren't
The same commands work on X11 and Wayland, macOS, Windows 11 and Android, and they reach other machines over SSH or from WSL. Where a platform needs a helper tool, Tendril says which; where it cannot do something yet, the result says so.
What runs where
| Platform | Discover | Capture | Input | Elements |
|---|---|---|---|---|
| Linux, X11 | native | native | native XTest | AT-SPI, else the window tree |
| Linux, Wayland | hyprctl, swaymsg, wlr-randr | desktop portal; grim as fallback | ydotool; wtype for keys only | AT-SPI |
| macOS | osascript | screencapture | osascript | AX |
| Windows 11 | native | native | native | native Win32 |
| Android | ADB | screencap | input | UIAutomator |
"Native" means inside the binary, with no subprocess. Cameras use ffmpeg everywhere: AVFoundation on macOS, V4L2 on Linux, DirectShow on Windows. Audio uses pw-record or parecord on Linux and afrecord on macOS; on Windows it is probe-only for now. When no input helper is installed on Wayland, run fails with a structured unsupported_capability error rather than doing nothing.
Linux
On X11 everything is built in: discovery through XRandR and EWMH, capture, and focus, keyboard and pointer through XTest. Wayland has no single portable path, so Tendril uses each compositor's own tools for discovery and the desktop portal for screenshots, and documents the supported combinations. Input on Wayland goes through ydotool (which needs its daemon) or, for keys only, wtype.
macOS
macOS gates screen capture, input, microphone and camera behind permissions. tendril permissions reports all four with remediation steps and changes nothing; --request, in a foreground session, shows the system prompts and opens the right System Settings panes.
tendril permissions --jsontendril permissions --request
Windows
Windows 11 support is native inside the binary: monitor and window enumeration, GDI capture, focus transfer, keyboard and mouse input, and Win32 control enumeration for list-elements. Audio capture is probe-only until a WASAPI recorder lands.
Android
--android SERIAL drives a phone or emulator over ADB; --android auto picks the only connected device and fails clearly when there are none or several.
tendril --android emulator-5554 list --jsontendril --android emulator-5554 list-elements --jsontendril --android auto capture -o screen.pngtendril --android emulator-5554 run 'launch("com.example"),assert_visible("Ready")' tendril --android emulator-5554 run 'notifications(),wait(500ms),back()'
| Run action | On Android |
|---|---|
lclick(x,y), dblclick | One or two input tap |
drag, scroll | input swipe |
send("…"), plain text | input text |
Return, Back, Home, volume keys, key codes | input keyevent |
tap_text, tap_desc, tap_resource | Tap by text, content description or resource id from the UI dump |
scroll_until("Done") | Swipe and re-read until the selector is visible, then tap it |
assert_visible, assert_absent | Check the UI without touching it |
launch("pkg") | Start or switch to an app |
back(), home(), recents(), notifications(), quicksettings() | System navigation |
Every Android run leaves a debug trail (the commands, the UI dump and the screenshot) in a per-run directory, or in TENDRIL_ANDROID_ARTIFACT_DIR.
Remote machines and WSL
tendril --remote user@host --json listtendril --remote user@host --window ID run 'send("hello")'tendril --wsl-tunnel --json listtendril --remote user@host --wsl-tunnel --json list # a Windows host behind a remote WSL
- The remote needs
tendrilon itsPATH, orTENDRIL_REMOTE_BIN. On Linux the bootstrap fixes non-login shells: it adds package-manager paths, ignores forwarded displays that point back at the client, and discovers the session's runtime directory, bus and display sockets. - For MCP, standard input and output are streamed through untouched, so JSON-RPC framing survives. SSH failures in ordinary commands come back as structured error envelopes.
- From WSL,
--wsl-tunnelrunstendril.exeon the Windows host, from itsPATHorTENDRIL_WSL_WINDOWS_BIN.
A disposable desktop
For browser and desktop work that must not touch a person's screen, tendril-headless (shipped in the package, and as scripts/tendril-headless.sh) starts an isolated 1920 × 1080 X11 session with XTest, a lightweight window manager and a browser, and prints the variables to point Tendril at it. It is process-scoped, keeps only lifecycle metadata under the runtime directory, and has smoke tests for clipboard, Unicode, drag, hover, scroll and file-upload behaviour. The captures on the home page came from one.
tendril-headless --name site --display 151 start[tendril-headless] starting Xvfb :151 at 1920x1080x24[tendril-headless] starting window manager: openbox[tendril-headless] starting browser: …/bin/chromiumexport DISPLAY=:151export XDG_SESSION_TYPE=x11tendril-headless --name site stop
Runtime dependencies
A source-backed audit lists every external tool Tendril calls, per command and platform, and classifies each as self-contained, a documented prerequisite or a compatibility fallback, with a direction for replacing it. Linux X11 and Windows are self-contained today; macOS relies on tools that ship with the system; Wayland and cameras rely on documented helpers that the Nix package can supply.