Skip to main content
rebind is the command-line companion to the Rebind app: scaffold a package, validate and lint it offline, run it against your local Rebind app with live logs and hot-reload, then pack, install, and publish it. It also streams app-wide logs, manages loaded script instances, sends diagnostic reports, and manages the installed service. Standard setup installs the optional command-line tools and adds them to your PATH. Custom setup can omit them. The CLI owns the package on disk. Everything after publish, the review status, private or public, withdrawing a version, earnings, and the payout destination, lives in the app under Marketplace > Publish. See Publishing scripts. init, check, lint, pack, and preview work fully offline. service uses the operating system’s service manager and can run while Rebind is down. The other commands use the local Rebind app. whoami, hosted ai, publication, and report need a signed-in session. A publication rehearsal is offline. Marketplace install requirements are described under install.

Commands

Optional [path] arguments default to the current directory. Development commands accept a package directory or a single .lua/.luau file unless their section says otherwise. script start requires an installed script path.

init

The package name is the final path component (rebind init tools/hello creates a package named hello), lowercased. With no path the current directory becomes the package, and files that already exist are kept, so rebind init beside a main.luau you already wrote adds only the manifest around it. A directory that already holds a rebind.toml is refused. The scaffolded rebind.toml carries a commented-out [marketplace] block (summary, title, category, icon, media, and [marketplace.price]). The README.md ships inside the .rbp and becomes the listing’s overview when you publish.

check

Offline validation of the package contract: manifest schema and pricing rules, identity declarations, the full require graph, Luau compilation of every reachable module, and a permission audit. The audit covers the two permissions the runtime enforces, net (Net.*) and exec (System.Exec, System.ExecDetached). It compares uses against the entry modeline, so a script that declares exec and forgets net fails here instead of at runtime. Undeclared means all permissions, so for a script that declares none there is nothing to contradict and the comparison is inert. A package carrying a [marketplace] block is held to more: publish refuses an undeclared set, so check fails it here rather than at upload. The audit lexes the source, so a mention inside a string or comment is not a call, but it does not resolve aliases (local n = Net; n.Get()). With a [marketplace] block, check also validates the listing: the shape and caps of everything the block declares, and for drm = true the two things a sealed file cannot carry. What must merely be present (a summary, a screenshot) is publish’s concern. The rules and limits live in Package format.
Checking a script that lives inside a package shows its container:

Installed and extracted packages

Archives retain the manifest but omit its declared listing media. For an installed or extracted package directory, allow those missing files explicitly:
This option permits absent listing media only. Media declarations and files that exist are still validated, as are the code, permissions, and package limits. run keeps the same option during hot reload. Without the option, missing declared media remains an error, so author checks catch missing assets. publish always requires declared media files and does not accept this option.

lint

Advisory linting over the same require graph, with the Rebind standard library preloaded so HID, Log, hooks, and the rest of the SDK resolve. Warnings never block anything; error-severity findings (definite bugs) exit non-zero and block publish.

run

The dev loop: validates, loads the entry into the running Rebind app, streams the script’s log channel, and hot-reloads when a source file changes.
Only this script’s lines appear: its own Log.* output plus Rebind’s lifecycle notices and errors for it. --all-logs streams every Rebind log line (prefixed with its source) for app-wide debugging. Edits to .lua/.luau/ .toml files and anything under assets/ trigger a reload; a failing re-check keeps the previous run alive. The watcher owns the instance and its current runtime incarnation. An external stop or restart, a kill switch, or a changed Rebind process ends that ownership. The watcher then exits instead of starting the script again on a later edit. An older Rebind service without ownership metadata is refused before starting the script. Reload uses the restart lifecycle, so a failure after the old runtime stops does not restore it. If the package entry changes, rerun rebind run. Ctrl+C requests a stop of the owned incarnation. Force-killing the CLI, including cancelling its subprocess in rebind ai, can leave the script loaded. Use ps and script stop to inspect and stop it. Use logs to watch app-wide output without starting a script. rebind run scripts/wiggle.luau runs a single script from a package’s scripts/ directory. It inherits the package’s min_sdk, and its requires resolve from the package root. For installed directories, see Installed and extracted packages.

exec and repl

Both run chunks in a persistent scratch runtime inside the Rebind app: the full SDK with real input output, state surviving between calls.
rebind exec - reads the chunk from stdin. A Lua error exits non-zero. rebind repl is the interactive version: same runtime, TAB completion queried live from the VM (your own variables complete, not just SDK names), and unfinished chunks continue on the next line. ⌃D exits.

pack

Builds the .rbp registry artifact, a gzipped tarball of the package’s source, manifest, and README. Listing media is uploaded separately at publish and is not packed. Format details are in Package format.
--out <path> sets the output file. The default is <slug>.rbp in the current directory. The path can be any package directory, including one the app installed under its scripts folder (the location is in Quickstart). For omitted listing media, see Installed and extracted packages. Repacking excludes the root .rebind-market install identity file.

install

Puts a package into the Rebind app’s scripts tree. A .rbp file lands under packages/<name>/, with the name taken from the archive’s rebind.toml:
@handle/name installs a public marketplace listing the same way the app’s storefront does: the listing lands under marketplace/. Published free listings without source protection can be installed as a guest. Paid listings require a signed-in account entitled to them, not Pro. Protected listings also have hardware requirements. A private listing is not reachable this way; install it from its link in the app. --force replaces an existing install of the same package or listing.

publish

Check → lint → pack → upload for review. Error-severity lint findings block the upload; warnings do not. Requires:
  • a package (rebind.toml); a bare script has no listing to publish
  • a signed-in account with a claimed @handle; see publishing requirements for Developer access and source protection
  • a [marketplace] section with summary and category; title is optional and defaults to the package name
  • a [marketplace.price] table for a paid listing; omit it for a free listing
  • at least one screenshot .png in media; the first png becomes the cover
  • an optional icon, separate from the cover and gallery
The Rebind app’s authenticated session authorizes the uploads. For a plaintext listing the .rbp goes up first, followed by the cover, optional icon, and remaining gallery items in declaration order. For a DRM listing there is no archive: the require graph is bundled into one sealed file and sent with the listing, and only the media is uploaded. A README.md at the package root is sent as the listing’s overview; without one the product page falls back to summary.
A successful publish queues the version for review. Publishing scripts covers what happens after, under Marketplace > Publish in the app. rebind publish --dry-run validates the listing and prepares the archive or protected-source bundle locally, then prints the proposed terms and artifact size. It does not sign in, upload, or submit for review. Account access, server version conflicts, and review decisions remain unchecked.

preview

Loads the package manifest and opens a self-contained local HTML approximation of its marketplace listing from local package files and media, without executing the package. No sign-in or running Rebind app is required.
The preview surfaces missing or invalid listing fields, but it is not the store UI. Run rebind check before publishing for the complete package validation. rebind preview --json prints structured listing data and embedded local media without opening a browser. The desktop Assistant uses it for its in-app preview. Neither preview mode executes the package or simulates its input behavior.

ps

Lists loaded script instances, including their IDs, state, uptime, Luau heap, and average tick duration. It also reports the script sample’s age. A loaded instance can be running, in an error state, or waiting to restart.
--watch refreshes the terminal as status arrives. Without --json, it requires a terminal. --json writes one full status object, including its scripts array. Together, the flags produce newline-delimited JSON (NDJSON): one status object per line, starting with the initial snapshot. Pending updates may be coalesced, so this is a status feed, not a lifecycle event ledger. Warnings about stale status go to stderr. Ctrl+C or a closed output pipe exits successfully; a nonterminal --watch without --json exits with code 2. The per-instance heap is that VM’s Luau allocation, not process RSS or total memory attributable to the script. Tick duration is an exponential moving average in microseconds, not a CPU percentage. Rebind process memory, when available, is reported separately. Missing metrics mean unknown, not zero.

script

Controls one loaded instance through Rebind’s lifecycle manager.
start <path> requires a file in Rebind’s installed scripts tree. Use run for a development file outside that tree. stop, restart, and inspect take a selector: the exact runtime UUID, an exact path reported in file or source_path, or a unique display name. UUIDs take precedence. Names and paths must match exactly; prefixes are not selectors. If more than one instance matches, the command fails and lists IDs to choose from. These same selector rules apply to logs --script and /spy script. stop removes the selected instance without changing its autostart setting. The existing input-release safety path can release keys or buttons held by other scripts. inspect shows sampled metrics, launch origin, runtime incarnation, timer and coroutine counts, retry attempts, and any error or restart-unavailable reason. --json returns the selected script object. An incarnation identifies one runtime lifetime; restarting an instance creates a new incarnation. Restart behavior depends on the launch origin:
  • Disk-backed scripts are read again from disk and pass through current authorization checks, including checks for protected scripts.
  • Source-launched scripts replay retained entry source. Retention is limited to 1 MiB per source and 16 MiB total. Exceeding either limit does not reject the initial start; it makes replay unavailable, reported by inspect.
  • Restart is not transactional. Once the old runtime has stopped, a failure to load the replacement leaves an error state rather than restoring it.

logs

Streams new log lines from Rebind and every running script. Each entry includes the level, source component, and local time when the CLI receives it, not the time the event originally occurred.
--level sets the minimum level: debug, info, warn, or error. With no filter, all received levels appear. debug also includes trace lines from the VM. Relay debug lines require the Rebind service to run with RUST_LOG=debug; script Log.Debug lines are published independently of that filter. --script <selector> resolves a loaded instance using the selector rules and filters by its runtime ID carried on log records. This is attribution, not a text search over the source label or message. Lines without a matching ID are excluded. An instance without runtime attribution metadata is refused. Without --script, all sources remain visible. There is no history replay. If Rebind cannot be reached at startup, the command prints the directory for earlier Rebind log files, or a journalctl command for a Linux systemd install. Script log history is not retained by Rebind. Early startup messages are written to the Rebind log file before live publishing is available. After connecting, reconnection across a Rebind restart is best effort. Lines emitted while disconnected are not recovered. Ctrl+C stops the stream.

macro record

Create a temporary Luau helper that prepares a foreground window and records through the relay’s shared macro recorder. By default, a new mouse-button press starts the take:
--window is a case-insensitive substring of the foreground window title. --start-key accepts Mouse1 through Mouse5 and defaults to Mouse1. --stop-key accepts F1 through F24 and defaults to F1. Choose a stop key different from the global recorder hotkey. --name defaults to recording. Before arming, the helper restores the window’s saved position and size and moves the cursor to desktop coordinates (0, 0). It verifies both before accepting the start button. Release any held mouse buttons during setup. The first successful setup captures the window’s current bounds; the command saves that layout for subsequent recordings matching the same title pattern, including after graceful cancellation. To choose a different layout, pass --bounds 60,80,1000,700 for x, y, width, and height. A layout or cursor that cannot be verified prevents recording from starting. If the window moves or resizes while armed, setup runs again before a new press can start the take. The pointer reset is setup, not part of the macro. Add --start-on-focus to begin recording automatically after setup succeeds, without waiting for a mouse-button press. This includes the movement from (0, 0) to your first drawing point. F1 still finishes the take by default. By default, the first new button-down while the window is foreground starts the take and is included in it. The stop key finishes the take even after focus changes; its press and release are excluded from the recording. Release the stop key to finish the helper. Pressing it before recording starts cancels the helper. Another caller’s recording is never replaced. The command reports progress and returns the saved macros/*.luau path. The relay chooses a free filename. Temporary helper files are removed after completion or graceful cancellation. If saving fails, the command reports the retained take’s path. Ctrl+C stops the helper; a cancelled take is discarded. Force-killing the CLI can leave its helper running, as with rebind run. This requires input capture and foreground-window support on the host. Window bounds use native desktop coordinates. On macOS, the menu bar can prevent a normal window from reaching y=0; use its actual allowed top position in --bounds. Cursor (0, 0) remains the desktop origin. A relay started with --no-capture cannot observe the trigger keys. Linux foreground-window integration is not implemented. For supervising this command from the app, see Assistant event waits and recovery.

report

Sends a diagnostic report through the running, signed-in Rebind app. See Send logs for review for the sending procedure, confirmation, and bundle contents.

service

Checks or controls the installed Rebind service through the operating system’s service manager. It does not install a missing service.
status reports TCP reachability. On macOS and Linux it also reports service-manager state; on Windows it names the scheduled task. It honors REBIND_PORT_OFFSET; service-manager state still refers to the installed service and does not prove which process owns the responding ports. start, stop, and restart act only on the installed service. They refuse to run if REBIND_PORT_OFFSET, REBIND_DATA_DIR, or ZENOH_ROUTER_ENDPOINT is present, even if its value is empty or zero. Unset these variables before managing the installed service. An occupied Rebind TCP port can prevent a start; stop a manually launched copy where it runs. stop does not disable auto-start. On macOS, Rebind starts again at your next GUI login. On Linux, it starts again at the next boot. start brings it back sooner. On macOS, restart unloads the agent, waits for it to unload, then loads it again. Linux service changes use systemctl --no-ask-password, so the CLI does not open a password prompt. Service-manager errors reach the terminal. If access is denied, rerun the command with sudo. On WSL without a Rebind systemd unit, use a Windows shell for the Windows commands.

Troubleshooting exit codes

These codes apply to logs, report, and service:

whoami

mcid is your registered Rebind Link, shown as the same short id the app displays, with its live attachment state; transport is the session mode (hardware when your device drives output, software otherwise).

ai

For the app’s separate conversation and execution controls, see Use Assistant. A terminal coding agent for the scripts in the current directory. It reads and edits files, validates them with check and lint, runs chunks with exec, and calls other subcommands, asking before it writes a file or runs a command. Its CLI tool refuses report; run that command yourself to review what is sent. The model runs through your Rebind session: the app must be running and signed in, and the server puts the full SDK documentation in front of every request, so the agent writes against the real API.
Two models: default and pro, picked with --model or /model inside the session. Both models draw on one daily AI budget for your account. The status bar shows how much of today’s budget is used; it resets at 00:00 UTC. Your AGENTS.md in the directory, if present, is given to the agent as standing instructions. Inside the session: Enter sends, Alt+Enter or a trailing \ adds a line, Esc cancels, Ctrl+C quits. /plan <task> asks for a step list to approve first, /run [path] runs a script with live logs, /init <name> scaffolds a package, /resume reloads the last session from .rebind-ai/session.json, !cmd runs a shell command. /help lists commands and keyboard controls. Left and Right move the prompt cursor, Home and End move within its line, and Delete and Backspace remove characters around the cursor. Up and Down recall history, then restore your unsent draft. Paste inserts at the cursor. A busy turn or a used-up AI budget keeps your draft in the prompt. Change models after the current turn finishes or after cancelling it. Confirmation previews scroll with PgUp/PgDn or the mouse wheel. Up and Down select a choice, Enter confirms it, and Esc dismisses the preview. Choices stay visible while you scroll through a long file, diff, or command. --ollama <url> or --openai <url> use a local or third-party model instead of your Rebind session; those receive no documentation from the server and rely on the lua_api and example tools.

Runtime inspector

Spy opens and starts observing automatically in each AI session. It shows script status, system context, recent logs, and normalized input while the Rebind app is running. Wide terminals use a sidebar; narrow terminals keep a compact summary above the prompt. The panel stays visible during confirmations and when observation is paused or off. In hardware mode, Spy shows the number of peripherals reported as attached, including zero. Devices that are only shared or visible to the host are not counted. Failed USB queries show unavailable; old observations show stale. Software mode has no hardware peripheral count. Input stays local unless you approve /spy share. Approval queues that excerpt for your next user turn, including a /plan request. That turn sends it to the selected model provider and saves it as chat in .rebind-ai/session.json. /spy clear and /spy off discard a pending attachment, but do not remove previously shared messages. The live observation rings themselves are ephemeral and are not saved or restored by /resume. While the inspector is enabled, the model can call runtime_context without another confirmation to read script and system status and recent logs. It cannot read the input ring through that tool. Tool results enter the conversation and saved session. Each user turn has a shared 16 KiB evidence budget for input attachments and all runtime_context results, so repeated calls do not grant another full budget. Requests that cannot fit the remaining budget fail.

Observation limits

Older records are evicted as buffers fill. Gaps, evictions, and omitted records mean the view is incomplete. Status age and input age describe observations, not a guarantee that the runtime is still in that state. Input records describe normalized events at capture admission, not physical USB packets or a typed-text transcript. Consecutive movement or scroll events may be aggregated. Outcomes describe dispatch decisions and attempts, such as suppression or attempted reinjection; they do not prove that a target application received the input. Pending input stays visible while dispatch is blocked. Missing key state after a gap is unknown, not released. Input observation uses an owner-specific loopback HTTP lease. Only one inspector can hold it at a time. Without renewal, it expires after five seconds and clears the relay’s input buffer. The raw trace is not published over Zenoh.

ui

On Windows with the default URL, opens the installed Rebind Desktop app when that optional component is present. Otherwise it opens the control panel with an installed Chromium browser’s app mode, falling back to an ordinary browser tab when needed. --url defaults to http://127.0.0.1:19480 and must stay on loopback: the app’s WebSocket guard only accepts localhost, 127.0.0.1, and ::1 origins.