Bump to v2.3.0: replace polling with SSE stream, fix detector imports

- Add GET /stream SSE endpoint to aggregator.py; state is pushed
  instantly on every change instead of fetched every 2s
- Replace setInterval polling in index.html with EventSource;
  onerror shows the ( ?.?) face and auto-reconnect is handled by
  the browser natively
- Fix ModuleNotFoundError in detectors: inject project root into
  PYTHONPATH when launching subprocesses from kao.py
- Update openapi.yaml, CLAUDE.md, README.md with /stream endpoint
- Remove completed SSE item from TODO.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 17:25:15 -06:00
parent 9291066263
commit aaae20281d
7 changed files with 114 additions and 33 deletions

View File

@@ -14,7 +14,7 @@ info:
## TTL/Heartbeat Pattern
Events can have a TTL (time-to-live) that auto-expires them. Detectors typically send
heartbeat events that expire if not refreshed, indicating loss of communication.
version: 2.2.0
version: 2.3.0
license:
name: MIT
@@ -183,12 +183,41 @@ paths:
schema:
$ref: "#/components/schemas/WakeResponse"
/stream:
get:
summary: SSE stream of state updates
description: |
Server-Sent Events stream that pushes the current state as JSON whenever
it changes. The frontend connects here instead of polling `/status`.
Each event is a `data:` line containing a JSON-encoded Status object,
followed by a blank line. A `: keepalive` comment is sent every 30
seconds to prevent proxy timeouts. The current state is pushed
immediately on connection.
The browser's `EventSource` API handles automatic reconnection if the
connection drops.
operationId: getStream
responses:
"200":
description: Event stream
content:
text/event-stream:
schema:
type: string
description: |
Newline-delimited SSE events. `data:` lines contain a
JSON-encoded Status object. Lines beginning with `:` are
keepalive comments and can be ignored.
example: "data: {\"current_state\": \"optimal\", ...}\n\n"
/status:
get:
summary: Get current status
description: |
Returns the current display state including the active emote, color,
animation, and list of active events. The frontend polls this endpoint.
animation, and list of active events. Prefer `/stream` for live
displays; use this endpoint for one-shot queries.
operationId: getStatus
responses:
"200":