- Go 98.3%
- Nix 1%
- Shell 0.5%
- Swift 0.2%
Separate agent_spawn into coding-agent lifecycle (configurable types via ~/.config/nro-node/agents.json) vs display_spawn for GUI apps on remote displays. Add agent_tell, agent_clear for session communication. Add unified tmux MCP tool with send/read/scroll/list/resize/split/select actions and matching CLI commands. Add OCR-based find/click_text interact actions for remote X11. Filter SSH certificate noise in stderr. Update all MCP prompts for new tool names and add agent-session, tmux-control prompts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| cmd/nro-node | ||
| helpers | ||
| internal | ||
| scripts | ||
| vendor | ||
| .gitignore | ||
| flake.lock | ||
| flake.nix | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
| VERSION | ||
nro
A wrapper CLI for orchestrating virtual displays, headless agents, and tool interactions across your hosts.
nro ties together BetterDisplay, X11, tmux, SSH, xdotool, axcli, and FFmpeg behind a single CLI so that AI agents can spin up displays, launch apps, interact with GUIs, and record screens on any node in your mesh — local or remote, same commands.
It was born from a practical need: I have a Hetzner NixOS instance and a local NixOS server, and I need to manage headless agents on them — create X11 displays over VNC, spawn tools, screenshot results, all without being physically there. On Darwin, BetterDisplay (paid license) adds virtual displays so agents can test GUI apps without touching your physical screens. That part is an extra — the core is about giving agents eyes and hands on remote hosts, switching to edge computing when needed or delegating to a remote node.
What's in the box
- Virtual displays — create, show, hide, resize, screenshot. BetterDisplay on macOS,
x11-managerdaemon on NixOS/Linux. Same CLI on both. - Agent sessions — named tmux (local) or
x11-manager(remote) sessions, each bound to a display. - App interaction — keyboard, mouse, accessibility automation.
axclion macOS,xdotoolon Linux. - Screen recording — FFmpeg-based capture of any display (AVFoundation on macOS,
xwdon Linux). - VNC tunneling — SSH tunnel lifecycle + viewer launch, for when you actually want eyes on a remote session.
- Host management — list configured hosts, inspect displays/agents/tunnels per host.
- Multi-host — everything works local or remote transparently via
[host] <name>convention.
Architecture
nro is the project. nro-node is what runs on each host.
nro-node ← Single binary, runs on every host.
├─ darwin support ← BetterDisplay + axcli + AVFoundation
└─ nixos/linux ← x11-manager + xdotool + xwd
Platform differences are handled internally — the CLI surface is the same everywhere.
Quick start
Install
Nix (NixOS / Linux / macOS):
# Run directly
nix run git+https://git.pondi.app/nro/nro-node
# Or add to your flake inputs
{
inputs.nro-node.url = "git+https://git.pondi.app/nro/nro-node";
}
Host setup
Each host needs nro plus its platform dependencies:
NixOS (server):
# In your flake — the x11-manager daemon handles display/agent lifecycle
{
inputs.nro-node.url = "git+https://git.pondi.app/nro/nro-node";
# then in your config:
services.nro-node.enable = true;
}
macOS (optional — for local virtual displays):
# BetterDisplay for virtual displays (paid license)
brew install betterdisplay
# axcli for accessibility-based app interaction
cargo install axcli
First commands
# Host management
nro host ls # list configured hosts
nro host show hetzner # inspect host details + agents + tunnels
# List all displays and agents across hosts
nro ls
nro ls hetzner # filter to one host
# Virtual displays
nro display create my-display # local (BetterDisplay)
nro display create hetzner my-display --geometry 1920x1080 # remote (X11)
nro display show my-display # connect local display
nro display hide my-display # disconnect local display
nro display screenshot my-display # local screenshot
nro display screenshot hetzner my-display ~/shot.png # remote screenshot
nro display geometry hetzner my-display 2560x1440 # resize remote display
# Agent sessions
nro agent create my-agent # local tmux session
nro agent create hetzner my-agent --geometry 1920x1080 # remote via x11-manager
nro agent info hetzner my-agent # session details
nro agent spawn my-agent firefox # run tool locally
nro agent spawn hetzner my-agent firefox # run tool remotely
nro agent expose hetzner my-agent # expose agent to VNC
nro agent hide hetzner my-agent # hide from VNC
nro agent destroy hetzner my-agent # tear down
# Attach to tmux (local or remote, fzf picker)
nro attach
nro attach hetzner my-agent
# VNC tunneling
nro start hetzner 1 # tunnel + viewer to display :1
nro start hetzner my-agent # tunnel + viewer to agent's display
nro status # active tunnels
nro stop # tear down all tunnels
nro stop hetzner # tear down tunnels for one host
# Screen recording
nro record screen start my-display --fps 30
nro record screen start hetzner :1 --fps 10 --duration 60
nro record screen stop my-display
nro record screen status
# App interaction (macOS axcli / Linux xdotool)
nro interact Safari snapshot
nro interact Safari click 'AXButton[title="Send"]'
nro interact hetzner my-agent key Return
nro interact hetzner my-agent type "hello world"
nro interact list-apps # list accessible apps (macOS)
MCP integration (Claude Code)
nro ships an MCP server that exposes every tool above, plus six workflow prompts.
Connecting
claude mcp add nro -- nro mcp
Or in your Claude Code config:
{
"mcpServers": {
"nro": {
"command": "nro",
"args": ["mcp"]
}
}
}
Tools
| Tool | Description |
|---|---|
display_ls |
List displays (local BetterDisplay or remote X11) |
display_create |
Create a virtual display |
display_show |
Connect/show a virtual display |
display_hide |
Disconnect/hide a virtual display |
display_destroy |
Remove a virtual display |
display_screenshot |
Screenshot a display (base64 PNG locally, remote path) |
display_geometry |
Set remote X11 display resolution |
agent_ls |
List agent sessions |
agent_create |
Create an agent session |
agent_destroy |
Destroy an agent session |
agent_spawn |
Run a command in an agent session |
agent_info |
Show agent session details |
interact |
Drive apps via axcli (macOS) or xdotool (Linux) |
host_ls |
List all configured remote hosts |
host_show |
Show host details: config, displays, agents, tunnels |
Prompts
| Prompt | When you'd use it |
|---|---|
observe |
Screenshot a display and describe what's visible. |
deploy-verify |
Deploy something, screenshot the result, verify visually. |
interact-app |
Drive a GUI app end-to-end (click, type, wait, assert). |
orchestrate |
Coordinate multiple agents across hosts for a larger task. |
provision |
Set up a full environment: display + agent + tool in one shot. |
watchdog |
Periodically observe a display, alert on anomalies. |
Example flow
Ask Claude Code:
"Spin up a fresh display on hetzner, open Firefox, navigate to our staging URL, screenshot, and tell me if the header renders correctly."
Claude reaches for provision, creates the X11 display, spawns an agent, launches Firefox via agent_spawn, screenshots with display_screenshot, and reports back.
Status
nro is v0.x and the API will break. Use it, file issues, expect churn. If you want stability, pin a commit.
License
MIT. See LICENSE.
Copyright © 2026 Lenora