#!/usr/bin/env python3 """ Timmy Bridge Monitor Tracks heartbeat, artifacts, and response metrics for Local Timmy """ import asyncio import json import sqlite3 import time from datetime import datetime from pathlib import Path # Simple WebSocket client for monitoring import websockets DB_PATH = Path("/root/allegro/timmy_metrics.db") RELAY_URL = "ws://167.99.126.228:3334" class TimmyMonitor: def __init__(self): self.db = None self.init_db() def init_db(self): """Initialize SQLite database for metrics""" self.db = sqlite3.connect(DB_PATH) cursor = self.db.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS heartbeats ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT, timmy_pubkey TEXT, event_id TEXT, content_preview TEXT, latency_ms INTEGER, response_time_ms INTEGER ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS artifacts ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT, timmy_pubkey TEXT, artifact_type TEXT, reference TEXT, size_bytes INTEGER, description TEXT ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS conversations ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT, started_at TEXT, ended_at TEXT, turn_count INTEGER, total_latency_ms INTEGER ) ''') self.db.commit() async def listen(self): """Listen to relay for Timmy events""" print(f"[Monitor] Connecting to {RELAY_URL}") while True: # Main reconnect loop - NOT recursive try: async with websockets.connect(RELAY_URL) as ws: # Subscribe to all events (filter by Timmy's pubkey once known) sub_id = "timmy-monitor" req = ["REQ", sub_id, {}] await ws.send(json.dumps(req)) print("[Monitor] Listening for events...") while True: msg = await ws.recv() data = json.loads(msg) await self.handle_event(data) except Exception as e: print(f"[Monitor] Error: {e}") print("[Monitor] Reconnecting in 5 seconds...") await asyncio.sleep(5) # Loop continues automatically - no recursion async def handle_event(self, data): """Process incoming events""" if data[0] != "EVENT": return event = data[2] kind = event.get("kind") pubkey = event.get("pubkey") content = event.get("content", "") created_at = event.get("created_at") timestamp = datetime.fromtimestamp(created_at).isoformat() if kind == 1: # Short text note - heartbeat print(f"[Heartbeat] {timestamp} from {pubkey[:16]}...") self.log_heartbeat(pubkey, event["id"], content[:100]) elif kind == 30078: # Artifact event print(f"[Artifact] {timestamp} from {pubkey[:16]}...") self.log_artifact(pubkey, content, event.get("tags", [])) def log_heartbeat(self, pubkey, event_id, content): """Log heartbeat event""" cursor = self.db.cursor() cursor.execute(''' INSERT INTO heartbeats (timestamp, timmy_pubkey, event_id, content_preview) VALUES (?, ?, ?, ?) ''', (datetime.now().isoformat(), pubkey, event_id, content)) self.db.commit() def log_artifact(self, pubkey, content, tags): """Log artifact creation""" # Extract artifact type from tags artifact_type = "unknown" for tag in tags: if tag[0] == "t" and "artifact-type:" in tag[1]: artifact_type = tag[1].split(":")[1] cursor = self.db.cursor() cursor.execute(''' INSERT INTO artifacts (timestamp, timmy_pubkey, artifact_type, reference, size_bytes, description) VALUES (?, ?, ?, ?, ?, ?) ''', (datetime.now().isoformat(), pubkey, artifact_type, content[:64], len(content), content[:200])) self.db.commit() def generate_report(self): """Generate morning retrospective report""" cursor = self.db.cursor() # Last 24 hours cursor.execute(''' SELECT COUNT(*), AVG(latency_ms) FROM heartbeats WHERE timestamp > datetime('now', '-1 day') ''') heartbeat_count, avg_latency = cursor.fetchone() cursor.execute(''' SELECT COUNT(*), artifact_type FROM artifacts WHERE timestamp > datetime('now', '-1 day') GROUP BY artifact_type ''') artifacts = cursor.fetchall() report = f""" # Timmy Retrospective - {datetime.now().strftime('%Y-%m-%d %H:%M')} ## Heartbeats - Count: {heartbeat_count or 0} - Avg latency: {avg_latency or 'N/A'} ms ## Artifacts Created {chr(10).join([f"- {count} {atype}" for count, atype in artifacts]) if artifacts else "- None"} ## Status {'✓ Active' if heartbeat_count and heartbeat_count > 0 else '✗ No activity detected'} """ return report if __name__ == "__main__": monitor = TimmyMonitor() # Run listener try: asyncio.run(monitor.listen()) except KeyboardInterrupt: print("\n[Monitor] Shutting down") print(monitor.generate_report())