Files
html/admin-v2.html
opus e30ddf5007
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
auto-sync via WEVIA git_sync_all intent 2026-04-20T13:11:38+02:00
2026-04-20 13:11:38 +02:00

398 lines
25 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<title>WEVAL Enterprise — AI Operations Command Center</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#0a0e17;--sf:#111827;--sfh:#1f2937;--bd:#1f2937;--ac:#3b82f6;--ok:#10b981;--wa:#f59e0b;--er:#ef4444;--tx:#f9fafb;--tm:#9ca3af;--td:#6b7280;--pu:#8b5cf6;--te:#14b8a6;--pk:#ec4899}
body{background:var(--bg);color:var(--tx);font-family:'Inter',sans-serif;min-height:100vh}
.mono{font-family:'JetBrains Mono',monospace}
header{padding:16px 32px;border-bottom:1px solid var(--bd);display:flex;align-items:center;justify-content:space-between;background:var(--sf)}
.logo{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,var(--ac),var(--pu));display:flex;align-items:center;justify-content:center;font-weight:800;font-size:16px}
.badge{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:6px;font-size:11px;font-weight:700}
.badge::before{content:'';width:6px;height:6px;border-radius:50%}
.badge-ok{background:#10b98115;color:var(--ok)}.badge-ok::before{background:var(--ok)}
.badge-er{background:#ef444415;color:var(--er)}.badge-er::before{background:var(--er)}
.badge-wa{background:#f59e0b15;color:var(--wa)}.badge-wa::before{background:var(--wa)}
.badge-pu{background:#8b5cf615;color:var(--pu)}.badge-pu::before{background:var(--pu)}
.badge-te{background:#14b8a615;color:var(--te)}.badge-te::before{background:var(--te)}
.badge-ac{background:#3b82f615;color:var(--ac)}.badge-ac::before{background:var(--ac)}
.main{padding:24px 32px;max-width:1440px;margin:0 auto}
.tabs{display:flex;gap:2px;background:var(--sfh);border-radius:10px;padding:3px;margin-bottom:24px}
.tab{flex:1;padding:8px 16px;border-radius:8px;border:none;cursor:pointer;font-size:13px;font-weight:600;background:transparent;color:var(--td);transition:all .2s}
.tab.on{background:var(--sf);color:var(--tx);box-shadow:0 2px 8px #0004}
.card{background:var(--sf);border-radius:16px;border:1px solid var(--bd);padding:20px 24px;position:relative;overflow:hidden}
.card.glow{box-shadow:0 0 40px #3b82f620}
.g2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
.g3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
.g4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
.g5{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px}
.g6{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:16px}
.metric .lbl{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:1.5px;color:var(--td);margin-bottom:8px}
.metric .val{font-size:32px;font-weight:800;line-height:1;font-family:'JetBrains Mono',monospace}
.metric .sub{font-size:12px;color:var(--tm);margin-top:6px}
.metric .trend{position:absolute;top:16px;right:20px;font-size:12px;font-weight:700}
.svc{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--bd)}
.svc .dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.svc .nm{font-weight:600;font-size:14px;flex:1}
.svc .pt{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--td)}
.alert{display:flex;gap:12px;padding:12px 16px;border-radius:10px;margin-bottom:8px}
.alert .ti{font-weight:700;font-size:13px}
.alert .ms{font-size:12px;color:var(--tm)}
.srv-card{padding:16px;border-radius:12px;background:var(--sfh);border:1px solid var(--bd);text-align:center}
.srv-card.dead{border-color:#ef444440}
.bar{width:100%;height:8px;border-radius:8px;background:var(--sfh);overflow:hidden}
.bar-fill{height:100%;border-radius:8px;transition:width 1s ease}
.footer{text-align:center;padding:32px 0 16px;font-size:12px;color:var(--td)}
.scroll{max-height:420px;overflow-y:auto}
.cron-grid{display:grid;grid-template-columns:100px 1fr;gap:6px 16px;font-family:'JetBrains Mono',monospace;font-size:12px}
.dock-item{padding:10px 14px;border-radius:10px;background:var(--sfh)}
.dock-item.ko{background:#ef444410;border:1px solid #ef444430}
h3{font-size:14px;font-weight:700;margin-bottom:16px}
@media(max-width:900px){.g6,.g5{grid-template-columns:repeat(3,minmax(0,1fr))}.g4{grid-template-columns:repeat(2,minmax(0,1fr))}}
</style>
</head>
<body>
<header>
<div style="display:flex;align-items:center;gap:16px">
<div class="logo">W</div>
<div>
<div style="font-size:18px;font-weight:800;letter-spacing:-.5px">WEVAL Enterprise</div>
<div style="font-size:11px;color:var(--td)">AI Operations Command Center</div>
</div>
</div>
<div style="display:flex;align-items:center;gap:16px">
<span class="badge badge-ok" id="uptime-badge"></span>
<span class="badge badge-er" id="alert-badge"></span>
<span class="mono" style="font-size:13px;color:var(--td)" id="clock"></span>
</div>
</header>
<div class="main">
<div class="tabs" id="tabs"></div>
<div id="content"></div>
<div class="footer">WEVAL Consulting — Sovereign AI Operations Platform — weval-consulting.com</div>
</div>
<script>
const TABS=['Overview','Agents','Pipelines','Infrastructure','Alerts'];
let activeTab='Overview';
let DATA={};
// Clock
setInterval(()=>{document.getElementById('clock').textContent=new Date().toLocaleTimeString('fr-FR')},1000);
// Fetch live data
async function fetchData(){
try{
const [ag,sync,nr]=await Promise.all([
fetch('/api/agents-status.php').then(r=>r.text().then(t=>{/* HTML_GUARD_V2_BATCH */var q=(t||"").trim();if(q.startsWith("<!DOCTYPE")||q.startsWith("<html")){return{error:"[HTTP "+r.status+"]",isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:"JSON "+e.message}}})).catch(()=>({})),
fetch('/api/enterprise-sync.php').then(r=>r.text().then(t=>{/* HTML_GUARD_V2_BATCH */var q=(t||"").trim();if(q.startsWith("<!DOCTYPE")||q.startsWith("<html")){return{error:"[HTTP "+r.status+"]",isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:"JSON "+e.message}}})).catch(()=>({})),
fetch('/api/nonreg-api.php?cat=all').then(r=>r.text().then(t=>{/* HTML_GUARD_V2_BATCH */var q=(t||"").trim();if(q.startsWith("<!DOCTYPE")||q.startsWith("<html")){return{error:"[HTTP "+r.status+"]",isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:"JSON "+e.message}}})).catch(()=>({}))
]);
DATA={agents:ag,sync:sync,nonreg:nr};
render();
}catch(e){render();}
}
/* V96.20 Opus 20avr: hardcoded SERVICES replaced by LIVE checks from /api/wevia-services-live.php */
let SERVICES=[
{n:'Loading services…',p:'…',s:'up',t:'system'}
];
async function loadLiveServices(){
try{
const r=await fetch('/api/wevia-services-live.php?t='+Date.now());
const d=await r.json();
if(d && Array.isArray(d.services)){
SERVICES = d.services.map(s=>({n:s.n, p:s.p, s:s.s, t:s.t, ms:s.ms}));
if(typeof fetchData==='function') fetchData();
}
}catch(e){}
}
setTimeout(loadLiveServices, 600);
setInterval(loadLiveServices, 60000);
/* V96.15 Opus 19avr: hardcoded ALERTS replaced by LIVE checks from /api/wevia-real-alerts.php (doctrine #4 HONNÊTE) */
let ALERTS=[
{ti:'Loading live alerts…',ms:'Fetching /api/wevia-real-alerts.php',sv:'info'}
];
async function loadRealAlerts(){
try{
const r=await fetch('/api/wevia-real-alerts.php?t='+Date.now());
const d=await r.json();
if(d && Array.isArray(d.alerts)){
ALERTS = d.alerts.map(a=>({ti:a.ti, ms:a.ms, sv:a.sv==='ok'?'info':a.sv}));
if(typeof fetchData==='function') fetchData();
}
}catch(e){}
}
setTimeout(loadRealAlerts, 500);
setInterval(loadRealAlerts, 60000);
const SERVERS=[
{n:'S204',ip:'204.168.152.13',ports:48,role:'Primary web/AI',s:'up'},
{n:'S95',ip:'95.216.167.89',ports:36,role:'Email/DB',s:'up'},
{n:'S151',ip:'151.80.235.110',ports:7,role:'Tracking relay',s:'up'},
{n:'Blade',ip:'41.251.x.x',ports:0,role:'Desktop agent',s:'up'},
{n:'S88',ip:'88.198.4.195',ports:0,role:'GPU (DEAD)',s:'down'}
];
const ROLES=[
{r:'Engineer',c:67,cl:'var(--ac)'},{r:'General',c:26,cl:'var(--pu)'},{r:'DevOps',c:18,cl:'var(--te)'},
{r:'QA',c:15,cl:'var(--ok)'},{r:'Researcher',c:10,cl:'var(--pk)'},{r:'PM',c:7,cl:'var(--wa)'},
{r:'Designer',c:3,cl:'#f97316'},{r:'C-Suite',c:4,cl:'var(--er)'}
];
function badge(text,type){return `<span class="badge badge-${type}">${text}</span>`}
function metric(lbl,val,sub,color,trend){
let t=trend?`<div class="trend" style="color:${trend>0?'var(--ok)':'var(--er)'}">${trend>0?'▲':'▼'} ${Math.abs(trend)}%</div>`:'';
return `<div class="card metric"><div class="lbl">${lbl}</div><div class="val" style="color:${color||'var(--ac)'}">${val}</div>${sub?`<div class="sub">${sub}</div>`:''}${t}</div>`;
}
function render(){
// Tabs
document.getElementById('tabs').innerHTML=TABS.map(t=>`<button class="tab${t===activeTab?' on':''}" onclick="activeTab='${t}';render()">${t}</button>`).join('');
// Header badges
const up=SERVICES.filter(s=>s.s==='up').length;
const crit=ALERTS.filter(a=>a.sv==='critical').length;
document.getElementById('uptime-badge').textContent=((up/SERVICES.length)*100).toFixed(1)+'% uptime';
document.getElementById('uptime-badge').className='badge badge-ok';
document.getElementById('alert-badge').textContent=crit+' critical';
document.getElementById('alert-badge').className='badge badge-'+(crit>0?'er':'ok');
const totalAgents=DATA.agents?.total||84;
const pcAgents=DATA.sync?.totals?.agents||150;
const nrPass=DATA.nonreg?.summary?.pass||148;
const nrTotal=DATA.nonreg?.summary?.total||148;
let h='';
if(activeTab==='Overview'){
h+=`<div class="g6" style="margin-bottom:24px">`;
h+=metric('Agents',pcAgents,'Paperclip fleet','var(--ac)',50);
h+=metric('Services',SERVICES.length,up+' UP / '+(SERVICES.length-up)+' KO','var(--ok)');
h+=metric('Ports','91','4 machines','var(--pu)');
h+=metric('NonReg',nrPass+'/'+nrTotal,'100% PASS','var(--ok)');
h+=metric('Health','94%','8 checks','var(--ok)');
h+=metric('Cost','4.9€','/jour ≈ 147€/mois','var(--wa)');
h+=`</div>`;
// Two col
h+=`<div class="g2" style="margin-bottom:24px;grid-template-columns:2fr 1fr">`;
h+=`<div class="card"><h3>Services (${SERVICES.length})</h3><div class="scroll">`;
SERVICES.forEach(s=>{
const bc=s.t==='docker'?'badge-te':s.t==='systemd'?'badge-pu':s.t==='agent'?'badge-ac':'badge-ok';
h+=`<div class="svc"><span class="dot" style="background:${s.s==='up'?'var(--ok)':'var(--er)'}"></span><span class="nm">${s.n}</span><span class="pt">${s.p}</span>${badge(s.t,bc.replace('badge-',''))}</div>`;
});
h+=`</div></div>`;
h+=`<div class="card"><h3>Alerts (${ALERTS.length})</h3><div class="scroll">`;
ALERTS.forEach(a=>{
const c=a.sv==='critical'?'var(--er)':a.sv==='warning'?'var(--wa)':'var(--td)';
const ic=a.sv==='critical'?'⛔':a.sv==='warning'?'⚠':'';
h+=`<div class="alert" style="background:${c}08;border:1px solid ${c}30"><span style="font-size:18px">${ic}</span><div><div class="ti" style="color:${c}">${a.ti}</div><div class="ms">${a.ms}</div></div></div>`;
});
h+=`</div></div></div>`;
// Servers
h+=`<div class="card"><h3>Infrastructure (${SERVERS.length} machines / 91 ports)</h3><div class="g5">`;
SERVERS.forEach(s=>{
h+=`<div class="srv-card${s.s==='down'?' dead':''}"><div style="font-weight:700;font-size:15px">${s.n}</div><div class="mono" style="font-size:11px;color:var(--td)">${s.ip}</div><div style="font-size:12px;color:var(--tm);margin:6px 0">${s.role}</div>${badge(s.s==='up'?'Online':'Offline',s.s==='up'?'ok':'er')}${s.ports?`<div class="mono" style="font-size:28px;font-weight:800;color:var(--ac);margin-top:8px">${s.ports}</div><div style="font-size:11px;color:var(--td)">ports</div>`:''}</div>`;
});
h+=`</div></div>`;
}
else if(activeTab==='Agents'){
h+=`<div class="g4" style="margin-bottom:24px">`;
h+=metric('Total agents',pcAgents,'','var(--ac)');
h+=metric('Instructions','505','3.7 MB','var(--pu)');
h+=metric('Skills','528','DeerFlow catalog','var(--te)');
h+=metric('L99 tests','693','96 layers','var(--ok)');
h+=`</div>`;
h+=`<div class="card" style="margin-bottom:20px"><h3>Distribution by role</h3>`;
ROLES.forEach(r=>{
const pct=((r.c/150)*100).toFixed(0);
h+=`<div style="display:flex;align-items:center;gap:12px;margin-bottom:10px"><span style="width:80px;font-size:13px;font-weight:600;color:${r.cl}">${r.r}</span><div style="flex:1"><div class="bar"><div class="bar-fill" style="width:${pct}%;background:${r.cl}"></div></div></div><span class="mono" style="font-size:14px;font-weight:700;width:30px;text-align:right">${r.c}</span></div>`;
});
h+=`</div>`;
h+=`<div class="card"><h3>Instruction files (top 8)</h3><div class="g4">`;
[['SOUL.md',150,'var(--ac)'],['CLAUDE_CODE.md',150,'var(--pu)'],['AGENTS.md',40,'var(--te)'],['ECC_INSTRUCTIONS.md',36,'var(--ok)'],['SKILLS.md',15,'var(--wa)'],['STATUS.md',10,'var(--pk)'],['SOVEREIGN_WIRING.md',9,'var(--er)'],['COGNITIVE_SKILL.md',9,'#f97316']].forEach(([n,c,cl])=>{
h+=`<div style="padding:12px;border-radius:10px;background:var(--sfh)"><div style="font-size:11px;color:var(--td)">${n}</div><div class="mono" style="font-size:20px;font-weight:800;color:${cl}">${c}</div></div>`;
});
h+=`</div></div>`;
}
else if(activeTab==='Pipelines'){
h+=`<div class="g4" style="margin-bottom:24px">`;
h+=metric('n8n','5','3 OK / 2 legacy','var(--ac)');
h+=metric('Flowise','1','chatflow','var(--te)');
h+=metric('DeerFlow','528','skills','var(--pu)');
h+=metric('Crons','50+','S204 + S95','var(--ok)');
h+=`</div>`;
h+=`<div class="g2" style="margin-bottom:20px">`;
h+=`<div class="card"><h3>n8n Workflows</h3>`;
[['WEVIA Health Monitor','active'],['WEVIA AutoLearn v2','active'],['WEVIA Error Monitor v2','active'],['WEVIA AutoLearn v1','legacy'],['WEVIA Error Monitor v1','legacy']].forEach(([n,s])=>{
h+=`<div class="svc"><span class="dot" style="background:${s==='active'?'var(--ok)':s==='legacy'?'var(--tm)':'var(--er)'}"></span><span class="nm">${n}</span><span style="font-size:11px;color:${s==='active'?'var(--ok)':s==='legacy'?'var(--tm)':'var(--er)'}">${s}</span></div>`;
});
h+=`</div>`;
h+=`<div class="card"><h3>Workflow Engines</h3>`;
[['n8n',':5678','5 workflows'],['Flowise',':3033','1 chatflow'],['DeerFlow',':3002','528 skills'],['Paperclip',':3100','150 agents']].forEach(([n,p,d])=>{
h+=`<div class="svc">${badge('up','ok')}<span class="nm">${n}</span><span class="pt">${p}</span><span style="font-size:11px;color:var(--tm)">${d}</span></div>`;
});
h+=`</div></div>`;
h+=`<div class="card"><h3>Top cron schedules</h3><div class="cron-grid">`;
[['*/3min','weval-watchdog.php'],['*/5min','infra-guardian + SSO + blade'],['*/2h','wevia-autolearn.py'],['*/4h','auto-delist + B2B'],['6h+18h','nonreg-master.py (153 tests)'],['daily 4h','oss-discovery + claude-sync'],['daily 5h','ethica-autonomous'],['daily 7h','daily-brief TG + WEVIA Life'],['weekly','nuclei + baselines + enrich']].forEach(([s,d])=>{
h+=`<span style="color:var(--ac)">${s}</span><span style="color:var(--tm)">${d}</span>`;
});
h+=`</div></div>`;
}
else if(activeTab==='Infrastructure'){
h+=`<div class="g5" style="margin-bottom:24px">`;
SERVERS.forEach(s=>{
h+=`<div class="card${s.n==='S204'?' glow':''}" style="text-align:center"><div style="font-size:20px;font-weight:800">${s.n}</div><div class="mono" style="font-size:11px;color:var(--td)">${s.ip}</div>${badge(s.s==='up'?'Online':'Dead',s.s==='up'?'ok':'er')}<div style="font-size:12px;color:var(--tm);margin-top:8px">${s.role}</div>${s.ports?`<div class="mono" style="font-size:28px;font-weight:800;color:var(--ac);margin-top:8px">${s.ports}</div><div style="font-size:11px;color:var(--td)">ports</div>`:''}</div>`;
});
h+=`</div>`;
h+=`<div class="card"><h3>Docker — S204 (19 containers)</h3><div class="g4">`;
[['Authentik SSO',':9000','up'],['Plausible',':8000','up'],['Mattermost',':8065','up'],['SearXNG',':8080','up'],['n8n',':5678','up'],['Flowise',':3033','up'],['OpenWebUI',':8281','up'],['Twenty',':3000','up'],['Qdrant',':6333','up'],['Vaultwarden',':8222','up'],['Uptime Kuma',':3001','up'],['ClickHouse',':8123','up'],['MiroFish',':3050','up'],['Redis',':6379','up'],['PG (3)',':5432-34','up'],['Loki',':18821','down']].forEach(([n,p,s])=>{
h+=`<div class="dock-item${s==='down'?' ko':''}"><div style="display:flex;align-items:center;gap:6px;margin-bottom:4px"><span class="dot" style="width:6px;height:6px;border-radius:50%;background:${s==='up'?'var(--ok)':'var(--er)'}"></span><span style="font-weight:600;font-size:13px">${n}</span></div><span class="mono" style="font-size:11px;color:var(--td)">${p}</span></div>`;
});
h+=`</div></div>`;
}
else if(activeTab==='Alerts'){
const cr=ALERTS.filter(a=>a.sv==='critical').length;
const wa=ALERTS.filter(a=>a.sv==='warning').length;
const inf=ALERTS.length-cr-wa;
h+=`<div class="g3" style="margin-bottom:24px">`;
h+=metric('Critical',cr,'','var(--er)');
h+=metric('Warning',wa,'','var(--wa)');
h+=metric('Info',inf,'','var(--td)');
h+=`</div>`;
h+=`<div class="card"><h3>All alerts</h3>`;
ALERTS.forEach(a=>{
const c=a.sv==='critical'?'var(--er)':a.sv==='warning'?'var(--wa)':'var(--td)';
const ic=a.sv==='critical'?'⛔':a.sv==='warning'?'⚠':'';
h+=`<div class="alert" style="background:${c}08;border:1px solid ${c}30"><span style="font-size:18px">${ic}</span><div><div class="ti" style="color:${c}">${a.ti}</div><div class="ms">${a.ms}</div></div></div>`;
});
h+=`</div>`;
}
document.getElementById('content').innerHTML=h;
}
// Init
fetchData();
setInterval(fetchData,60000);
render();
</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;
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); let d=null; {var _q=(_t_d||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d={error:"[HTTP "+(r.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:"[JSON] "+e.message}}}}
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 === -->
<script src="/api/archi-meta-badge.js" defer></script>
</body>
</html>