#!/usr/bin/env python3 """CASAN H6 — hosted AgentOps dashboard server (D3). Serves the generated dashboard over HTTP with a stale-aware /healthz probe so an external monitor (uptime check / load-balancer) can page when telemetry stops flowing — not just when the process dies. Routes: GET / -> dashboard HTML (also /dashboard) GET /healthz -> 200 {"status":"ok",...} while metrics are fresh, 503 {"status":"stale",...} when metrics are older than CASAN_DASHBOARD_STALE_S (default 3600s) or missing. Usage: dashboard-server.py """ import json import os import pathlib import sys import time from http.server import BaseHTTPRequestHandler, HTTPServer ROOT = pathlib.Path(__file__).resolve().parents[3] PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8787 DASH = pathlib.Path(os.environ.get( "CASAN_DASHBOARD_HTML", ROOT / "docs" / "output" / "casan" / "central-agentops-dashboard.html")) METRICS = pathlib.Path(os.environ.get( "CASAN_DASHBOARD_METRICS", ROOT / ".specify" / "logs" / "cost" / "metrics.jsonl")) ALERTS = pathlib.Path(os.environ.get( "CASAN_DASHBOARD_ALERTS", ROOT / ".specify" / "agentops" / "alerts.log")) STALE_S = int(os.environ.get("CASAN_DASHBOARD_STALE_S", "3600")) def count_lines(path: pathlib.Path) -> int: if not path.exists(): return 0 return sum(1 for line in path.read_text(encoding="utf-8").splitlines() if line.strip()) class Handler(BaseHTTPRequestHandler): def _send(self, code: int, ctype: str, body: str) -> None: data = body.encode("utf-8") self.send_response(code) self.send_header("Content-Type", ctype) self.send_header("Content-Length", str(len(data))) self.end_headers() self.wfile.write(data) def do_GET(self): # noqa: N802 (http.server API) if self.path == "/healthz": if METRICS.exists(): age = int(time.time() - METRICS.stat().st_mtime) stale = age > STALE_S else: age = -1 stale = True body = json.dumps({ "status": "stale" if stale else "ok", "metrics_age_s": age, "stale_after_s": STALE_S, "runs": count_lines(METRICS), "alerts": count_lines(ALERTS), }) self._send(503 if stale else 200, "application/json", body) elif self.path in ("/", "/dashboard"): if DASH.exists(): self._send(200, "text/html; charset=utf-8", DASH.read_text(encoding="utf-8")) else: self._send(404, "text/plain", "dashboard not generated") else: self._send(404, "text/plain", "not found") def log_message(self, *args): # silence per-request stderr noise pass BIND = os.environ.get("CASAN_DASHBOARD_BIND", "127.0.0.1") if __name__ == "__main__": HTTPServer((BIND, PORT), Handler).serve_forever()