CLI¶
Installing the resonite-io package provides a resoio command (entry point resoio.cli:main). Commands are
flat, named by action — there are no subcommand groups (e.g. resoio mic, not
resoio voice mic). Each command maps to a modality client.
Commands¶
| Command | Modality | Direction | Notes |
|---|---|---|---|
resoio ping |
Connection | unary | Liveness check. |
resoio wait |
Connection | unary | Block until Connection.Ping succeeds (startup readiness), then print the resolved socket path. Sockets whose owning engine PID is gone are skipped. Optional pid targets resonite-{pid}.sock; -T/--timeout (default 30s, <=0 tries once) bounds the wait. |
resoio info |
Info | unary | Print mod/engine version, OS platform, Wine flag, and engine/renderer host PIDs. |
resoio record |
Camera / Speaker | Resonite → Python | Capture video and/or audio. --video / --audio filter (neither = muxed). -o - streams to stdout; -o PATH (.mp4 / .wav) writes that file; omitted writes record_<timestamp>.mp4 (.wav for --audio) to the current directory. On file save the saved absolute path is printed to stdout. |
resoio screenshot |
Camera | Resonite → Python | Save a single frame as an opaque PNG. -o PATH (.png) or -o - for stdout; omitted writes screenshot_<timestamp>.png to the current directory. On file save the saved absolute path is printed to stdout. |
resoio mic |
Microphone | Python → Resonite | Stream audio into Resonite as a virtual mic. |
resoio drive |
Locomotion | Python → Resonite | Interactive WASD driving (--sprint / --look-rate / --no-wait). |
resoio grabber |
Grabber | unary | Grab at the desktop cursor ray hit point, then operate the held / equipped item (desktop mode only). The action positional (grab / release / state / use / unuse / click / equip / dequip / interactive) is required; --hand / --radius / --button {primary,secondary} work before or after it. use holds a button down until unuse (left-click aligns / activates a tool); click is press+release; both take --strength (analog primary press pressure 0..1, default 1.0, e.g. BrushTool/Pen, ignored for the secondary button); equip / dequip handle tools. |
resoio display |
Display | unary | get prints the current snapshot; set applies a partial config and prints the post-apply snapshot. Resolution can be a preset (hd/fhd/qhd/uhd) or WIDTHxHEIGHT with an optional @FPS suffix (e.g. resoio display set fhd@30); the -W/--width, -H/--height, -F/--max-fps flags override the spec field by field (at least one of the spec or a flag is required). |
resoio world |
World | unary | List / open worlds and sessions. |
resoio context-menu |
ContextMenu | unary | Open / select the radial menu. |
resoio dash |
Dash | unary | Drive the ESC dash overlay. |
resoio inventory |
Inventory | unary | Interactive REPL: browse (ls/cd), mutate (mkdir/cp/mv/rm), spawn, and thumb (save an item's thumbnail image). |
resoio session |
Session | unary | Configure the connected session via nested subcommands: settings get/set (partial apply), users list, user kick/ban/silence/respawn/role (target with --id/--name/--self; respawn defaults to self), roles list, overrides list. |
resoio contact |
Contact | unary | Browse and manage contacts (friends) via nested subcommands: list (--search / --filter all\|accepted\|requests / --include-hidden), get, search (--exact), add (--username), accept, remove. list hides dash-hidden (ignored / blocked) contacts by default; --include-hidden shows them. The mutating ops (add / accept / remove) write the real cloud contact list. |
resoio auth |
Auth | unary | Resonite cloud sign-in via nested subcommands: login (credential positional; password via env/stdin/prompt, never a flag), logout, status. |
resoio cursor |
Cursor | unary | Set / center / get / release the desktop cursor. set and center hold the position until release. |
resoio launch |
— (umu-launcher) | local process | Start Resonite (engine + renderer) via umu-launcher and print both host PIDs. -e/--exe / RESONITE_EXE and -p/--profile / MOD_PATH select the install + mod profile; --vanilla skips the mod. --prefix (WINEPREFIX) and --proton-path (PROTONPATH, default GE-Proton) pick the Wine prefix and Proton build. --data-path / --cache-path / --logs-path relocate Resonite's database / cache / logs; any other Resonite launch option goes after -- (e.g. -- -Screen -Verbose). Never refuses when an instance is already running — run several at once by giving each a distinct --data-path (and prefix), or use --name <label> to auto-allocate an isolated tree under ~/.resonite-io/instances/<label>/ (own WINEPREFIX + data + Camera queue). Non-gRPC. |
resoio terminate |
— (signals) | local process | Force-stop a single Resonite instance by killing the engine + renderer (SIGTERM → SIGKILL). Takes [resonite_pid] [renderer_pid] (from launch) or auto-detects the single running instance. Non-gRPC. |
resoio terminate-all |
— (signals) | local process | Force-stop every running Resonite instance (SIGTERM → SIGKILL per process); prints one engine/renderer pair per instance (--format human\|json). The multi-instance counterpart to terminate. Non-gRPC. |
resoio shutdown |
Lifecycle | unary | Ask the engine to quit gracefully (Lifecycle.Shutdown). Best-effort — on Linux the engine often hangs during teardown and never exits, so follow up with terminate when you need a guaranteed stop. Prints the engine's host PID (from Info). |
record is the Resonite → Python capture command (it pulls Camera and Speaker), while mic
is its independent Python → Resonite counterpart.
launch / terminate / terminate-all are local process control (no gRPC). launch spawns
the umu-launcher chain and waits until the engine (resonite_pid) and renderer
(renderer_pid) host processes appear, printing both. It never refuses because another
instance is already running — to run several side by side, give each launch its own
--data-path (so the instances don't share a Resonite database). --name <label> is a
convenience that auto-allocates an isolated tree under ~/.resonite-io/instances/<label>/ (its
own WINEPREFIX, -DataPath / -CachePath / -LogsPath, and a private Camera IPC queue so the
instances never cross-talk on the renderer queue); explicit --prefix / --data-path still win
over the label defaults, and --name imposes no guard. Each engine binds its own
resonite-{pid}.sock, so target a specific instance with
--socket ~/.resonite-io/resonite-{pid}.sock (or RESONITE_IO_SOCKET). terminate signals one
instance's two PIDs (SIGTERM → SIGKILL); given no PIDs it auto-detects the single running
instance (and errors if it finds more than one — use explicit PIDs or terminate-all).
terminate-all stops every instance at once. Because they work from the host process table they
run before the UDS exists and regardless of whether the client is reachable.
shutdown, by contrast, is a pure gRPC call (Lifecycle.Shutdown) that asks a running
engine to quit gracefully — use it when the client is up and reachable. It is best-effort,
though: on Linux (including the dev container) FrooxEngine frequently hangs during teardown and
the engine never exits on its own, so reach for terminate when you need a guaranteed stop
(or already hold the PIDs from launch). The cooperative pattern is shutdown to ask the
engine nicely, then terminate to make sure the process is gone.
auth¶
resoio auth signs the engine in and out of the Resonite cloud account (Python → Resonite),
mirroring how a gh auth login-style flow works. It has three nested leaves:
resoio auth login [credential]— authenticate.credentialis an optional positional (username or email). The password is never a flag and never appears onargv; it is read from, in order:- the
RESONITE_IO_PASSWORDenvironment variable, - piped stdin (e.g.
printf '%s' "$pw" | resoio auth login alice), - an interactive hidden prompt (no echo) when neither of the above is provided.
- the
resoio auth logout— sign the engine out.resoio auth status— report whether the engine is logged in, and for whom. The human output renders the session expiry as a UTC datetime;--format jsoncarries both the exactsession_expires_unix_nanosand a derived ISO-8601session_expires_iso(nullwhen there is no expiry).
login flags:
--totp CODE— two-factor one-time code, when the account has 2FA enabled.--no-remember— do not persist the session. By default the login asks the engine to remember the session (remember_me=True); persistence is delegated entirely to the engine.
All three leaves accept --format human|json (see below).
Security stance. The password is never passed on the command line or written to logs — only
the env var, piped stdin, or the hidden prompt can supply it. --no-remember controls only
whether the engine persists the session; resoio itself stores no credentials and keeps no
session state. When remember_me is set, persistence is the engine's responsibility, not the
CLI's.
# Sign in (hidden password prompt; nothing sensitive on argv)
resoio auth login alice@example.com
# Non-interactive: feed the password via env or piped stdin
RESONITE_IO_PASSWORD="$pw" resoio auth login alice@example.com
printf '%s' "$pw" | resoio auth login alice@example.com --totp 123456
# Don't let the engine persist this session
resoio auth login alice@example.com --no-remember
# Inspect / tear down
resoio auth status --format json | jq .logged_in
resoio auth logout
Output format (--format)¶
Commands that return structured data accept --format human|json (default human):
humankeeps the existing human-readable text.jsonprints a single machine-readable document to stdout — proto field names insnake_case, enums as their name string, big integers (e.g.unix_nanos) exact, non-ASCII preserved.
Errors always go to stderr and the exit code signals success/failure; stdout carries only the result document.
--format is not on every command. Commands that return a single value print it raw on one
line instead of as JSON:
shutdownprints the engine host PID;terminateprints the PIDs it killed (orresonite not running). (launchdoes take--format, returning aresonite_pid/renderer_pidpair.)waitprints the resolved socket path that became ready.screenshot/record/world thumbnailprint the saved absolute path when writing a file (and-o -streams raw bytes to stdout with no path line).
Interactive commands (drive, grabber interactive, inventory) have no structured output and do not
accept --format (grabber interactive --format json exits with code 2).
Examples¶
# Liveness
resoio ping --message hello
# Block until the engine is up (max 60s), then ping it
resoio wait -T 60 && resoio ping
# Wait for a specific engine PID's socket
resoio wait 12345
# Record 10 seconds of muxed video+audio to a timestamped file in the CWD
# (the saved absolute path is printed to stdout)
resoio record --duration 10
# ... or to an explicit path / stdout
resoio record -o out.mp4 --duration 10
resoio record -o - --video | ffplay -
# Save a single frame as PNG (timestamped file in the current directory)
resoio screenshot
# ... or to an explicit path / stdout
resoio screenshot -o shot.png
resoio screenshot -o - | feh -
# Machine-readable output, piped to jq
resoio info --format json | jq .platform
resoio world sessions --format json | jq '.[].name'
resoio session users list --format json | jq '.[].user_name'
resoio contact list --filter requests --format json | jq '.[].username'
# Read the display settings, then set the resolution / fps
resoio display get
resoio display set fhd # preset -> 1920x1080
resoio display set 1280x720@60 # explicit WIDTHxHEIGHT@FPS
resoio display set fhd -F 144 # preset resolution, fps overridden by -F
resoio display set --max-fps 30 # just cap the background fps (no resolution)
# Aim with the held cursor, grab at the ray hit point, then release
resoio cursor center
resoio grabber grab --radius 0.5
resoio grabber release
resoio cursor release
# Equip a grabbed tool (e.g. a Pen), draw by holding the primary button
# while moving the cursor, then release the button and dequip
resoio grabber equip
resoio grabber use --button primary --strength 0.8 # press and hold (pen pressure)
resoio cursor set 0.4 0.5 # drag the cursor to draw a stroke
resoio cursor set 0.6 0.5
resoio grabber unuse --button primary # release the button
resoio grabber dequip
# Start Resonite (engine + renderer) and capture both PIDs
resoio launch --format json # {"resonite_pid": ..., "renderer_pid": ...}
# Pick a specific Wine prefix and Proton build
resoio launch --prefix ~/prefixes/resonite --proton-path GE-Proton
# Run two instances side by side. --name auto-allocates isolated data trees;
# or separate them yourself with --data-path. Then talk to one by its socket.
resoio launch --name a # {"resonite_pid": 12345, ...}
resoio launch --name b # {"resonite_pid": 23456, ...}
resoio launch --data-path ~/res-c/data --prefix ~/res-c/pfx # manual isolation
resoio --socket ~/.resonite-io/resonite-12345.sock ping
# Stop it — by the PIDs from launch, auto-detect the single instance, or stop every instance
resoio terminate 12345 12399
resoio terminate
resoio terminate-all --format json # [{"resonite_pid": ..., "renderer_pid": ...}, ...]
# ... or ask a running engine to quit gracefully over gRPC (prints the engine host PID)
resoio shutdown
Run any command with --help for its full flag list. For programmatic use, see the
API Reference.