HTTP API
The pet serves this text at GET /. It is generated from the engine (engine/door/api.cpp) by tools/specdoc.py; edit the engine, not this file.
NestBox
========
A pixel pet on a small screen. Anything on your network can make it react with one HTTP POST.
POST /v1/event (JSON body, Content-Type: application/json, up to 16 KB)
{"source": "tests", "event": "error", "level": "error", "for": 30}
source who is sending: "my-agent", "tests", "ci", "home"... (default "webhook")
event what happened. Known events:
working -> watches its laptop work until done, error or stop (or `for` s);
if you type too, it works at its desk itself
busy -> busy at its desk until done, error or stop: a build, a deploy, tests
done -> a happy pose with a heart
celebrate -> celebrates, bigger than done: shipped, merged, a birthday
needs_you -> calls you with a "!" and `text` as its question for a minute,
then waits with a small "!" and a status line showing how long,
calling again every 10 min (3 times). The line turns grey after
30 min without word from that session and clears an hour after
its last word (or after `for` s). Asking again refreshes it; a new
question calls again. It ends when that session sends working,
done or stop. Sessions wait separately ("needs you x2").
A tap stops the calling; a double press dismisses all waiting.
your_turn -> "Myagent: your turn" in the status bar until you are back, tap
the pet, or that session sends its next event
error -> a sad reaction with a red cross; if the user is away, it stays
slumped with "Myagent failed - N min" in red until they are back
down -> stays down until the same sender sends up, then celebrates:
a red build, an outage
alert -> a startled hop: a doorbell, a message, a timer
warning -> oops, lighter than error
music ("playing": true|false) -> notes and swaying on top of anything; dances when free
quiet -> sits still and reactions wait (60 min, or `for`): a meeting, focus
break -> eats (20 min, or `for`)
party -> a party (30 min, or `for`)
stop -> clears what this source/session set (working, busy, down,
needs you, its state and text); nobody else's
shake -> dizzy (sent by the device's motion sensor)
pet -> petted. A tap on the device itself also acknowledges `until` "seen".
any other event reacts by its level; `state` and `anim` reach every other state and animation
level info (a small nod) | happy | alert | error (default info)
session optional: keeps several sessions of one source apart
text optional: a few words in a speech bubble over the pet (up to 72 characters)
color optional: the text's colour, a name (orange, red, green, blue, yellow, purple, pink, cyan,
white, gray) or "#rrggbb". Remembered per source; the default is cream.
size optional: small | medium | large, for a speech bubble and the ring (default medium)
status optional: a line in the status bar along the top (e.g. "standup in 5 min"), one per source
(8 at most), in `color`, until replaced, `"status": ""`, stop, or `for` seconds (default 1 h).
The bar shows one line at a time in large letters; several take turns, long ones scroll.
Coding agent sessions (see GET /agents) show their current step and running helpers here.
meter optional: a progress ring in the top-right corner, one per source: a number, filled clockwise in
`color` (e.g. a playlist's progress, a download, a usage limit); null removes it. Each message sets
the whole ring. Up to 3 show, the most recently updated on the right. A ring stays 24 h after its
last update, or `for` seconds; stop clears it. GET /v1/state lists them.
max optional, with meter: the value of a full ring, above 0 (default 100); the number is kept in 0..max
label optional, with meter: a few characters inside the ring (default: the percentage of max, e.g. 62)
at optional: pet (a speech bubble, default) | top | bottom: a caption, a row in the status bar's style
(top: just under the status bar)
state optional: ask for any named state. Moods are held `for` seconds (default 60): idle, wander,
working, typing_fast, watching, needs_you, asleep, busy, party, dancing, fighting, eating,
quiet, down. Reactions play once: done, error, dizzy, waking_up, celebrate, noted, surprised,
petted, grumpy, confused, yawn, oops.
anim optional: play any animation the pet has, by name (e.g. "Hop", "Dance"), `for` seconds
(default 3) or "once": true (one run). A pet without it plays its Idle.
face optional, with anim: you | front-right | right | back-right | away | back-left | left |
front-left (or "row": 0-7). Default: you; with `to`, the way it arrives.
to optional: walk there first, then play: [x, y] in screen pixels (as GET /v1/state reports; the nearest
spot it may stand on) or "home" (its place by the desk). Its time starts when it has arrived.
steps optional: a chain, up to 16, each {to, face, anim, for | once, text}, played in order; then it
goes back to its own life. A spot it can't reach within 30 s is skipped. An agent calling for
you or a reaction comes first, then the chain carries on. `stop` from the same source ends it.
GET /v1/pets gives each animation's directions (8, or 1: it always faces you) and ms (one run).
for optional: seconds a working/needs-you lasts, or a reaction plays
until optional: "seen" keeps a state, text, status or needs_you until the user taps the pet (or presses
its button), the same source/session sends its next message, or an hour (or `for`) passes.
needs_you does this by default; e.g. {"source":"deploy","text":"deploy failed","state":"down",
"until":"seen"}.
pet optional: bring out another pet: its name, "next" or "previous" (a short cross-fade)
Reply: {"ok": true, "reaction": "done", "state": "done"}
The Mac companion app sends {"source": "mac", "event": "activity", "kps": 2.5, "idle_s": 0,
"key_ms": 300, "cpu": 30, "locked": false} about once a second: key presses per second (a count, never
which keys), ms since the last key, seconds since the last input, CPU load, plus "time" (unix seconds) and
"utc_offset" (seconds east of UTC) so the scene can follow the time of day. A switched-off signal is left
out (kps is sent as -1). When these messages stop for 20 s the pet goes to sleep.
The device's side button sends {"event": "button", "press": "single" | "double" | "long"}: a single
press pats the pet; a double press dismisses everything waiting for you (with nothing waiting, it brings
out the next pet); a long press toggles hush (quiet: no reactions). Only the device itself can send it.
The pet answers only to its own name and addresses, and refuses writes from another site's web page.
GET /v1/state what the pet is doing now (JSON)
GET /v1/pets every pet in detail (its words, scene, what each state plays, animations) and which is out (JSON)
GET /v1/spec every event word, every state and the animations it plays, in order (the pet format's contract)
GET /agents the integration guide for coding agents: give your agent this address to set itself up
GET /llms.txt the same guide
A reply may carry a "warning": an unknown event without a level, an animation this pet doesn't have, or an
unknown state still works (a nod, its Idle), and the warning says what to send instead.
Debugging: {"event": "hold", "state": "asleep"} keeps it in one state (wander, idle, working,
typing_fast, needs_you, asleep); {"event": "auto"} lets it decide again.
Examples:
curl -d '{"source":"tests","event":"error"}' http://nestbox.local/v1/event
curl -d '{"source":"deploy","event":"done"}' http://nestbox.local/v1/event
curl -d '{"text":"lunch!","color":"green","anim":"Hop"}' http://nestbox.local/v1/event
curl -d '{"state":"asleep","for":600,"text":"do not disturb"}' http://nestbox.local/v1/event
curl -d '{"source":"me","steps":[{"to":[60,200],"face":"left","anim":"Eat","for":4,"text":"snack"},
{"anim":"Hop","once":true},{"to":"home"}]}' http://nestbox.local/v1/event