diff --git a/core/dashboard_error.log b/core/dashboard_error.log index ccc7fac..db75006 100644 --- a/core/dashboard_error.log +++ b/core/dashboard_error.log @@ -2,3 +2,5 @@ * Running on http://127.0.0.1:5106 [33mPress CTRL+C to quit[0m 127.0.0.1 - - [03/Aug/2026 21:35:50] "GET /health HTTP/1.1" 200 - +127.0.0.1 - - [03/Aug/2026 23:50:58] "GET / HTTP/1.1" 200 - +127.0.0.1 - - [03/Aug/2026 23:50:58] "[33mGET /favicon.ico HTTP/1.1[0m" 404 - diff --git a/core/evolution_log.json b/core/evolution_log.json new file mode 100644 index 0000000..554f4cc --- /dev/null +++ b/core/evolution_log.json @@ -0,0 +1,10 @@ +[ + { + "vertical": "all", + "agent": "hermes", + "feature": "NOC Dashboard v2 deployed", + "details": "Clean premium design with Inter font, per-site cards, metrics, evolution tracking panel", + "status": "completed", + "timestamp": "2026-08-03T23:51:15.624329" + } +] \ No newline at end of file diff --git a/core/evolution_log.py b/core/evolution_log.py new file mode 100644 index 0000000..bc18dc3 --- /dev/null +++ b/core/evolution_log.py @@ -0,0 +1,74 @@ +""" +Site Evolution Log — tracks all improvements made by evolution agents. +Appended to by sub-agents, displayed on NOC dashboard. +""" +import json, os +from datetime import datetime +from pathlib import Path + +LOG_PATH = Path(os.path.expanduser("~/auto-publisher/core/evolution_log.json")) + +def log_evolution(vertical: str, agent: str, feature: str, details: str, status: str = "completed"): + """Record an evolution event.""" + log = [] + if LOG_PATH.exists(): + log = json.loads(LOG_PATH.read_text()) + + log.append({ + "vertical": vertical, + "agent": agent, + "feature": feature, + "details": details[:500], + "status": status, + "timestamp": datetime.now().isoformat(), + }) + + # Keep last 200 entries + log = log[-200:] + LOG_PATH.write_text(json.dumps(log, indent=2)) + + +def get_recent_evolutions(limit: int = 20) -> list[dict]: + """Get recent evolution events.""" + if not LOG_PATH.exists(): + return [] + log = json.loads(LOG_PATH.read_text()) + return sorted(log, key=lambda x: x["timestamp"], reverse=True)[:limit] + + +def get_evolution_stats() -> dict: + """Aggregate evolution statistics.""" + if not LOG_PATH.exists(): + return {"total": 0, "by_vertical": {}, "recent": []} + + log = json.loads(LOG_PATH.read_text()) + by_v = {} + for entry in log: + v = entry["vertical"] + by_v[v] = by_v.get(v, 0) + 1 + + return { + "total": len(log), + "by_vertical": by_v, + "latest": log[-1]["timestamp"][:19] if log else None, + "recent": [e["feature"] for e in log[-5:]], + } + + +if __name__ == "__main__": + import argparse + ap = argparse.ArgumentParser() + ap.add_argument("--log", action="store_true") + ap.add_argument("--vertical", type=str, default="all") + ap.add_argument("--feature", type=str, default="test") + ap.add_argument("--details", type=str, default="") + ap.add_argument("--agent", type=str, default="manual") + ap.add_argument("--status", type=str, default="completed") + args = ap.parse_args() + + if args.log: + log_evolution(args.vertical, args.agent, args.feature, args.details, args.status) + print(f"Logged: {args.vertical} — {args.feature}") + else: + stats = get_evolution_stats() + print(json.dumps(stats, indent=2)) diff --git a/dashboard/noc.py b/dashboard/noc.py index d3c8f42..2f1eea8 100644 --- a/dashboard/noc.py +++ b/dashboard/noc.py @@ -1,10 +1,14 @@ """ NOC Dashboard — clean, modern fleet monitor for all 8 publisher sites. """ -import json, time, threading, requests +import json, time, threading, requests, sys, os from datetime import datetime from flask import Flask, jsonify +# Evolution log +sys.path.insert(0, os.path.expanduser("~/auto-publisher/core")) +from evolution_log import get_recent_evolutions, get_evolution_stats, log_evolution + app = Flask(__name__) SITES = { @@ -57,6 +61,9 @@ def home(): if not cache["ts"] or (datetime.now() - datetime.fromisoformat(cache["ts"])).seconds > 30: threading.Thread(target=refresh, daemon=True).start() data = cache["data"] or {} + evo_stats = get_evolution_stats() + evo_recent = get_recent_evolutions(6) + evo_html = build_evo_html(evo_recent) return HTML.format( alive=sum(1 for s in data.values() if s.get("alive")), total=len(data), @@ -65,6 +72,8 @@ def home(): today=sum(s.get("today", 0) for s in data.values()), subs=sum(s.get("subs", 0) for s in data.values()), updated=(cache["ts"] or "")[:19], + evo_total=evo_stats["total"], + evo_html=evo_html, cards=build_cards(data), ) @@ -115,10 +124,27 @@ def api_refresh(): refresh() return jsonify({"status": "ok"}) +@app.route("/api/evolution") +def api_evolution(): + return jsonify({ + "stats": get_evolution_stats(), + "recent": get_recent_evolutions(20), + }) + @app.route("/health") def health(): return jsonify({"status": "ok", "ts": cache["ts"]}) +def build_evo_html(events): + if not events: + return '