Skip to content

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.

resoio --help

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 (SIGTERMSIGKILL). 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 (SIGTERMSIGKILL 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 (SIGTERMSIGKILL); 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. credential is an optional positional (username or email). The password is never a flag and never appears on argv; it is read from, in order:
    1. the RESONITE_IO_PASSWORD environment variable,
    2. piped stdin (e.g. printf '%s' "$pw" | resoio auth login alice),
    3. an interactive hidden prompt (no echo) when neither of the above is provided.
  • 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 json carries both the exact session_expires_unix_nanos and a derived ISO-8601 session_expires_iso (null when 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):

  • human keeps the existing human-readable text.
  • json prints a single machine-readable document to stdout — proto field names in snake_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:

  • shutdown prints the engine host PID; terminate prints the PIDs it killed (or resonite not running). (launch does take --format, returning a resonite_pid / renderer_pid pair.)
  • wait prints the resolved socket path that became ready.
  • screenshot / record / world thumbnail print 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.