- 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>
- Extract shared send_event/clear_event into detectors/base.py, removing
~150 lines of duplication across all 6 detectors
- Fix default aggregator URL from port 5000 to 5100 in all detectors
- Standardize cpu.py and memory.py to use active_alerts set pattern
- Fix immediate emote rotation on startup (last_emote_change = time.time())
- Extract magic numbers to named constants in aggregator
- Protect write_status() with try/except OSError
- Fix notify event ID collision with monotonic counter
- Replace blocking stream_output() with background daemon threads in kao.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- install.sh now clones from git instead of copying files
- Service runs git pull on restart for automatic updates
- Support config.local.json for production settings (gitignored)
- kao.py prefers config.local.json when present
To update production: push changes, then 'sudo systemctl restart kao'
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Port now set via config.json "port" field
- Aggregator reads PORT from environment variable
- Updated all docs and scripts to use port 5100
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Renamed sentry.py to kao.py
- Updated all references from Sentry-Emote to Kao
- Kao (顔) means "face" in Japanese
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>