# Orbtile adapter examples

Reference adapters for the [Orbtile Adapter Protocol v1](reference.md).
The API is off by default: the user turns on the "Adapter API" switch in Orbtile › Settings › Agents › Adapter API and
then allows each new adapter. To develop without Orbtile, use `mock-orbtile.py`. All of the examples talk HTTP over the
Unix socket named in the discovery file.

Orbtile's own test suite runs the three reference adapters (`ref-shell.sh`, `ref.py`, `ref.mjs`) against the real
server (`AdapterAPITests.testReferenceAdaptersAgainstTheRealServer` and `ReferenceAdapterTests`): the happy path, the
whole Python demo, a failing command, awkward titles and paths, no discovery file, a regenerated token (401, then 404
`adapter_not_registered`: a new token ends every registration) and an Orbtile relaunch in the middle of the work (new
token and no registrations: 401, then 404). `mock-orbtile.py` has its own tests in the same file.

Staying registered: an adapter that has been silent for more than 15 s shows its sessions `offline`, and after 60 s its
registration and sessions are gone (the next call gets 404 and it must register again). The `ref.mjs` poller
sends the full list every 5 s and the `ref.py` demo never pauses longer than 10 s; a long-running adapter of your own
should send a `POST /adapters/<id>/heartbeat` every 10 s. `ref-shell.sh` does that: while its command runs, a
background loop sends a heartbeat every 10 s (`ORBTILE_HEARTBEAT_SECONDS` changes it) and, on 401 or 404, reads the
discovery file again, registers again and resends the running session. The loop ends with the command (or when the
script is killed) and never writes to the command's output.

| File | Needs | What it shows |
|---|---|---|
| `ref-shell.sh` | bash, curl, plutil, iconv | `ref-shell.sh make test`: the command shows as `tool`, then `done` or `error` |
| `ref.py` | Python 3.8+ stdlib | server proof, register, session updates, retry on 401 / 404 / 429; resends the whole session after registering again |
| `ref.mjs` | Node 18+ | a poller: full session list every 5 s |
| `mock-orbtile.py` | Python 3.8+ stdlib | a stand-in server: run your adapter without Orbtile |

Try them without Orbtile:

```sh
python3 mock-orbtile.py /tmp/orbtile-mock/adapter-api.json &
export ORBTILE_API_FILE=/tmp/orbtile-mock/adapter-api.json
./ref-shell.sh sleep 2
python3 ref.py
node ref.mjs          # Ctrl-C to stop
kill %1               # stop the mock; it removes its socket and discovery file
```

The mock prints one line per request. It checks the token and the basic field rules, including the `urlSchemes` rules
at registration (at most 4, lowercase, unique, never `file`, `javascript`, `data`, `orbtile` or `x-apple.systempreferences`)
and the `openURL` rule (its scheme must be one of the registered `urlSchemes`, else `422` on the field). But the JSON
Schema ([JSON schema](adapter-protocol-v1.schema.json)) and Orbtile itself are the authority. The mock does
not ask for an Allow (`enabled` is always `true`), keep time (no TTL, no 15 s offline, no 60 s reap), limit rates or end
registrations when a token changes.

When the discovery file sits in a deep directory (a Unix socket path holds at most 104 bytes), the mock binds its socket
in a short private folder under `/tmp` and the discovery file names it, so read `socketPath` from the file. Both files
are removed when the mock ends (SIGTERM or Ctrl-C).

Palettes: the shell example glows green (150°), Python cyan (200°) and Node magenta (320°). Each sets an
`attentionHue` (330°, 18°, 75°), a different alarm colour each, following the palette rules in the v1 reference.
Their registrations still get a `proximity:` warning: every alarm hue is currently near a built-in waiting colour.
