462 lines
29 KiB
HTML
462 lines
29 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>WEVAL Realtime Monitor v3</title>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&family=Outfit:wght@400;600;700;900&display=swap');
|
|
:root{--bg:#06080f;--card:#0c1018;--border:#1a1f2e;--text:#e2e8f0;--dim:#64748b;--green:#10b981;--red:#ef4444;--yellow:#f59e0b;--blue:#3b82f6;--purple:#8b5cf6;--cyan:#06b6d4;--pink:#ec4899;--accent:#6d28d9}
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
body{background:var(--bg);color:var(--text);font-family:'Outfit',sans-serif;overflow-x:hidden}
|
|
.header{padding:16px 24px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--border);background:linear-gradient(180deg,#0c1018 0%,#06080f 100%)}
|
|
.header h1{font-size:20px;font-weight:900;background:linear-gradient(135deg,var(--purple),var(--cyan));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
|
.header .stats{display:flex;gap:20px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--dim)}
|
|
.header .stats span{color:var(--green)}
|
|
.tabs{display:flex;gap:4px;padding:12px 24px;border-bottom:1px solid var(--border)}
|
|
.tab{padding:8px 16px;border-radius:6px;cursor:pointer;font-size:13px;font-weight:600;color:var(--dim);transition:.2s}
|
|
.tab:hover{background:var(--card);color:var(--text)}
|
|
.tab.active{background:var(--accent);color:#fff}
|
|
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;padding:16px 24px}
|
|
.card{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:14px;position:relative;transition:.2s}
|
|
.card:hover{border-color:var(--accent);transform:translateY(-1px)}
|
|
.card .top{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
|
|
.card .name{font-weight:700;font-size:14px;display:flex;align-items:center;gap:6px}
|
|
.card .name .icon{font-size:16px}
|
|
.badge{padding:2px 8px;border-radius:4px;font-size:10px;font-weight:700;font-family:'JetBrains Mono',monospace;text-transform:uppercase}
|
|
.badge.up{background:#064e3b;color:var(--green)}
|
|
.badge.down{background:#450a0a;color:var(--red)}
|
|
.badge.idle{background:#422006;color:var(--yellow)}
|
|
.badge.agent{background:#1e1b4b;color:var(--purple)}
|
|
.badge.mode{background:#083344;color:var(--cyan)}
|
|
.card .meta{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--dim);line-height:1.8}
|
|
.card .meta b{color:var(--text)}
|
|
.section-title{padding:16px 24px 4px;font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:var(--dim);border-top:1px solid var(--border)}
|
|
.counter-row{display:flex;gap:12px;padding:12px 24px;flex-wrap:wrap}
|
|
.counter{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:14px 20px;text-align:center;min-width:100px;flex:1}
|
|
.counter .num{font-size:28px;font-weight:900;font-family:'JetBrains Mono',monospace;background:linear-gradient(135deg,var(--purple),var(--cyan));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
|
.counter .label{font-size:11px;color:var(--dim);text-transform:uppercase;margin-top:2px}
|
|
.pulse{width:8px;height:8px;border-radius:50%;display:inline-block;animation:pulse 2s infinite}
|
|
.pulse.green{background:var(--green);box-shadow:0 0 6px var(--green)}
|
|
.pulse.red{background:var(--red);box-shadow:0 0 6px var(--red)}
|
|
.pulse.yellow{background:var(--yellow);box-shadow:0 0 6px var(--yellow)}
|
|
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
|
#scan-bar{height:2px;background:var(--accent);width:0;transition:width 1s linear;position:fixed;top:0;left:0;z-index:99}
|
|
</style>
|
|
<!-- DOCTRINE-60-UX-ENRICH direct-inject-20260424-143918 -->
|
|
<style id="doctrine60-ux-direct">
|
|
|
|
/* DOCTRINE-60-UX-ENRICH injected-direct */
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
top: 0; left: 0; width: 100vw; height: 100vh;
|
|
background: radial-gradient(circle at 50% 50%, rgba(100,180,255,0.08), transparent 60%);
|
|
pointer-events: none;
|
|
z-index: -1;
|
|
}
|
|
.card, .kpi, .panel, .btn {
|
|
transition: all 0.3s cubic-bezier(0.2,0,0.1,1);
|
|
}
|
|
.card:hover, .kpi:hover, .panel:hover {
|
|
box-shadow: 0 4px 20px rgba(100,180,255,0.2);
|
|
border-color: rgba(100,180,255,0.5);
|
|
}
|
|
@keyframes pulseD60 {
|
|
0%,100% { opacity: 1; transform: scale(1); }
|
|
50% { opacity: 0.7; transform: scale(1.05); }
|
|
}
|
|
.pulse, .live-indicator, .active, .online {
|
|
animation: pulseD60 3s ease-in-out infinite;
|
|
}
|
|
.modal, .chat, .speech, .overlay {
|
|
backdrop-filter: blur(12px);
|
|
-webkit-backdrop-filter: blur(12px);
|
|
}
|
|
.enter-stagger {
|
|
animation: enterStagD60 0.5s cubic-bezier(0.2,0,0.1,1) forwards;
|
|
}
|
|
@keyframes enterStagD60 {
|
|
from { opacity: 0; transform: translateY(20px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
<body><div id="live-stats" ondblclick="this.remove()" style="position:fixed;top:0;left:0;right:0;z-index:9999;display:flex;justify-content:center;gap:12px;padding:4px 8px;background:linear-gradient(135deg,#1e293b,#0f172a);font-family:sans-serif"><div style="color:#4ade80;font:700 10px sans-serif"><body>#9889; <span id="ls-ag">669</span> Agents</div><div style="color:#60a5fa;font:700 10px sans-serif"><body>#127970; <span id="ls-dp">22</span> Depts</div><div style="color:#fbbf24;font:700 10px sans-serif"><body>#128051; 20 Docker</div><div style="color:#a78bfa;font:700 10px sans-serif"><body>#129302; 10 Ollama</div><div style="color:#f87171;font:700 10px sans-serif"><body>#128200; <span id="ls-nr">153/153</span></div><div style="color:#34d399;font:700 10px sans-serif"><body>#128274; SSO OK</div><div style="width:6px;height:6px;border-radius:50%;background:#4ade80;animation:lp 2s infinite;align-self:center"></div></div><style>@keyframes lp{0%,100%{opacity:1}50%{opacity:.3}}</style>
|
|
<div id="scan-bar"></div>
|
|
<div class="header">
|
|
<h1>⚡ WEVAL SOVEREIGN COMMAND v3</h1>
|
|
<div class="stats">
|
|
<div>Scan: <span id="scan-time">...</span>ms</div>
|
|
<div>Updated: <span id="update-time">...</span></div>
|
|
<div>Refresh: <span>15s</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="counter-row" id="counters"></div>
|
|
|
|
<div class="tabs">
|
|
<div class="tab active" onclick="show('all')">All</div>
|
|
<div class="tab" onclick="show('infra')">🏗 Infra</div>
|
|
<div class="tab" onclick="show('agents')">🤖 Agents</div>
|
|
<div class="tab" onclick="show('modes')">⚡ Modes</div>
|
|
<div class="tab" onclick="show('services')">🔧 Services</div>
|
|
<div class="tab" onclick="show('data')">📊 Data</div>
|
|
<div class="tab" onclick="show('oss')">🌐 OSS</div>
|
|
</div>
|
|
|
|
<div id="content"></div>
|
|
|
|
<script>
|
|
const BASE = '';
|
|
const services = [
|
|
// === INFRASTRUCTURE ===
|
|
{cat:'infra',icon:'💚',name:'WEVIA Brain',url:'/api/weval-ia',check:'status',desc:'Chatbot API souverain'},
|
|
{cat:'infra',icon:'🧠',name:'Qdrant Vector DB',url:'/api/health-qdrant.php',check:'status',desc:'RAG 4,414 skills'},
|
|
{cat:'infra',icon:'🔍',name:'SearXNG Search',url:'/api/health-searxng.php',check:'status',desc:'Meta-search souverain'},
|
|
{cat:'infra',icon:'⚙️',name:'n8n Automation',url:'/api/health-n8n.php',check:'status',desc:'Workflows & crons'},
|
|
{cat:'infra',icon:'💬',name:'Mattermost',url:'/api/health-mm.php',check:'status',desc:'Team chat + webhooks'},
|
|
{cat:'infra',icon:'📊',name:'Twenty CRM',url:'/api/health-twenty.php',check:'status',desc:'CRM pipeline contacts'},
|
|
{cat:'infra',icon:'📈',name:'Plausible Analytics',url:'/api/health-plausible.php',check:'status',desc:'Analytics GDPR'},
|
|
{cat:'infra',icon:'👁️',name:'Uptime Kuma',url:'/api/health-kuma.php',check:'status',desc:'Monitoring 9 endpoints'},
|
|
{cat:'infra',icon:'🐳',name:'Docker Engine',check:'static',status:'up',desc:'19 containers',meta:'images: 17'},
|
|
{cat:'infra',icon:'🌐',name:'Nginx Gateway',check:'static',status:'up',desc:'SSL + 172 req/day',meta:'http2: yes'},
|
|
{cat:'infra',icon:'📧',name:'PMTA Mail',check:'static',status:'idle',desc:'Port 25 STANDBY',meta:'Hetzner anti-spam'},
|
|
{cat:'infra',icon:'🔒',name:'Authentik SSO',check:'static',status:'up',desc:'2 users, 38 auth/day'},
|
|
|
|
// === AI SERVICES ===
|
|
{cat:'services',icon:'🤖',name:'Ollama S151',url:'/api/health-ollama.php',check:'status',desc:'5 models local CPU'},
|
|
{cat:'services',icon:'🖥️',name:'Open WebUI',check:'static',status:'up',desc:'Chat UI :8281',meta:'Ollama frontend'},
|
|
{cat:'services',icon:'🔀',name:'Flowise',check:'static',status:'up',desc:'LLM Workflows :3033'},
|
|
{cat:'services',icon:'📎',name:'Paperclip CEO',check:'static',status:'up',desc:'Sovereign AI agent',meta:'WEVIA Brain'},
|
|
{cat:'services',icon:'🦌',name:'DeerFlow Research',check:'static',status:'up',desc:'113 skills :3002'},
|
|
{cat:'services',icon:'🐟',name:'MiroFish',check:'static',status:'up',desc:'AI Fish :3050+:5001'},
|
|
{cat:'services',icon:'🖥️',name:'WEVCODE',check:'static',status:'up',desc:'4 modes fast/deep/code/math'},
|
|
{cat:'services',icon:'🔧',name:'WEDROID v5',check:'static',status:'up',desc:'7 providers backend diag'},
|
|
{cat:'services',icon:'💊',name:'Ethica HCP',check:'static',status:'up',desc:'131K+ HCPs MA/TN/DZ'},
|
|
{cat:'services',icon:'📧',name:'WEVIA Life Email',check:'static',status:'up',desc:'Eisenhower triage'},
|
|
{cat:'services',icon:'🗡️',name:'Blade Sentinel',url:'/api/blade-api.php?k=BLADE2026&action=status',check:'blade',desc:'Razer Blade v2.2'},
|
|
{cat:'services',icon:'🔊',name:'TTS Voice',check:'static',status:'idle',desc:'espeak/pico2wave'},
|
|
{cat:'services',icon:'📊',name:'Mermaid Diagrams',check:'static',status:'up',desc:'mmdc server-side'},
|
|
{cat:'services',icon:'🧪',name:'Playwright Tests',check:'static',status:'up',desc:'41/41 E2E pass'},
|
|
{cat:'services',icon:'📜',name:'L99 Command Center',check:'static',status:'up',desc:'250 tests, 13 layers'},
|
|
{cat:'services',icon:'🔄',name:'Claude Sync',check:'static',status:'up',desc:'23 transcripts 21.4MB'},
|
|
|
|
// === 19 AGENTS (oh-my-claudecode) ===
|
|
{cat:'agents',icon:'🏗️',name:'Agent: Architect',check:'static',status:'up',desc:'System design & API patterns',badge:'agent'},
|
|
{cat:'agents',icon:'🐛',name:'Agent: Debugger',check:'static',status:'up',desc:'Bug tracing & root cause',badge:'agent'},
|
|
{cat:'agents',icon:'👀',name:'Agent: Code Reviewer',check:'static',status:'up',desc:'Security & best practices',badge:'agent'},
|
|
{cat:'agents',icon:'✂️',name:'Agent: Code Simplifier',check:'static',status:'up',desc:'Refactoring & cleanup',badge:'agent'},
|
|
{cat:'agents',icon:'🎯',name:'Agent: Critic',check:'static',status:'up',desc:'Quality assessment',badge:'agent'},
|
|
{cat:'agents',icon:'📊',name:'Agent: Analyst',check:'static',status:'up',desc:'Data analysis & KPIs',badge:'agent'},
|
|
{cat:'agents',icon:'🎨',name:'Agent: Designer',check:'static',status:'up',desc:'UI/UX & frontend',badge:'agent'},
|
|
{cat:'agents',icon:'📋',name:'Agent: Planner',check:'static',status:'up',desc:'Project planning & tasks',badge:'agent'},
|
|
{cat:'agents',icon:'🛡️',name:'Agent: Security',check:'static',status:'up',desc:'OWASP & vulnerability scan',badge:'agent'},
|
|
{cat:'agents',icon:'🧪',name:'Agent: QA Tester',check:'static',status:'up',desc:'Test generation & E2E',badge:'agent'},
|
|
{cat:'agents',icon:'✍️',name:'Agent: Writer',check:'static',status:'up',desc:'Documentation & content',badge:'agent'},
|
|
{cat:'agents',icon:'⚡',name:'Agent: Executor',check:'static',status:'up',desc:'Shell & deployment ops',badge:'agent'},
|
|
{cat:'agents',icon:'📐',name:'Agent: Blueprint',check:'static',status:'up',desc:'Architecture diagrams',badge:'agent'},
|
|
{cat:'agents',icon:'💰',name:'Agent: Proposal',check:'static',status:'up',desc:'Commercial proposals',badge:'agent'},
|
|
{cat:'agents',icon:'⚖️',name:'Agent: Contract',check:'static',status:'up',desc:'Legal & NDA generation',badge:'agent'},
|
|
{cat:'agents',icon:'📊',name:'Agent: Dashboard',check:'static',status:'up',desc:'KPI dashboards',badge:'agent'},
|
|
{cat:'agents',icon:'🌐',name:'Agent: Translate',check:'static',status:'up',desc:'201 languages',badge:'agent'},
|
|
{cat:'agents',icon:'🏭',name:'Agent: DevForge',check:'static',status:'up',desc:'Code gen & APIs',badge:'agent'},
|
|
{cat:'agents',icon:'📝',name:'Agent: Academy',check:'static',status:'up',desc:'Training & certification',badge:'agent'},
|
|
|
|
// === 7 SUPERCLAUDE MODES ===
|
|
{cat:'modes',icon:'💡',name:'Mode: Brainstorming',check:'static',status:'up',desc:'Creative exploration & ideation',badge:'mode'},
|
|
{cat:'modes',icon:'📊',name:'Mode: Business Panel',check:'static',status:'up',desc:'Strategy, KPIs, analysis',badge:'mode'},
|
|
{cat:'modes',icon:'🔬',name:'Mode: Deep Research',check:'static',status:'up',desc:'Multi-source synthesis',badge:'mode'},
|
|
{cat:'modes',icon:'🧠',name:'Mode: Introspection',check:'static',status:'up',desc:'Meta-cognition & reflection',badge:'mode'},
|
|
{cat:'modes',icon:'🎯',name:'Mode: Orchestration',check:'static',status:'up',desc:'Multi-agent coordination',badge:'mode'},
|
|
{cat:'modes',icon:'📋',name:'Mode: Task Management',check:'static',status:'up',desc:'Planning & deadlines',badge:'mode'},
|
|
{cat:'modes',icon:'⚡',name:'Mode: Token Efficiency',check:'static',status:'up',desc:'Compressed clarity',badge:'mode'},
|
|
|
|
// === DATA & OSS ===
|
|
{cat:'data',icon:'📚',name:'Skills RAG',check:'static',status:'up',desc:'4,414 points Qdrant',meta:'5,422 SKILL.md files'},
|
|
{cat:'data',icon:'🎨',name:'Prompts Library',url:'/api/prompts-library.php',check:'json',key:'count',desc:'DiffusionDB + PromptHero'},
|
|
{cat:'data',icon:'📖',name:'Code Wiki',url:'/api/code-wiki.php',check:'json',key:'files',desc:'Auto-documentation API'},
|
|
{cat:'data',icon:'🏋️',name:'Training Pipeline',check:'static',status:'up',desc:'2,022 samples TRL ready',meta:'GPU: Colab/Kaggle'},
|
|
{cat:'data',icon:'🤖',name:'Agents Catalog',url:'/api/agents-catalog.php',check:'json',key:'count',desc:'oh-my-claudecode 19 agents'},
|
|
{cat:'data',icon:'📊',name:'AI Benchmark',check:'static',status:'up',desc:'182 models, 40 categories',meta:'WEVIA avg 135.9'},
|
|
{cat:'data',icon:'🔍',name:'ModelScope Hub',url:'/api/modelscope-provider.php?action=status',check:'json',key:'ok',desc:'Qwen/DeepSeek/GLM4 routing'},
|
|
{cat:'data',icon:'🎬',name:'LTX-Video',url:'/api/ltx-video-api.php?action=status',check:'json',key:'ok',desc:'Video gen (GPU free options)'},
|
|
|
|
{cat:'oss',icon:'🌐',name:'OSS Discovery',check:'static',status:'up',desc:'619 tools, 615 wired',meta:'pass_rate: 100%'},
|
|
{cat:'oss',icon:'📦',name:'Repos /opt/',check:'static',status:'up',desc:'52 repositories cloned',meta:'antigravity, HolyClaude...'},
|
|
{cat:'oss',icon:'🐙',name:'GHGrab Cloner',check:'static',status:'up',desc:'Bulk GitHub skills clone'},
|
|
{cat:'oss',icon:'🔒',name:'AgentShield Audit',check:'static',status:'up',desc:'CLAUDE.md: secrets=clean'},
|
|
{cat:'oss',icon:'🛡️',name:'WEVIA Gateway',check:'static',status:'up',desc:'Sovereign Anthropic+OpenAI proxy'},
|
|
];
|
|
|
|
let currentTab = 'all';
|
|
function show(tab) {
|
|
currentTab = tab;
|
|
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
event.target.classList.add('active');
|
|
render();
|
|
}
|
|
|
|
async function checkService(s) {
|
|
if (s.check === 'static') return {...s, status: s.status || 'up', latency: 0};
|
|
try {
|
|
const start = performance.now();
|
|
const r = await fetch(BASE + s.url, {signal: AbortSignal.timeout(8000)});
|
|
const latency = Math.round(performance.now() - start);
|
|
if (!r.ok) return {...s, status: 'down', latency, meta: `HTTP ${r.status}`};
|
|
const data = await r.json().catch(() => null);
|
|
if (s.check === 'status') return {...s, status: data?.status === 'ok' ? 'up' : 'down', latency, data};
|
|
if (s.check === 'json') return {...s, status: data?.ok || data?.[s.key] ? 'up' : 'down', latency, data, meta: `${s.key}: ${data?.[s.key] ?? '?'}`};
|
|
if (s.check === 'blade') return {...s, status: data?.blade?.online ? 'up' : 'down', latency, data, meta: `CPU: ${data?.blade?.heartbeat?.cpu ?? '?'}`};
|
|
return {...s, status: 'up', latency, data};
|
|
} catch(e) { return {...s, status: 'down', latency: 0, meta: 'timeout'}; }
|
|
}
|
|
|
|
function render() {
|
|
const filtered = currentTab === 'all' ? services : services.filter(s => s.cat === currentTab);
|
|
const groups = {};
|
|
filtered.forEach(s => { (groups[s.cat] = groups[s.cat] || []).push(s); });
|
|
|
|
const labels = {infra:'🏗 Infrastructure',services:'🔧 Services',agents:'🤖 Agents (19)',modes:'⚡ Modes (7)',data:'📊 Data & APIs',oss:'🌐 OSS Discovery'};
|
|
let html = '';
|
|
for (const [cat, items] of Object.entries(groups)) {
|
|
html += `<div class="section-title">${labels[cat] || cat}</div><div class="grid">`;
|
|
items.forEach(s => {
|
|
const st = s._status || s.status || 'idle';
|
|
const badge = s.badge ? `<span class="badge ${s.badge}">${s.badge}</span>` : `<span class="badge ${st}">${st}</span>`;
|
|
const lat = s._latency ? `${s._latency}ms` : '';
|
|
html += `<div class="card" data-cat="${s.cat}">
|
|
<div class="top"><div class="name"><span class="icon">${s.icon}</span>${s.name}</div>${badge}</div>
|
|
<div class="meta">${s.desc}${s._meta ? '<br>'+s._meta : (s.meta ? '<br>'+s.meta : '')}${lat ? '<br>⚡ '+lat : ''}</div>
|
|
</div>`;
|
|
});
|
|
html += '</div>';
|
|
}
|
|
document.getElementById('content').innerHTML = html;
|
|
}
|
|
|
|
async function scan() {
|
|
const bar = document.getElementById('scan-bar');
|
|
bar.style.width = '0%';
|
|
setTimeout(() => bar.style.width = '100%', 50);
|
|
const start = performance.now();
|
|
|
|
const dynamic = services.filter(s => s.check !== 'static' && s.url);
|
|
const results = await Promise.allSettled(dynamic.map(checkService));
|
|
|
|
results.forEach((r, i) => {
|
|
if (r.status === 'fulfilled') {
|
|
const s = r.value;
|
|
const orig = services.find(x => x.name === s.name);
|
|
if (orig) { orig._status = s.status; orig._latency = s.latency; orig._meta = s.meta; }
|
|
}
|
|
});
|
|
|
|
const elapsed = Math.round(performance.now() - start);
|
|
document.getElementById('scan-time').textContent = elapsed;
|
|
document.getElementById('update-time').textContent = new Date().toLocaleTimeString('fr');
|
|
|
|
// Counters
|
|
const up = services.filter(s => (s._status || s.status) === 'up').length;
|
|
const down = services.filter(s => (s._status || s.status) === 'down').length;
|
|
const idle = services.filter(s => (s._status || s.status) === 'idle').length;
|
|
const agents = services.filter(s => s.badge === 'agent').length;
|
|
const modes = services.filter(s => s.badge === 'mode').length;
|
|
|
|
document.getElementById('counters').innerHTML = [
|
|
{n:services.length,l:'TOTAL'},
|
|
{n:up,l:'UP'},
|
|
{n:down,l:'DOWN'},
|
|
{n:idle,l:'IDLE'},
|
|
{n:agents,l:'AGENTS'},
|
|
{n:modes,l:'MODES'},
|
|
{n:19,l:'DOCKER'},
|
|
{n:52,l:'REPOS'},
|
|
].map(c => `<div class="counter"><div class="num">${c.n}</div><div class="label">${c.l}</div></div>`).join('');
|
|
|
|
render();
|
|
setTimeout(() => bar.style.width = '0%', 1500);
|
|
}
|
|
|
|
scan();
|
|
setInterval(scan, 15000);
|
|
</script>
|
|
<script src="/api/live-stats.js"></script>
|
|
<!-- WAVE 162 — Unified Pipeline Overlay -->
|
|
<div id="unifiedLiveOverlay" style="position:fixed;bottom:12px;right:12px;width:280px;max-height:calc(100vh - 120px);overflow-y:auto;background:linear-gradient(135deg,rgba(10,14,26,0.94),rgba(30,30,60,0.92));border:1px solid rgba(6,182,212,0.4);border-radius:10px;padding:10px;backdrop-filter:blur(14px);z-index:9999;font:600 9px Nunito,system-ui;color:#e2e8f0;box-shadow:0 4px 30px rgba(0,0,0,0.5)">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;padding-bottom:5px;border-bottom:1px solid rgba(100,116,139,0.3)">
|
|
<div style="font:900 10px Orbitron,system-ui;color:#06b6d4">🔴 <b id=closeLive style=cursor:pointer;margin-right:6px;color:gray onclick=unifiedLiveOverlay.remove()>x</b>UNIFIED LIVE</div>
|
|
<div id="ulo-ts" style="font-size:8px;color:#64748b"></div>
|
|
</div>
|
|
<div id="ulo-body">Loading...</div>
|
|
</div>
|
|
<script>
|
|
(function(){
|
|
const U='/api/weval-unified-pipeline.php';
|
|
async function tick(){
|
|
try{
|
|
const r=await fetch(U,{cache:'no-cache'});
|
|
if(!r.ok) return;
|
|
const d=await r.json();
|
|
const body=document.getElementById('ulo-body');
|
|
const ts=document.getElementById('ulo-ts');
|
|
if(!body) return;
|
|
const h=d.l99.health||'?';
|
|
const hc={GREEN:'#10b981',YELLOW:'#f59e0b',RED:'#ef4444'}[h]||'#64748b';
|
|
let html='<div style="background:'+hc+'15;border-left:3px solid '+hc+';padding:5px;margin-bottom:5px;border-radius:3px"><b style="color:'+hc+'">● '+h+'</b> L99 <b>'+d.l99.pass+'/'+d.l99.total+'</b><br><span style="color:#94a3b8">Disk '+d.system.disk_pct+'% Docker '+d.system.docker_count+' Crons '+d.system.cron_count+'</span></div>';
|
|
html+='<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-bottom:5px"><div style="background:rgba(6,182,212,0.1);border:1px solid rgba(6,182,212,0.3);border-radius:4px;padding:4px"><div style="font:800 8px Orbitron;color:#06b6d4">SOVEREIGN</div><b>'+d.providers.count+'</b> providers<br><b>'+d.ollama.models+'</b> Ollama<br><b>'+d.qdrant.collections.length+'</b> Qdrant</div><div style="background:rgba(139,92,246,0.1);border:1px solid rgba(139,92,246,0.3);border-radius:4px;padding:4px"><div style="font:800 8px Orbitron;color:#8b5cf6">PAPERCLIP</div><b>'+d.goals.length+'</b> goals<br><b>'+d.projects.length+'</b> projects<br><b>'+d.routines.length+'</b> routines</div></div>';
|
|
html+='<div style="background:rgba(245,158,11,0.1);border:1px solid rgba(245,158,11,0.3);border-radius:4px;padding:4px;margin-bottom:5px"><div style="font:800 8px Orbitron;color:#f59e0b">ETHICA</div><b>'+(d.ethica.hcps_validated/1000).toFixed(0)+'K</b> HCPs '+d.ethica.coverage.join(' ')+'</div>';
|
|
const rpa=d.routines_per_agent||{};
|
|
const top=Object.entries(rpa).sort((a,b)=>b[1]-a[1]).slice(0,5);
|
|
if(top.length){
|
|
html+='<div style="font:800 8px Orbitron;color:#10b981;margin:4px 0">TOP AGENTS</div>';
|
|
top.forEach(([n,c])=>{html+='<div style="display:flex;justify-content:space-between;padding:1px 3px;background:rgba(16,185,129,0.05);border-radius:2px;margin-bottom:1px"><span>'+n+'</span><b style="color:#10b981">'+c+'</b></div>';});
|
|
}
|
|
html+='<div style="margin-top:5px;padding-top:4px;border-top:1px solid rgba(100,116,139,0.3);font-size:8px;color:#64748b;text-align:center"><a href="/wevia-master.html" style="color:#06b6d4">Master</a> · <a href="/agents-archi.html" style="color:#06b6d4">Archi</a> · <a href="/wevia-meeting-rooms.html" style="color:#06b6d4">Rooms</a> · <a href="https://paperclip.weval-consulting.com" style="color:#06b6d4" target="_blank">Paperclip</a></div>';
|
|
body.innerHTML=html;
|
|
if(ts) ts.textContent=new Date().toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit',second:'2-digit'});
|
|
}catch(e){}
|
|
}
|
|
setTimeout(tick,1500);setInterval(tick,30000);
|
|
})();
|
|
</script>
|
|
|
|
|
|
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
|
|
<script>
|
|
(function(){
|
|
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
|
|
var d = document;
|
|
var m = d.createElement('div');
|
|
m.id = 'opus-udrill';
|
|
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
|
|
var inner = d.createElement('div');
|
|
inner.id = 'opus-udrill-in';
|
|
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
|
|
inner.addEventListener('click', function(e){ e.stopPropagation(); });
|
|
m.appendChild(inner);
|
|
m.addEventListener('click', function(){ m.style.display='none'; });
|
|
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
|
|
(d.body || d.documentElement).appendChild(m);
|
|
|
|
function openCard(card) {
|
|
// Clone card content + show close btn + increase font-size
|
|
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
|
|
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
|
|
inner.innerHTML = html;
|
|
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
|
|
m.style.display = 'flex';
|
|
}
|
|
|
|
function wire(root) {
|
|
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
|
|
var cards = root.querySelectorAll(sels);
|
|
for (var i = 0; i < cards.length; i++) {
|
|
var c = cards[i];
|
|
if (c.__opusWired) continue;
|
|
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
|
|
var r = c.getBoundingClientRect();
|
|
if (r.width < 60 || r.height < 40) continue;
|
|
c.__opusWired = true;
|
|
c.style.cursor = 'pointer';
|
|
c.setAttribute('role','button');
|
|
c.setAttribute('tabindex','0');
|
|
c.addEventListener('click', function(ev){
|
|
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
|
|
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
|
|
if (ev.target.closest('a,button,input,select')) return;
|
|
ev.preventDefault(); ev.stopPropagation();
|
|
openCard(this);
|
|
});
|
|
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
|
|
}
|
|
}
|
|
|
|
// Initial + mutation observer
|
|
var initRun = function(){ wire(d.body || d.documentElement); };
|
|
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
|
|
else initRun();
|
|
var mo = new MutationObserver(function(muts){
|
|
var newCard = false;
|
|
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
|
|
if (newCard) initRun();
|
|
});
|
|
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
|
|
})();
|
|
</script>
|
|
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
|
|
|
|
|
|
<!-- === OPUS HONEST NR/L99 OVERLAY v1 19avr - append-only doctrine #14 === -->
|
|
<script>
|
|
(function(){
|
|
if (window.__opusHonestOverlay) return; window.__opusHonestOverlay = true;
|
|
async function updateHonestValues(){
|
|
try {
|
|
const r = await fetch('/api/l99-honest.php', {cache:'no-store'});
|
|
const d = await r.json();
|
|
if (!d.ok) return;
|
|
const realNR = `${d.combined.pass}/${d.combined.total}`;
|
|
const realSigma = d.sigma;
|
|
// Find elements showing the myth values
|
|
const mythRegex = /(153\/153|304\/304|NR status 153\/153|L99 status 304\/304|NR 153\/153|L99 304\/304)/g;
|
|
// Walk text nodes
|
|
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
|
|
const toReplace = [];
|
|
let node;
|
|
while (node = walker.nextNode()) {
|
|
if (node.nodeValue && mythRegex.test(node.nodeValue)) toReplace.push(node);
|
|
}
|
|
toReplace.forEach(textNode => {
|
|
const parent = textNode.parentNode;
|
|
if (!parent || parent.hasAttribute('data-opus-honest-applied')) return;
|
|
const newText = textNode.nodeValue.replace(/153\/153/g, realNR).replace(/304\/304/g, realNR);
|
|
textNode.nodeValue = newText;
|
|
parent.setAttribute('data-opus-honest-applied', '1');
|
|
});
|
|
// Add a small badge bottom-right showing honest live status
|
|
if (!document.getElementById('opus-honest-badge')) {
|
|
const b = document.createElement('div');
|
|
b.id = 'opus-honest-badge';
|
|
b.style.cssText = 'position:fixed;bottom:12px;right:12px;background:linear-gradient(90deg,#14b8a6,#a855f7);color:#05060a;padding:6px 12px;font:10px/1.3 Inter,system-ui,sans-serif;font-weight:700;border-radius:8px;z-index:99993;box-shadow:0 4px 12px rgba(0,0,0,0.3);cursor:pointer;max-width:280px';
|
|
b.title = 'Cliquer pour détails';
|
|
b.innerHTML = `✓ NR ${realNR} · ${realSigma} live`;
|
|
b.onclick = () => {
|
|
alert(`HONEST NonReg (doctrine #4):\n\nmaster: ${d.master.pass}/${d.master.total}\nopus: ${d.opus.pass}/${d.opus.total}\ncombined: ${realNR}\nsigma: ${realSigma}\n\n${d.myth_153}\n${d.myth_304}`);
|
|
};
|
|
document.body.appendChild(b);
|
|
}
|
|
} catch(e){console.error('L99-honest fetch error:', e);}
|
|
}
|
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', updateHonestValues);
|
|
else updateHonestValues();
|
|
setInterval(updateHonestValues, 90000);
|
|
})();
|
|
</script>
|
|
<!-- === OPUS HONEST END === -->
|
|
|
|
|
|
<script src="/api/a11y-auto-enhancer.js" defer></script>
|
|
<!-- WTP_UDOCK_V1 (Opus 21-avr t33b5) --><script src="/wtp-unified-dock.js" defer></script>
|
|
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
|
|
<!-- DOCTRINE-60-UX-JS --><script id="doctrine60-ux-js-direct">
|
|
|
|
// DOCTRINE-60-UX-JS staggered entrance
|
|
(function(){
|
|
if (!('IntersectionObserver' in window)) return;
|
|
const obs = new IntersectionObserver((entries) => {
|
|
entries.forEach((e, i) => {
|
|
if (e.isIntersecting) {
|
|
setTimeout(() => e.target.classList.add('enter-stagger'), i * 80);
|
|
obs.unobserve(e.target);
|
|
}
|
|
});
|
|
});
|
|
document.querySelectorAll('.card, .kpi, .panel').forEach(el => obs.observe(el));
|
|
})();
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|