At a glance
Pi agent → hopper-pi (Node/TS) → ZMQ → Hopper Code Backend (Grasshopper in Rhino)
The agent lives in Pi (Node/TypeScript). It sends commands and queries to a
small C# Grasshopper plugin (rhino-zmq-poc.gha) that runs
inside Rhino and marshals work onto Rhino’s UI thread via
InvokeOnUiThread. Edits within an agent turn are wrapped in a
single RhinoAgentTransaction so they undo as one step.
ZeroMQ channels
| Port | Pattern | Purpose |
|---|---|---|
5555 | PUB/SUB | Events: job status, canvas XML snapshots |
5556 | PUSH/PULL | Commands: edits, scripts, widgets |
5557 | REQ/REP | Queries: canvas state, component search, errors |
Connection discovery
The backend tries the legacy 5555–5557 ports
first. If any are already in use, it automatically binds a free loopback
port triplet and writes the live endpoints — plus a local connection token —
to a user-local connection profile:
| OS | Profile path |
|---|---|
| Windows | %APPDATA%\hopper-pi\connection.json |
| macOS | ~/Library/Application Support/hopper-pi/connection.json |
| Linux | ~/.local/share/hopper-pi/connection.json (or $XDG_DATA_HOME/hopper-pi/) |
The token is generated once and reused across backend/frontend restarts, so
normal restarts do not require re-pairing. Override discovery with
HOPPER_CONNECTION_PROFILE, or set endpoints manually with
GH_ZMQ_PUB, GH_ZMQ_PUSH, and
GH_ZMQ_REQ. If you point at a token-protected backend manually,
also set GH_ZMQ_TOKEN.
Repo layout
| Path | Role |
|---|---|
src/ | Pi extension: ZMQ client, tools, XML parsing |
grasshopper-plugin/ | C# Grasshopper plugin (rhino-zmq-poc.gha) |
scripts/install-grasshopper-plugin.mjs | Build + install plugin to Libraries |
mds/ | Skills, prompts, and reference docs for the agent |