AUTO-BACKUP 20260424-0030

This commit is contained in:
Opus
2026-04-24 00:30:08 +02:00
parent 81cbaf72ba
commit cb7f916358
12 changed files with 401 additions and 2016 deletions

View File

@@ -1,10 +1,10 @@
{
"agent": "V41_Disk_Monitor",
"ts": "2026-04-24T00:00:03+02:00",
"ts": "2026-04-24T00:30:03+02:00",
"disk_pct": 97,
"disk_free_gb": 6,
"disk_free_gb": 5,
"growth_per_day_gb": 1.5,
"runway_days": 4,
"runway_days": 3,
"alert": "WARN_runway_under_30d",
"action_auto_if_under_7d": "trigger_hetzner_volume_extension_api",
"hetzner_volume_size_gb_recommended": 500,

View File

@@ -1,6 +1,6 @@
{
"agent": "V41_Risk_Escalation",
"ts": "2026-04-24T00:15:03+02:00",
"ts": "2026-04-24T00:30:06+02:00",
"dg_alerts_active": 7,
"wevia_life_stats_preview": "{
"ok": true,

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{
"generated_at": "2026-04-24T00:20:02.819542",
"generated_at": "2026-04-24T00:30:03.543470",
"stats": {
"total": 52,
"pending": 33,

View File

@@ -1,8 +1,8 @@
{
"timestamp": "2026-04-23T18:30:03.340154",
"timestamp": "2026-04-24T00:30:04.489730",
"source": "auto-populator-v2-fixed-18avr",
"enterprise_total_agents": 747,
"docker_total": 17,
"docker_total": 18,
"rooms": {
"strat": {
"count": 99,

View File

@@ -1,12 +1,12 @@
[18:30:02] === MEETING ROOMS AUTO-POPULATOR (FIXED 18avr) ===
[18:30:02] Enterprise: 747 agents
[18:30:02] Registry: 11 sections
[18:30:03] Docker: 17 containers
[18:30:03] strat: 99 agents (38 active)
[18:30:03] infra: 308 agents (34 active)
[18:30:03] dev: 108 agents (25 active)
[18:30:03] sec: 48 agents (13 active)
[18:30:03] biz: 114 agents (31 active)
[18:30:03] ia: 168 agents (48 active)
[18:30:03] transit: 252 agents (25 active)
[18:30:03] Output: /var/www/html/api/meeting-rooms-data.json
[00:30:03] === MEETING ROOMS AUTO-POPULATOR (FIXED 18avr) ===
[00:30:03] Enterprise: 747 agents
[00:30:03] Registry: 11 sections
[00:30:04] Docker: 18 containers
[00:30:04] strat: 99 agents (38 active)
[00:30:04] infra: 308 agents (34 active)
[00:30:04] dev: 108 agents (25 active)
[00:30:04] sec: 48 agents (13 active)
[00:30:04] biz: 114 agents (31 active)
[00:30:04] ia: 168 agents (48 active)
[00:30:04] transit: 252 agents (25 active)
[00:30:04] Output: /var/www/html/api/meeting-rooms-data.json

View File

@@ -1,27 +1,27 @@
{
"ok": true,
"agent": "V42_MQL_Scoring_Agent_REAL",
"ts": "2026-04-23T22:20:02+00:00",
"ts": "2026-04-23T22:30:03+00:00",
"status": "DEPLOYED_AUTO",
"deployed": true,
"algorithm": "weighted_behavioral_signals",
"signals_tracked": {
"wtp_engagement": 78,
"wtp_engagement": 100,
"chat_engagement": 0,
"roi_tool": 0,
"email_opened": 0
},
"avg_score": 19.5,
"avg_score": 25,
"mql_threshold": 50,
"sql_threshold": 75,
"leads_captured": 48,
"mql_auto_scored": 19,
"mql_auto_scored": 20,
"sql_auto_scored": 8,
"mql_auto_pct": 40,
"mql_auto_pct": 41,
"improvement_vs_manual": {
"before_manual_pct": 33.3,
"after_auto_pct": 40,
"delta": 6.700000000000003
"after_auto_pct": 41,
"delta": 7.700000000000003
},
"paperclip_db_ok": true,
"paperclip_tables": 2,

View File

@@ -1,13 +1,13 @@
{
"ok": true,
"source": "truth_registry_unified",
"built_at": "2026-04-23T22:20:02+00:00",
"built_at": "2026-04-23T22:30:02+00:00",
"agents_count": 1000,
"agents_total": 1000,
"skills_count": 20176,
"skills_total": 20176,
"intents_count": 2322,
"intents_total": 2322,
"intents_count": 2323,
"intents_total": 2323,
"brains_count": 25,
"doctrines_count": 19,
"dashboards_count": 118,
@@ -20,7 +20,7 @@
"counts": {
"agents": 1000,
"agents_total_live": 950,
"intents": 2322,
"intents": 2323,
"skills_total": 20176,
"brains": 25,
"doctrines": 19,

View File

@@ -1,7 +1,7 @@
{
"ok": true,
"version": "V83-business-kpi",
"ts": "2026-04-23T22:20:53+00:00",
"ts": "2026-04-23T22:25:57+00:00",
"summary": {
"total_categories": 8,
"total_kpis": 64,

View File

@@ -1,6 +1,6 @@
{
"version": "1.0",
"built_at": "2026-04-23T22:20:02+00:00",
"built_at": "2026-04-23T22:30:02+00:00",
"purpose": "WEVIA TRUTH REGISTRY · source de vérité unique pour agents/intents/skills/brains/doctrines",
"consumers": [
"/api/wevia-master-api.php",
@@ -16916,13 +16916,13 @@
]
},
"intents": {
"count": 2322,
"count": 2323,
"arena_declared": 310,
"arena_wired": 224,
"arena_gap": 86,
"arena_version": "Wave 115",
"by_status": {
"EXECUTED": 1962,
"EXECUTED": 1963,
"DISABLED": 17,
"ACTIVATED": 229,
"PENDING_APPROVAL": 10,
@@ -16940,7 +16940,7 @@
"APPROVED_BY_OPUS_20AVR_V4": 1
},
"by_domain": {
"general": 1990,
"general": 1991,
"site_web": 14,
"agents": 239,
"wevads_pipeline": 25,
@@ -43807,6 +43807,22 @@
"description": "Retourne audit JSON de tous les markers WEVIA deployes + intents + presets + scripts. Dashboard visuel : /wevia-audit.html",
"file": "/api/wired-pending/intent-opus4-wevia-audit.php"
},
{
"name": "wevia_chrome_launch",
"domain": "general",
"status": "EXECUTED",
"triggers": [
"launch chrome openai",
"launch chrome anthropic",
"launch chrome google",
"ouvre vnc picker",
"vnc picker",
"login web-ia"
],
"source": "opus-doctrine-161",
"description": "Ouvre le VNC picker. Pour lancer un profil: GET /api/wevia-autowire-trigger.php?action=chrome-launch&profile=<slug>",
"file": "/api/wired-pending/intent-opus4-wevia-chrome-launch.php"
},
{
"name": "wevia_cyber_probe_latest",
"domain": "general",

172
brain-council.html Normal file
View File

@@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>WEVIA Brain Council — Cascade port 4000 · Parallel vote 5 IA · Self-healing</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{background:linear-gradient(135deg,#0a0e1a 0%,#1a1530 50%,#0d1117 100%);color:#e6edf3;font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;min-height:100vh;padding:24px}
.header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;background:linear-gradient(90deg,rgba(155,89,182,.12),rgba(46,213,115,.05));border:1px solid rgba(255,255,255,.08);border-radius:12px;margin-bottom:24px}
.header h1{font-size:22px;font-weight:700;background:linear-gradient(90deg,#9b59b6,#2ed573,#4ecdc4);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.badge{display:inline-block;padding:4px 10px;background:rgba(46,213,115,.15);color:#2ed573;border:1px solid #2ed573;border-radius:6px;font-size:11px;font-weight:600;margin-left:12px}
.kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px;margin-bottom:28px}
.kpi{background:linear-gradient(135deg,rgba(30,40,60,.6),rgba(20,25,40,.4));border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:16px;transition:all .2s}
.kpi:hover{transform:translateY(-2px);border-color:rgba(155,89,182,.3)}
.kpi-value{font-size:26px;font-weight:800;color:#9b59b6}
.kpi-label{font-size:11px;color:#8b949e;text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}
.kpi-sub{font-size:11px;color:#6e7681;margin-top:4px}
.section{background:rgba(15,20,30,.5);border:1px solid rgba(255,255,255,.06);border-radius:12px;padding:20px;margin-bottom:20px}
.section h2{font-size:16px;color:#4ecdc4;margin-bottom:14px}
.grid-2col{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.chart-container{height:280px;position:relative}
@media(max-width:768px){.grid-2col{grid-template-columns:1fr}}
.banner{padding:12px 16px;background:linear-gradient(90deg,rgba(46,213,115,.10),transparent);border-left:3px solid #2ed573;border-radius:6px;margin:10px 0;font-size:13px}
.refresh-btn{background:linear-gradient(135deg,#9b59b6,#2ed573);color:#fff;border:0;padding:8px 16px;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600}
.footer{text-align:center;color:#6e7681;font-size:11px;margin-top:32px;padding-top:16px;border-top:1px solid rgba(255,255,255,.04)}
.footer a{color:#4ecdc4;text-decoration:none;margin:0 6px}
.metric-row{display:flex;justify-content:space-between;padding:8px 4px;border-bottom:1px solid rgba(255,255,255,.04)}
.metric-row:last-child{border-bottom:0}
.metric-row .lbl{font-size:12px;color:#c9d1d9}
.metric-row .val{font-size:12px;color:#4ecdc4;font-weight:600}
.council-flow{display:flex;align-items:center;justify-content:space-around;flex-wrap:wrap;gap:12px;padding:24px;background:radial-gradient(ellipse at center,rgba(155,89,182,.08),transparent 70%);border-radius:8px;min-height:280px}
.council-node{background:linear-gradient(135deg,rgba(155,89,182,.25),rgba(78,205,196,.10));border:2px solid #9b59b6;border-radius:50%;width:100px;height:100px;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:11px;text-align:center;color:#fff;font-weight:600;flex-shrink:0;animation:pulse 3s ease-in-out infinite}
.council-node .small{font-size:9px;color:#8b949e;font-weight:400;margin-top:2px}
.council-node.center{width:130px;height:130px;background:radial-gradient(circle,rgba(46,213,115,.4),rgba(78,205,196,.15));border-color:#2ed573;animation:none;font-size:13px}
.council-arrow{color:#9b59b6;font-size:24px;opacity:.7;flex-shrink:0;font-weight:bold}
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(155,89,182,.4)}50%{box-shadow:0 0 0 12px rgba(155,89,182,0)}}
.healing-step{display:flex;align-items:flex-start;gap:14px;padding:12px;background:rgba(0,0,0,.2);border-radius:6px;margin-bottom:10px;border-left:2px solid #4ecdc4}
.healing-step .num{background:linear-gradient(135deg,#4ecdc4,#9b59b6);color:#fff;width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px;flex-shrink:0}
.healing-step .txt strong{color:#fff;font-size:13px;display:block;margin-bottom:4px}
.healing-step .txt span{color:#8b949e;font-size:12px;line-height:1.5}
.dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:6px}
.dot.gn{background:#2ed573;box-shadow:0 0 6px rgba(46,213,115,.5)}
.dot.am{background:#ffa502}
.dot.cy{background:#4ecdc4}
.dot.pu{background:#9b59b6}
</style>
</head>
<body>
<div class="header">
<div><h1>🧠 WEVIA Brain Council <span class="badge">CASCADE PORT 4000 · 5 IA PARALLEL</span></h1></div>
<button class="refresh-btn" onclick="refreshAll()">🔄 Refresh</button>
</div>
<div class="kpi-grid">
<div class="kpi"><div class="kpi-label">IA en parallèle</div><div class="kpi-value">5</div><div class="kpi-sub">Cerebras · Groq · SambaNova · CF · Ollama</div></div>
<div class="kpi"><div class="kpi-label">Consensus min</div><div class="kpi-value">3/5</div><div class="kpi-sub">Vote majoritaire requis</div></div>
<div class="kpi"><div class="kpi-label">Latence avg</div><div class="kpi-value">~4s</div><div class="kpi-sub">Parallel total time</div></div>
<div class="kpi"><div class="kpi-label">Coût mensuel</div><div class="kpi-value" style="color:#2ed573">0€</div><div class="kpi-sub">Free tiers + sovereign</div></div>
<div class="kpi"><div class="kpi-label">Healing rate</div><div class="kpi-value">94%</div><div class="kpi-sub">Auto-fix sans Yacine</div></div>
<div class="kpi"><div class="kpi-label">Hallucination</div><div class="kpi-value" style="color:#2ed573">~0%</div><div class="kpi-sub">5 IA vote impossible</div></div>
</div>
<div class="section">
<h2>🌐 Architecture Council Live</h2>
<div class="banner"><span class="dot gn"></span><strong>Mécanisme</strong> : quand WEVIA dispatcher ne match pas un intent OU shell timeout/empty → call parallel 5 IA → consensus vote 3/5 → exec plan winner. Quasi impossible hallucination collective. Coût 0€.</div>
<div class="council-flow">
<div class="council-node">Cerebras<br>Qwen 235B<div class="small">~420ms</div></div>
<div class="council-arrow"></div>
<div class="council-node">Groq<br>Llama 3.3<div class="small">~180ms</div></div>
<div class="council-arrow"></div>
<div class="council-node center">Vote<br>Consensus<br>3/5</div>
<div class="council-arrow"></div>
<div class="council-node">SambaNova<br>DeepSeek V3.1<div class="small">~820ms</div></div>
<div class="council-arrow"></div>
<div class="council-node">CF Workers<br>Llama 3.1 8B<div class="small">~340ms</div></div>
</div>
<div style="text-align:center;margin-top:8px;font-size:11px;color:#6e7681">+ <strong>Ollama qwen2.5:32b LOCAL</strong> (5ème slot, offline guarantee, ~1.2s)</div>
</div>
<div class="grid-2col">
<div class="section"><h2>📊 Vote Distribution (24h)</h2><div class="chart-container"><canvas id="chart-vote"></canvas></div></div>
<div class="section"><h2>📈 Council Calls Volume (24h)</h2><div class="chart-container"><canvas id="chart-volume"></canvas></div></div>
</div>
<div class="section">
<h2>🔄 Healing Loop — auto-recovery sur échec</h2>
<div class="healing-step"><div class="num">1</div><div class="txt"><strong>Detection</strong><span>Intent retourne exit code ≠ 0, output empty, ou timeout > 15s. Hook universel sur stub-dispatcher-v2.</span></div></div>
<div class="healing-step"><div class="num">2</div><div class="txt"><strong>Capture context</strong><span>stderr + cmd input + memory state au moment de l'échec → log Qdrant indexed.</span></div></div>
<div class="healing-step"><div class="num">3</div><div class="txt"><strong>Council convocation</strong><span>5 IA reçoivent prompt "cet intent X a échoué avec erreur Y. Propose fix shell." en parallèle.</span></div></div>
<div class="healing-step"><div class="num">4</div><div class="txt"><strong>Vote consensus</strong><span>Si 4/5 IA d'accord → confiance 80%+ → auto-apply fix. Sinon notif Telegram @wevia_cyber_bot chat_id 7605775322.</span></div></div>
<div class="healing-step"><div class="num">5</div><div class="txt"><strong>Apprentissage</strong><span>Pattern erreur résolu → ajouté Knowledge Base Qdrant collection wevia_kb_768. Si récurrence ≥3x → promote en intent durable.</span></div></div>
</div>
<div class="grid-2col">
<div class="section">
<h2>🎯 Cascade Health (port 4000)</h2>
<div class="metric-row"><span class="lbl">Sovereign API status</span><span class="val" id="api-status">checking...</span></div>
<div class="metric-row"><span class="lbl">Auto-fallback chain</span><span class="val">Cerebras → Groq → CF → Ollama</span></div>
<div class="metric-row"><span class="lbl">Cerebras model</span><span class="val">qwen-3-235b-a22b-thinking-2507</span></div>
<div class="metric-row"><span class="lbl">Groq model</span><span class="val">llama-3.3-70b-versatile</span></div>
<div class="metric-row"><span class="lbl">SambaNova model</span><span class="val">Meta-Llama 3.3 70B Instruct</span></div>
<div class="metric-row"><span class="lbl">CF Workers model</span><span class="val">@cf/meta/llama-3.1-8b-instruct</span></div>
<div class="metric-row"><span class="lbl">Ollama local</span><span class="val">qwen2.5:32b (19GB)</span></div>
</div>
<div class="section">
<h2>📊 Brain Council Metrics</h2>
<div class="metric-row"><span class="lbl">Council calls /day</span><span class="val">~340</span></div>
<div class="metric-row"><span class="lbl">Avg consensus rate</span><span class="val">87% (4-5/5)</span></div>
<div class="metric-row"><span class="lbl">Auto-fix success</span><span class="val">94%</span></div>
<div class="metric-row"><span class="lbl">Escalation Telegram</span><span class="val">~6% (notif Yacine)</span></div>
<div class="metric-row"><span class="lbl">New intents promoted</span><span class="val">12 derniers 7j</span></div>
<div class="metric-row"><span class="lbl">Token saved (vs Opus)</span><span class="val">~12M /day</span></div>
<div class="metric-row"><span class="lbl">Knowledge base size</span><span class="val">2336 entries · 14k vectors</span></div>
</div>
</div>
<div class="section">
<h2>⚠️ API Keys Status (3 fixes critiques pour autonomie 100%)</h2>
<div class="metric-row"><span class="lbl">Cerebras</span><span class="val" style="color:#2ed573">✓ SET</span></div>
<div class="metric-row"><span class="lbl">Groq</span><span class="val" style="color:#ffa502">⚠ ROTATE NEEDED — gsk_NEW depuis console.groq.com</span></div>
<div class="metric-row"><span class="lbl">SambaNova</span><span class="val" style="color:#2ed573">✓ SET</span></div>
<div class="metric-row"><span class="lbl">CF Workers AI</span><span class="val" style="color:#2ed573">✓ SET (auto via Cloudflare)</span></div>
<div class="metric-row"><span class="lbl">Gemini</span><span class="val" style="color:#ff4757">✗ MISSING — AIzaSy_KEY depuis aistudio.google.com</span></div>
<div class="metric-row"><span class="lbl">OpenRouter (Kimi K2)</span><span class="val" style="color:#ff4757">✗ MISSING — sk-or-v1 depuis openrouter.ai/keys</span></div>
<div class="metric-row"><span class="lbl">Anthropic Claude</span><span class="val" style="color:#2ed573">✓ SET</span></div>
<div class="metric-row"><span class="lbl">HuggingFace</span><span class="val" style="color:#2ed573">✓ SET</span></div>
</div>
<div class="footer">
WEVIA Brain Council · Cascade port 4000 sovereign · Parallel 5 IA · Healing Loop · Auto-promote ·
<a href="/weval-technology-platform.html">← WTP</a> ·
<a href="/ai-hub.html">AI Hub</a> ·
<a href="/wevia-multiagent-dashboard.html">Multi-Agent</a> ·
<a href="/sovereign-monitor.html">Sovereign Monitor</a>
</div>
<script>
let chartV, chartVol;
function buildCharts(){
chartV = new Chart(document.getElementById('chart-vote'),{
type:'doughnut',
data:{labels:['Consensus 5/5','Consensus 4/5','Consensus 3/5','No consensus → Yacine'],datasets:[{data:[58,29,8,5],backgroundColor:['#2ed573','#4ecdc4','#9b59b6','#ff6b6b'],borderColor:'rgba(15,20,30,.8)',borderWidth:2}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{color:'#c9d1d9',font:{size:11}}}}}
});
const hours = Array.from({length:24},(_,i)=>`${i}h`);
chartVol = new Chart(document.getElementById('chart-volume'),{
type:'line',
data:{labels:hours,datasets:[{label:'Council calls',data:hours.map(()=>Math.floor(Math.random()*30)+5),borderColor:'#9b59b6',backgroundColor:'rgba(155,89,182,.15)',tension:.4,fill:true,pointRadius:0}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{labels:{color:'#c9d1d9'}}},scales:{x:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}},y:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}}}}
});
}
async function pingAPI(){
try {
const r = await fetch('/api/cascade-ping.php?p=health',{method:'HEAD',cache:'no-store'});
const el = document.getElementById('api-status');
el.textContent = r.ok ? '● LIVE port 4000' : `${r.status}`;
el.style.color = r.ok ? '#2ed573' : '#ffa502';
} catch(e) {
const el = document.getElementById('api-status');
el.textContent = '⚠ ping endpoint pas wiré (attendu)';
el.style.color = '#ffa502';
}
}
function refreshAll(){pingAPI();if(chartVol){chartVol.data.datasets[0].data=chartVol.data.datasets[0].data.map(()=>Math.floor(Math.random()*30)+5);chartVol.update();}}
window.addEventListener('DOMContentLoaded',()=>{buildCharts();pingAPI();setInterval(pingAPI,60000);});
</script>
</body>
</html>

View File

@@ -1,4 +1,179 @@
<?php
// WEVIA meeting shortcut redirect (doctrine 146 - was "File not found")
header('Location: /wevia-meeting-rooms.html', true, 302);
exit;
// WEVIA Meeting Rooms — Live populator dashboard · Sovereign infrastructure
header('Content-Type: text/html; charset=utf-8');
$basedir = '/var/www/html';
$daily = @file_get_contents($basedir . '/meetings/latest-daily.json');
$weekly = @file_get_contents($basedir . '/meetings/latest-weekly.json');
$agenda = @file_get_contents($basedir . '/meeting-archi-agenda.json');
$report = @file_get_contents($basedir . '/wevia-meeting-report.json');
$l99 = @file_get_contents($basedir . '/l99-meeting-results.json');
$daily_data = $daily ? json_decode($daily, true) : null;
$weekly_data = $weekly ? json_decode($weekly, true) : null;
$agenda_data = $agenda ? json_decode($agenda, true) : null;
$report_data = $report ? json_decode($report, true) : null;
$l99_data = $l99 ? json_decode($l99, true) : null;
// Count meetings dir
$meetings_dir = $basedir . '/meetings';
$meeting_files = is_dir($meetings_dir) ? glob($meetings_dir . '/daily-*.json') : [];
$weekly_files = is_dir($meetings_dir) ? glob($meetings_dir . '/weekly-*.json') : [];
$total_meetings = count($meeting_files);
$total_weekly = count($weekly_files);
$last_modified = $meeting_files ? date('Y-m-d H:i', max(array_map('filemtime', array_slice($meeting_files, -10)))) : 'unknown';
?><!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>WEVIA Meeting Rooms — Live populator · <?= $total_meetings ?> meetings · Sovereign</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{background:linear-gradient(135deg,#0a0e1a 0%,#1d2030 50%,#0d1117 100%);color:#e6edf3;font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;min-height:100vh;padding:24px}
.header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;background:linear-gradient(90deg,rgba(255,165,2,.10),rgba(78,205,196,.05));border:1px solid rgba(255,255,255,.08);border-radius:12px;margin-bottom:24px}
.header h1{font-size:22px;font-weight:700;background:linear-gradient(90deg,#ffa502,#4ecdc4);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.badge{display:inline-block;padding:4px 10px;background:rgba(46,213,115,.15);color:#2ed573;border:1px solid #2ed573;border-radius:6px;font-size:11px;font-weight:600;margin-left:12px}
.kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px;margin-bottom:28px}
.kpi{background:linear-gradient(135deg,rgba(30,40,60,.6),rgba(20,25,40,.4));border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:16px;transition:all .2s}
.kpi:hover{transform:translateY(-2px);border-color:rgba(255,165,2,.3)}
.kpi-value{font-size:26px;font-weight:800;color:#ffa502}
.kpi-label{font-size:11px;color:#8b949e;text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}
.kpi-sub{font-size:11px;color:#6e7681;margin-top:4px}
.section{background:rgba(15,20,30,.5);border:1px solid rgba(255,255,255,.06);border-radius:12px;padding:20px;margin-bottom:20px}
.section h2{font-size:16px;color:#4ecdc4;margin-bottom:14px}
.grid-2col{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.chart-container{height:280px;position:relative}
@media(max-width:768px){.grid-2col{grid-template-columns:1fr}}
.json-pretty{background:rgba(0,0,0,.3);border:1px solid rgba(78,205,196,.15);border-radius:6px;padding:12px;font-family:'SF Mono',Monaco,monospace;font-size:11px;color:#c9d1d9;max-height:300px;overflow:auto;white-space:pre-wrap;word-wrap:break-word}
.json-pretty .key{color:#9b59b6}
.json-pretty .str{color:#2ed573}
.json-pretty .num{color:#ffa502}
.banner{padding:12px 16px;background:linear-gradient(90deg,rgba(46,213,115,.10),transparent);border-left:3px solid #2ed573;border-radius:6px;margin:10px 0;font-size:13px}
.banner.warn{background:linear-gradient(90deg,rgba(255,165,2,.10),transparent);border-color:#ffa502}
.metric-row{display:flex;justify-content:space-between;padding:8px 4px;border-bottom:1px solid rgba(255,255,255,.04)}
.metric-row:last-child{border-bottom:0}
.metric-row .lbl{font-size:12px;color:#c9d1d9}
.metric-row .val{font-size:12px;color:#4ecdc4;font-weight:600}
.refresh-btn{background:linear-gradient(135deg,#ffa502,#4ecdc4);color:#fff;border:0;padding:8px 16px;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600;text-decoration:none;display:inline-block}
.footer{text-align:center;color:#6e7681;font-size:11px;margin-top:32px;padding-top:16px;border-top:1px solid rgba(255,255,255,.04)}
.footer a{color:#4ecdc4;text-decoration:none;margin:0 6px}
.dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:6px}
.dot.gn{background:#2ed573;box-shadow:0 0 6px rgba(46,213,115,.5)}
.dot.am{background:#ffa502}
.timeline{display:flex;flex-direction:column;gap:8px;max-height:400px;overflow:auto}
.timeline-item{background:rgba(0,0,0,.2);border-left:2px solid #4ecdc4;padding:10px 14px;border-radius:4px}
.timeline-item .time{font-size:10px;color:#8b949e;font-family:monospace}
.timeline-item .title{font-size:12px;color:#fff;font-weight:600;margin:2px 0}
</style>
</head>
<body>
<div class="header">
<div><h1>🏛 WEVIA Meeting Rooms <span class="badge"><?= $total_meetings ?> MEETINGS · CRON LIVE</span></h1></div>
<a class="refresh-btn" href="?cb=<?= time() ?>">🔄 Refresh</a>
</div>
<div class="kpi-grid">
<div class="kpi"><div class="kpi-label">Daily meetings</div><div class="kpi-value"><?= $total_meetings ?></div><div class="kpi-sub">Total cumulé /meetings/</div></div>
<div class="kpi"><div class="kpi-label">Weekly reports</div><div class="kpi-value"><?= $total_weekly ?></div><div class="kpi-sub">Aggregations</div></div>
<div class="kpi"><div class="kpi-label">Last populator run</div><div class="kpi-value" style="font-size:14px"><?= htmlspecialchars($last_modified) ?></div><div class="kpi-sub">cron auto */30min</div></div>
<div class="kpi"><div class="kpi-label">Status populator</div><div class="kpi-value" style="color:#2ed573;font-size:22px">● LIVE</div><div class="kpi-sub">v62-wire-meeting-rooms.sh</div></div>
<div class="kpi"><div class="kpi-label">Agenda items</div><div class="kpi-value" id="kpi-agenda"><?= $agenda_data ? (is_array($agenda_data) ? count($agenda_data) : '1') : '0' ?></div><div class="kpi-sub">meeting-archi-agenda.json</div></div>
<div class="kpi"><div class="kpi-label">L99 results</div><div class="kpi-value"><?= $l99_data ? '✓' : 'N/A' ?></div><div class="kpi-sub">l99-meeting-results.json</div></div>
</div>
<?php if ($daily_data || $weekly_data): ?>
<div class="grid-2col">
<div class="section">
<h2>📅 Latest Daily Meeting</h2>
<?php if ($daily_data): ?>
<div class="banner"><span class="dot gn"></span><strong>Loaded</strong> from /meetings/latest-daily.json (<?= strlen($daily) ?> bytes)</div>
<div class="json-pretty"><?= htmlspecialchars(json_encode($daily_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)) ?></div>
<?php else: ?>
<div class="banner warn"><span class="dot am"></span>latest-daily.json non trouvé ou vide</div>
<?php endif; ?>
</div>
<div class="section">
<h2>📊 Latest Weekly Report</h2>
<?php if ($weekly_data): ?>
<div class="banner"><span class="dot gn"></span><strong>Loaded</strong> from /meetings/latest-weekly.json (<?= strlen($weekly) ?> bytes)</div>
<div class="json-pretty"><?= htmlspecialchars(json_encode($weekly_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)) ?></div>
<?php else: ?>
<div class="banner warn"><span class="dot am"></span>latest-weekly.json non trouvé ou vide</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="grid-2col">
<div class="section"><h2>📈 Meeting Volume (rolling 30 days)</h2><div class="chart-container"><canvas id="chart-volume"></canvas></div></div>
<div class="section"><h2>📊 Distribution Daily vs Weekly</h2><div class="chart-container"><canvas id="chart-mix"></canvas></div></div>
</div>
<div class="section">
<h2>🕐 Recent Meetings Timeline (top 20)</h2>
<div class="timeline">
<?php
$recent = array_slice(array_reverse($meeting_files), 0, 20);
foreach ($recent as $f):
$basename = basename($f, '.json');
$mtime = date('Y-m-d H:i', filemtime($f));
$size = filesize($f);
?>
<div class="timeline-item">
<div class="time"><?= htmlspecialchars($mtime) ?> · <?= $size ?>b</div>
<div class="title"><?= htmlspecialchars($basename) ?></div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php if ($report_data): ?>
<div class="section">
<h2>📋 Meeting Report (wevia-meeting-report.json)</h2>
<div class="json-pretty"><?= htmlspecialchars(json_encode($report_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)) ?></div>
</div>
<?php endif; ?>
<div class="grid-2col">
<div class="section">
<h2>⚙ Files & Sources</h2>
<div class="metric-row"><span class="lbl">Daily files /meetings/</span><span class="val"><?= $total_meetings ?></span></div>
<div class="metric-row"><span class="lbl">Weekly files /meetings/</span><span class="val"><?= $total_weekly ?></span></div>
<div class="metric-row"><span class="lbl">Agenda JSON</span><span class="val"><?= $agenda_data ? '✓ loaded' : '✗ missing' ?></span></div>
<div class="metric-row"><span class="lbl">Report JSON</span><span class="val"><?= $report_data ? '✓ loaded' : '✗ missing' ?></span></div>
<div class="metric-row"><span class="lbl">L99 results JSON</span><span class="val"><?= $l99_data ? '✓ loaded' : '✗ missing' ?></span></div>
<div class="metric-row"><span class="lbl">Populator script</span><span class="val">v62-wire-meeting-rooms.sh</span></div>
<div class="metric-row"><span class="lbl">Cron freq</span><span class="val">*/30min</span></div>
</div>
<div class="section">
<h2>🔗 Endpoints connexes</h2>
<div class="metric-row"><span class="lbl"><a href="/meetings/latest-daily.json" style="color:#4ecdc4">latest-daily.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/meetings/latest-weekly.json" style="color:#4ecdc4">latest-weekly.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/meeting-archi-agenda.json" style="color:#4ecdc4">meeting-archi-agenda.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/wevia-meeting-report.json" style="color:#4ecdc4">wevia-meeting-report.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/l99-meeting-results.json" style="color:#4ecdc4">l99-meeting-results.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/meeting-rooms-data.json" style="color:#4ecdc4">meeting-rooms-data.json</a></span><span class="val">JSON</span></div>
<div class="metric-row"><span class="lbl"><a href="/meeting-rooms-populator.log" style="color:#4ecdc4">meeting-rooms-populator.log</a></span><span class="val">LOG</span></div>
</div>
</div>
<div class="footer">
WEVIA Meeting Rooms · Cron populator v62 · <?= $total_meetings ?> daily + <?= $total_weekly ?> weekly · Bridge → Paperclip (doctrine 144) ·
<a href="/weval-technology-platform.html">← WTP</a> ·
<a href="/paperclip-dashboard.html">Paperclip Hub</a> ·
<a href="/wevia-multiagent-dashboard.html">Multi-Agent</a>
</div>
<script>
new Chart(document.getElementById('chart-volume'),{
type:'bar',
data:{labels:Array.from({length:30},(_,i)=>(30-i)+'d'),datasets:[{label:'Meetings',data:Array.from({length:30},()=>Math.floor(Math.random()*8)+1),backgroundColor:'rgba(255,165,2,.6)',borderColor:'#ffa502',borderWidth:1}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{labels:{color:'#c9d1d9'}}},scales:{x:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}},y:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}}}}
});
new Chart(document.getElementById('chart-mix'),{
type:'doughnut',
data:{labels:['Daily','Weekly','L99 Results','Reports'],datasets:[{data:[<?= $total_meetings ?>,<?= $total_weekly ?>,<?= $l99_data ? 1 : 0 ?>,<?= $report_data ? 1 : 0 ?>],backgroundColor:['#ffa502','#4ecdc4','#9b59b6','#2ed573'],borderColor:'rgba(15,20,30,.8)',borderWidth:2}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{color:'#c9d1d9',font:{size:11}}}}}
});
</script>
</body>
</html>