Imports 67 files from VPS (/opt/teleo-eval/) into repo as the single source of truth. Previously only 8 of 67 files existed in repo — the rest were deployed directly to VPS via SCP, causing massive drift. Includes: - pipeline/lib/: 33 Python modules (daemon core, extraction, evaluation, merge, cascade, cross-domain, costs, attribution, etc.) - pipeline/: main daemon (teleo-pipeline.py), reweave.py, batch-extract-50.sh - diagnostics/: 19 files (4-page dashboard, alerting, daily digest, review queue, tier1 metrics) - agent-state/: bootstrap, lib-state, cascade inbox processor, schema - systemd/: service unit files for reference - deploy.sh: rsync-based deploy with --dry-run, syntax checks, dirty-tree gate - research-session.sh: updated with Step 8.5 digest + cascade inbox processing No new code written — all files are exact copies from VPS as of 2026-04-06. From this point forward: edit in repo, commit, then deploy.sh. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
149 lines
7.2 KiB
Python
149 lines
7.2 KiB
Python
"""Shared UI components for the 4-page Argus dashboard.
|
|
|
|
Provides: nav bar, CSS, page skeleton, Chart.js imports, shared JS helpers.
|
|
All pages import render_page() and pass their body HTML + page-specific scripts.
|
|
"""
|
|
|
|
# Page definitions — used by nav bar
|
|
PAGES = [
|
|
{"path": "/prs", "label": "PRs", "icon": "✎"},
|
|
{"path": "/ops", "label": "Operations", "icon": "⚙"},
|
|
{"path": "/health", "label": "Knowledge Health", "icon": "♥"},
|
|
{"path": "/agents", "label": "Agents", "icon": "★"},
|
|
{"path": "/epistemic", "label": "Epistemic", "icon": "⚖"},
|
|
]
|
|
|
|
|
|
def _nav_html(active_path: str) -> str:
|
|
"""Render the shared navigation bar."""
|
|
links = []
|
|
for p in PAGES:
|
|
cls = "nav-active" if p["path"] == active_path else ""
|
|
links.append(
|
|
f'<a href="{p["path"]}" class="nav-link {cls}">'
|
|
f'{p["icon"]} {p["label"]}</a>'
|
|
)
|
|
return f"""<nav class="top-nav">
|
|
<div class="nav-brand">Argus</div>
|
|
<div class="nav-links">{"".join(links)}</div>
|
|
<div class="nav-aux">
|
|
<a href="/audit" class="nav-link">Audit</a>
|
|
<a href="/api/metrics" class="nav-link">API</a>
|
|
</div>
|
|
</nav>"""
|
|
|
|
|
|
SHARED_CSS = """
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body { font-family: -apple-system, system-ui, 'Segoe UI', sans-serif; background: #0d1117; color: #c9d1d9; }
|
|
.top-nav { display: flex; align-items: center; gap: 16px; padding: 12px 24px;
|
|
background: #161b22; border-bottom: 1px solid #30363d; position: sticky; top: 0; z-index: 100; }
|
|
.nav-brand { color: #58a6ff; font-weight: 700; font-size: 18px; }
|
|
.nav-links { display: flex; gap: 4px; flex: 1; }
|
|
.nav-aux { display: flex; gap: 4px; }
|
|
.nav-link { color: #8b949e; text-decoration: none; padding: 6px 12px; border-radius: 6px;
|
|
font-size: 13px; transition: all 0.15s; white-space: nowrap; }
|
|
.nav-link:hover { color: #c9d1d9; background: #21262d; }
|
|
.nav-active { color: #58a6ff !important; background: #0d1117; font-weight: 600; }
|
|
.page-content { padding: 24px; max-width: 1400px; margin: 0 auto; }
|
|
.page-header { margin-bottom: 20px; }
|
|
.page-header h1 { color: #58a6ff; font-size: 22px; }
|
|
.page-header .subtitle { color: #8b949e; font-size: 13px; margin-top: 4px; }
|
|
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin: 16px 0; }
|
|
.card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 16px; }
|
|
.card .label { color: #8b949e; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
.card .value { font-size: 28px; font-weight: 700; margin-top: 2px; }
|
|
.card .detail { color: #8b949e; font-size: 11px; margin-top: 2px; }
|
|
.green { color: #3fb950; }
|
|
.yellow { color: #d29922; }
|
|
.red { color: #f85149; }
|
|
.blue { color: #58a6ff; }
|
|
.purple { color: #bc8cff; }
|
|
.chart-container { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 16px; margin: 16px 0; }
|
|
.chart-container h2 { color: #c9d1d9; font-size: 14px; margin-bottom: 12px; }
|
|
canvas { max-height: 260px; }
|
|
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
@media (max-width: 800px) { .row { grid-template-columns: 1fr; } }
|
|
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
th { color: #8b949e; font-size: 11px; text-transform: uppercase; text-align: left; padding: 6px 10px; border-bottom: 1px solid #30363d; }
|
|
td { padding: 6px 10px; border-bottom: 1px solid #21262d; }
|
|
code { background: #21262d; padding: 2px 6px; border-radius: 3px; font-size: 12px; }
|
|
.section { margin-top: 28px; }
|
|
.section-title { color: #58a6ff; font-size: 15px; font-weight: 600; margin-bottom: 12px; padding-bottom: 6px; border-bottom: 1px solid #21262d; }
|
|
.funnel { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
.funnel-step { text-align: center; flex: 1; min-width: 100px; }
|
|
.funnel-step .num { font-size: 24px; font-weight: 700; }
|
|
.funnel-step .lbl { font-size: 11px; color: #8b949e; text-transform: uppercase; }
|
|
.funnel-arrow { color: #30363d; font-size: 20px; }
|
|
.footer { margin-top: 40px; padding: 16px 24px; border-top: 1px solid #21262d; color: #484f58; font-size: 11px; text-align: center; }
|
|
.footer a { color: #484f58; text-decoration: none; }
|
|
.footer a:hover { color: #8b949e; }
|
|
.alert-banner { padding: 8px 16px; font-size: 12px; border-radius: 6px; margin-bottom: 12px; }
|
|
.alert-critical { background: #f8514922; border: 1px solid #f85149; color: #f85149; }
|
|
.alert-warning { background: #d2992222; border: 1px solid #d29922; color: #d29922; }
|
|
.alert-info { background: #58a6ff22; border: 1px solid #58a6ff; color: #58a6ff; }
|
|
.badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; }
|
|
.badge-green { background: #23863633; color: #3fb950; }
|
|
.badge-yellow { background: #d2992233; color: #d29922; }
|
|
.badge-red { background: #f8514933; color: #f85149; }
|
|
.badge-blue { background: #1f6feb33; color: #58a6ff; }
|
|
"""
|
|
|
|
|
|
CHART_JS_IMPORTS = """<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation@3.1.0"></script>"""
|
|
|
|
|
|
SHARED_JS = """
|
|
const AGENT_COLORS = {
|
|
'rio': '#58a6ff', 'clay': '#3fb950', 'astra': '#bc8cff',
|
|
'leo': '#d29922', 'vida': '#f0883e', 'theseus': '#f85149',
|
|
'epimetheus': '#79c0ff', 'ganymede': '#8b949e', 'oberon': '#ec4899',
|
|
};
|
|
function agentColor(name) {
|
|
return AGENT_COLORS[name?.toLowerCase()] ||
|
|
'#' + ((name||'').split('').reduce((a,c) => (a*31+c.charCodeAt(0))&0xFFFFFF, 0x556677)).toString(16).padStart(6,'0');
|
|
}
|
|
Chart.defaults.color = '#8b949e';
|
|
Chart.defaults.borderColor = '#21262d';
|
|
Chart.defaults.font.family = '-apple-system, system-ui, sans-serif';
|
|
Chart.defaults.font.size = 11;
|
|
|
|
function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
|
|
function fmtPct(v) { return v != null ? (v * 100).toFixed(1) + '%' : '--'; }
|
|
function fmtNum(v) { return v != null ? v.toLocaleString() : '--'; }
|
|
function fmtDollars(v) { return v != null ? '$' + v.toFixed(2) : '--'; }
|
|
"""
|
|
|
|
|
|
def render_page(title: str, subtitle: str, active_path: str, body_html: str,
|
|
scripts: str = "", extra_css: str = "", timestamp: str = "") -> str:
|
|
"""Render a complete page with nav, content, and footer."""
|
|
ts_display = f" · {timestamp}" if timestamp else ""
|
|
return f"""<!DOCTYPE html>
|
|
<html lang="en"><head>
|
|
<meta charset="utf-8">
|
|
<title>Argus - {title}</title>
|
|
<meta http-equiv="refresh" content="60">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
{CHART_JS_IMPORTS}
|
|
<style>{SHARED_CSS}{extra_css}</style>
|
|
</head><body>
|
|
{_nav_html(active_path)}
|
|
<div class="page-content">
|
|
<div class="page-header">
|
|
<h1>{title}</h1>
|
|
<div class="subtitle">{subtitle}{ts_display} · auto-refresh 60s</div>
|
|
</div>
|
|
{body_html}
|
|
</div>
|
|
<div class="footer">
|
|
Argus · Teleo Pipeline Diagnostics ·
|
|
<a href="/api/metrics">Metrics API</a> ·
|
|
<a href="/api/vital-signs">Vital Signs API</a> ·
|
|
<a href="/api/contributors">Contributors API</a>
|
|
</div>
|
|
<script>{SHARED_JS}</script>
|
|
{scripts}
|
|
</body></html>"""
|