Wake and status control for remote machines — homelab boxes, GPU workstations — from the Paseo daemon: roster UI plus embedded MCP tools (power_status, power_wake, power_job_status) so agents can wake a host before running work on it.
Supported platforms: macOS, Linux.
npmjs · Paseo 0.9+
paseo plugin add npm:paseo-remote-power@0.1.0GitHub · Paseo 0.8 fallback
paseo plugin add georgepullen/paseo --ref fde9912cf5636761ae580eb1d47d33ab70d3de93 --path plugins/paseo-remote-powerGit security status and fallback installation are pinned to commit fde9912cf563.
From the plugin's README
Install it like any Paseo plugin (from the plugin directory or via the paseo
CLI), then add your first host on the Remote power surface. Requirements:
paseo >= 0.10.0, node >= 18.
README
Wake and status control for remote machines — homelab boxes, GPU workstations, lab servers — from the Paseo daemon.
The plugin keeps a small roster of hosts. Each host has an ordered list of wake
transports (generic: an HTTP POST endpoint, a Wake-on-LAN magic packet, or a
local command), a reachability probe (ssh BatchMode/ConnectTimeout, or a
custom status command that answers by exit code), and a bounded wake window
(default 110s). Waking is idempotent: if the probe already says up, nothing is
sent. You get:
power_status, power_wake, power_job_status
— so an agent can wake its machine before running work on it;Install it like any Paseo plugin (from the plugin directory or via the paseo
CLI), then add your first host on the Remote power surface. Requirements:
paseo >= 0.10.0, node >= 18.
| Field | Type | Meaning |
|---|---|---|
id |
string | Stable id (host-xxxxxxxx, generated). |
name |
string | Display name; unique per roster. |
sshTarget |
string? | ssh alias or user@host used by the reachability probe. |
statusCommand |
{command, args?}? |
Optional probe run on the daemon host; exit 0 = up. Wins over ssh. |
wakeWindowSeconds |
number? | Per-host wake window override (10–3600, default from settings). |
wakeTransports |
array | Ordered wake transports; first success wins, then verification starts. |
Transports:
| Type | Fields | Behavior |
|---|---|---|
http |
url, tokenRef? |
POST to the endpoint; when tokenRef is set, the matching bearer token from the plugin state dir is sent as Authorization: Bearer …. |
wol |
mac, host? |
Standard 102-byte magic packet over UDP (port 9). host targets a subnet-directed or relay address; default is broadcast. |
command |
command, args? |
Runs a local command on the daemon host; exit 0 = accepted. |
microlink |
host, mode?, secretRef?, port?, scheme?, headerPrefix? |
HMAC-signed wake to a wake authority on an ESP32-S3: a signed POST /v1/wake over HTTP, or one signed UDP datagram on the tailnet (mode: "udp"). Defaults: mode http, port 48320. secretRef names the hex secret in the plugin state dir; the secret never appears in a host record. The authorization scheme and header names are configurable per host (defaults Microlink-HMAC / X-Wake-; older authorities may override scheme and header names per host). |
Everything lives in the Paseo plugin state dir
(~/.paseo/plugin-data/xpufx/paseo-remote-power/) at runtime — never in this
repository. Placeholder shapes:
{
"hosts": [
{
"id": "host-1a2b3c4d",
"name": "lab-gpu-box",
"sshTarget": "lab-gpu-box",
"wakeWindowSeconds": 110,
"wakeTransports": [
{ "type": "http", "url": "http://192.0.2.10:8080/wake", "tokenRef": "lab-gpu-wake" },
{ "type": "microlink", "host": "wake.tailnet.net", "mode": "udp", "secretRef": "wake-secret" },
{ "type": "wol", "mac": "00:11:22:33:44:55", "host": "192.0.2.255" },
{ "type": "command", "command": "/usr/local/bin/wake-lab-gpu", "args": ["--port", "9"] }
],
"createdAt": 1767000000000,
"updatedAt": 1767000000000
}
]
}
Bearer tokens are stored only in tokens.json in the same state dir,
keyed by tokenRef. The roster form's write-only "token" field stores one for
the host and wires it to every http transport without an explicit ref;
hosts.json itself never contains secrets. Microlink hex wake secrets work
the same way: secretRef names a key in tokens.json, resolved only when a
wake runs, and never logged or echoed.
statusCommand wins over the microlink observer, which wins over ssh: a host
with a microlink transport is asked first via its unsigned
GET /v1/observer (target_awake: true = up; no answer falls through to
ssh), so the device's own wake authority settles the question without an ssh
round-trip.
up / alreadyUp.no-transport — nothing accepted the wake
request; never-reachable — a transport accepted the burst but the host
never came up inside the window. These are reported per job (error.mode)
so an agent can tell "the wake path is broken" from "the machine is sick".maxConcurrentWakes jobs run at once;
a host with a wake in flight returns the existing job instead of stacking
another.The plugin registers an MCP server (stdio) at the agent.create gate, so
every agent the daemon spawns — including scheduled agents — gets the
tools automatically. A scheduled agent that needs a machine awake before it
works looks like:
power_status({}) → is lab-gpu-box already up?power_wake({ hostId: "host-1a2b3c4d" }) → { jobId, status }
immediately.power_job_status({ jobId }) until the job reports up or failed.failed, read error.mode: never-reachable means the wake path
worked but the machine did not come up (worth retrying once or paging a
human); no-transport means the wake path itself is down.Jobs are shared state (the plugin server and the MCP server coordinate through the same jobs file in the plugin state dir), so a wake started from the roster UI is visible to agents and vice versa.
| Setting | Default | Meaning |
|---|---|---|
defaultWakeWindowSeconds |
110 | Verification window for wake jobs. |
probeTimeoutSeconds |
8 | Connect timeout for the ssh probe. |
maxConcurrentWakes |
2 | Global cap on simultaneous wake jobs. |
agentInjectionEnabled |
true | Inject the power tools into new agents (reload to apply). |
ssh -o BatchMode=yes -o ConnectTimeout=<n> <target> true — set up the
key/alias on the machine running the Paseo daemon, or use statusCommand
instead.host (a subnet-directed address or a WoL relay). Waking across the
internet with plain WoL does not work; put an http transport or a relay in
front of it.command transport runs arbitrary shell on the daemon host. Host
configuration is trusted operator input — anyone who can edit the roster can
run commands where the daemon runs.npm run typecheck # tsc --noEmit
npm test # typecheck + node --test suites (unit + stdio protocol)
Tests are hermetic: fixture state dirs and fake spawners/sockets, never a real network or the developer's home.
MIT — see LICENSE.
Scanned 12 Oct 2026, 11:28 UTC from georgepullen/paseo/plugins/paseo-remote-power.