1301f12e74
- deploy/lyra-dream.service: --loop 1800 user service on lyra-cortex, so Lyra's consolidation + reflection keeps ticking unattended between conversations - deploy/README.md: install / linger / operate runbook - logbus: mirror events to stderr so out-of-band runs (the dream service under journald) are observable, not just via the in-process web SSE feed Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
37 lines
1.2 KiB
Python
37 lines
1.2 KiB
Python
"""In-memory live log bus.
|
|
|
|
A thread-safe ring buffer that any part of Lyra can publish to and the web
|
|
server streams to the browser over SSE. Deliberately process-local and
|
|
ephemeral — it's an activity feed, not durable logging.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import threading
|
|
import time
|
|
from collections import deque
|
|
|
|
_LOCK = threading.Lock()
|
|
_EVENTS: deque[dict] = deque(maxlen=500)
|
|
_SEQ = 0
|
|
|
|
|
|
def log(level: str, msg: str, **fields) -> None:
|
|
"""Publish an event. `level` is info/debug/error/system; fields are extras."""
|
|
global _SEQ
|
|
with _LOCK:
|
|
_SEQ += 1
|
|
_EVENTS.append(
|
|
{"seq": _SEQ, "ts": time.time(), "level": level, "msg": msg, "fields": fields}
|
|
)
|
|
# Mirror to stderr so out-of-band runs (e.g. the dream service under
|
|
# systemd/journald) are observable, not just via the in-process SSE feed.
|
|
extra = " ".join(f"{k}={v}" for k, v in fields.items())
|
|
print(f"[{level}] {msg}{(' ' + extra) if extra else ''}", file=sys.stderr, flush=True)
|
|
|
|
|
|
def since(seq: int) -> list[dict]:
|
|
"""All buffered events with seq greater than `seq` (for SSE catch-up/polling)."""
|
|
with _LOCK:
|
|
return [e for e in _EVENTS if e["seq"] > seq]
|