Files
weval-consulting/blade-ai.html
2026-04-09 10:30:03 +02:00

632 lines
52 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8"><meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WEVAL — Blade AI Controller</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=DM+Sans:wght@300;400;500;700&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#06090f;--s1:#0d1321;--s2:#141d2f;--s3:#1a2744;--brd:#1e3054;--t1:#e8ecf4;--t2:#8899b4;--t3:#506380;--green:#00e09e;--red:#ff4d6a;--amber:#ffb547;--blue:#4da6ff;--purple:#a78bfa;--cyan:#22d3ee;--pink:#f472b6;--font:'DM Sans',sans-serif;--mono:'JetBrains Mono',monospace}
body{background:var(--bg);color:var(--t1);font-family:var(--font);overflow:hidden;height:100vh}
.app{display:grid;grid-template-columns:260px 1fr 320px;grid-template-rows:48px 1fr;height:100vh}
.topbar{grid-column:1/-1;background:var(--s1);border-bottom:1px solid var(--brd);display:flex;align-items:center;padding:0 16px;gap:12px}
.topbar h1{font-size:15px;font-weight:700;letter-spacing:.5px}
.topbar .dot{width:8px;height:8px;border-radius:50%;background:var(--red)}
.topbar .dot.on{background:var(--green);box-shadow:0 0 12px var(--green)}
.topbar .status{font-size:11px;color:var(--t2);font-family:var(--mono)}
.topbar .tabs{margin-left:auto;display:flex;gap:2px}
.topbar .tab{padding:6px 14px;font-size:12px;background:transparent;border:none;color:var(--t2);cursor:pointer;border-radius:6px;font-family:var(--font)}
.topbar .tab.active{background:var(--s3);color:var(--t1)}
.topbar .tab:hover{color:var(--t1)}
/* SIDEBAR */
.sidebar{background:var(--s1);border-right:1px solid var(--brd);overflow-y:auto;padding:12px}
.sb-section{margin-bottom:16px}
.sb-title{font-size:10px;font-weight:700;color:var(--t3);text-transform:uppercase;letter-spacing:1.5px;padding:4px 8px}
.sb-btn{width:100%;padding:8px 10px;background:transparent;border:1px solid transparent;color:var(--t2);border-radius:6px;cursor:pointer;font-size:12px;font-family:var(--font);text-align:left;display:flex;align-items:center;gap:8px;transition:all .12s}
.sb-btn:hover{background:var(--s2);color:var(--t1);border-color:var(--brd)}
.sb-btn .ico{font-size:14px;width:20px;text-align:center}
.sb-btn .badge{margin-left:auto;font-size:9px;background:var(--s3);padding:1px 6px;border-radius:99px;color:var(--t3)}
/* MAIN */
.main{overflow-y:hidden;padding:16px;display:flex;flex-direction:column;max-height:calc(100vh - 48px)}
.panel{display:none;flex:1;flex-direction:column}
.panel.active{display:flex}
/* AI CHAT */
.chat-messages{flex:1;overflow-y:auto;scroll-behavior:smooth;padding:8px 0;min-height:0}
.chat-msg{margin-bottom:12px;display:flex;gap:10px}
.chat-msg .avatar{width:28px;height:28px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex-shrink:0}
.chat-msg .avatar.ai{background:linear-gradient(135deg,#8b5cf6,#6366f1)}
.chat-msg .avatar.user{background:var(--s3);color:var(--blue)}
.chat-msg .bubble{background:var(--s2);border:1px solid var(--brd);border-radius:8px;padding:10px 12px;font-size:13px;line-height:1.6;max-width:90%}
.chat-msg .bubble code{font-family:var(--mono);font-size:11px;background:var(--s1);padding:1px 4px;border-radius:3px}
.chat-msg .bubble .task-result{background:var(--s1);border:1px solid var(--brd);border-radius:6px;padding:8px;margin-top:8px;font-family:var(--mono);font-size:11px;color:var(--cyan);max-height:120px;overflow-y:auto;white-space:pre-wrap}
.chat-input{display:flex;gap:8px;padding:12px 0 4px;border-top:1px solid var(--brd);flex-shrink:0;position:sticky;bottom:0;background:var(--bg)}
.chat-input textarea{flex:1;background:var(--s2);border:1px solid var(--brd);color:var(--t1);padding:10px 14px;border-radius:8px;font-size:13px;font-family:var(--font);outline:none;resize:vertical;min-height:42px;max-height:150px;line-height:1.4}
.chat-input textarea:focus{border-color:var(--blue)}
.chat-input button{background:var(--blue);color:#fff;border:none;padding:10px 20px;border-radius:8px;cursor:pointer;font-weight:600;font-size:13px}
/* GRID CARDS */
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px;margin-bottom:16px}
.card{background:var(--s2);border:1px solid var(--brd);border-radius:10px;padding:14px;cursor:pointer;transition:all .15s}
.card:hover{border-color:var(--blue);transform:translateY(-1px)}
.card .card-ico{font-size:20px;margin-bottom:6px}
.card .card-title{font-size:13px;font-weight:600;margin-bottom:2px}
.card .card-desc{font-size:11px;color:var(--t2);line-height:1.4}
/* RIGHT PANEL */
.rpanel{background:var(--s1);border-left:1px solid var(--brd);overflow-y:auto;padding:12px}
.rp-section{margin-bottom:16px}
.rp-title{font-size:10px;font-weight:700;color:var(--t3);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:8px}
.rp-metric{display:flex;justify-content:space-between;padding:5px 0;font-size:12px}
.rp-metric .v{font-family:var(--mono);font-weight:600}
.rp-bar{height:4px;background:var(--s3);border-radius:2px;margin:4px 0 8px;overflow:hidden}
.rp-bar .fill{height:100%;border-radius:2px;transition:width .5s}
.task-mini{background:var(--s2);border:1px solid var(--brd);border-radius:6px;padding:8px;margin-bottom:6px;font-size:11px}
.task-mini .tm-status{display:inline-block;width:6px;height:6px;border-radius:50%;margin-right:4px}
.task-mini .tm-status.pending{background:var(--amber)}
.task-mini .tm-status.done{background:var(--green)}
.task-mini .tm-status.failed{background:var(--red)}
/* RECIPES */
.recipe{background:var(--s2);border:1px solid var(--brd);border-radius:8px;padding:12px;margin-bottom:8px;display:flex;align-items:center;gap:12px;cursor:pointer;transition:all .15s}
.recipe:hover{border-color:var(--purple)}
.recipe .r-ico{font-size:22px}
.recipe .r-info{flex:1}
.recipe .r-title{font-size:13px;font-weight:600}
.recipe .r-desc{font-size:11px;color:var(--t2)}
.recipe .r-go{background:var(--s3);border:none;color:var(--t1);padding:6px 12px;border-radius:6px;cursor:pointer;font-size:11px;font-weight:600}
.recipe .r-go:hover{background:var(--purple);color:#fff}
.loading{color:var(--t3);font-size:12px;padding:20px;text-align:center}
@media(max-width:1024px){.app{grid-template-columns:1fr}.sidebar,.rpanel{display:none}}
/* UX FIX: scroll + input */
.main{display:flex;flex-direction:column;overflow:hidden}
.panel.active{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column}
#chat-messages{flex:1!important;overflow-y:auto!important;padding:8px!important;scroll-behavior:smooth!important;min-height:0!important}
.chat-input-wrap{flex-shrink:0!important;padding:12px 16px!important;border-top:1px solid var(--brd)!important;background:var(--s1)!important;display:flex!important;gap:8px!important;align-items:center!important}
#chat-in{width:100%!important;min-height:44px!important;font-size:14px!important;padding:12px 16px!important;background:var(--s2)!important;border:1px solid var(--brd)!important;border-radius:10px!important;color:var(--t1)!important;outline:none!important;font-family:var(--font)!important}
#chat-in:focus{border-color:var(--blue)!important}
/* Chat: input sticky bottom */
#panel-ai{display:flex;flex-direction:column;height:100%}
#panel-ai .chat-messages{flex:1;min-height:0;overflow-y:auto}
#panel-ai .chat-input{margin-top:auto;flex-shrink:0;position:sticky;bottom:0;background:var(--bg);padding:12px 0;border-top:1px solid var(--brd)}
/* === UX IMPROVEMENTS v2 === */
/* Pulse online dot */
@keyframes pulse{0%,100%{box-shadow:0 0 4px var(--green)}50%{box-shadow:0 0 16px var(--green),0 0 32px rgba(0,224,158,.2)}}
.topbar .dot.on{animation:pulse 2s ease infinite}
#sentinel-dot{transition:background .3s}
/* Smooth scrollbars */
::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--s3);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--brd)}
/* Card hover glow */
.card:hover{box-shadow:0 4px 20px rgba(77,166,255,.1);border-color:var(--blue)}
.card:active{transform:scale(.98)}
/* Recipe hover */
.recipe:hover{box-shadow:0 2px 16px rgba(167,139,250,.08)}
.recipe:active{transform:scale(.99)}
/* Button press feedback */
.sb-btn:active{transform:scale(.97);opacity:.8}
.r-go:active{transform:scale(.92)}
/* Health bars: dynamic colors */
.rp-bar .fill.warn{background:var(--amber)!important}
.rp-bar .fill.crit{background:var(--red)!important}
.rp-bar .fill.ok{background:var(--green)!important}
/* Chat message entrance */
@keyframes slideIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.chat-msg{animation:slideIn .25s ease}
/* Send button glow on focus */
.chat-input button{transition:all .15s}
.chat-input button:hover{box-shadow:0 2px 12px rgba(77,166,255,.3);transform:translateY(-1px)}
.chat-input button:active{transform:scale(.95)}
/* Task mini animation */
.task-mini{transition:all .2s}
.task-mini:hover{border-color:var(--blue);background:var(--s3)}
/* Sentinel bar polish */
#sentinel-bar{transition:background .3s}
/* Keyboard shortcut hint */
.chat-input textarea::placeholder{color:var(--t3)}
/* Right panel metric hover */
.rp-metric{transition:background .1s;padding:5px 4px;border-radius:4px}
.rp-metric:hover{background:var(--s2)}
/* Links hover */
.rpanel a{transition:color .15s}
.rpanel a:hover{color:var(--green)!important}
/* Download agent link update */
</style>
</head>
<body>
<div id="sentinel-bar" style="background:#1a0a2e;border-bottom:1px solid #2d1b4e;padding:8px 20px;display:flex;align-items:center;gap:12px;font-size:12px">
<span id="sentinel-dot" style="width:8px;height:8px;border-radius:50%;background:#ef4444;flex-shrink:0"></span>
<span id="sentinel-status" style="color:#94a3b8;font-family:'JetBrains Mono',monospace;font-size:11px">Sentinel: checking...</span>
<a id="sentinel-install" href="https://weval-consulting.com/downloads/install-sentinel.bat" style="display:none;margin-left:auto;background:#7c3aed;color:#fff;padding:4px 14px;border-radius:6px;text-decoration:none;font-weight:600;font-size:11px;white-space:nowrap" download>Installer Sentinel</a>
<button id="sentinel-fix" onclick="window.open('https://weval-consulting.com/downloads/install-sentinel.bat','_blank')" style="display:none;margin-left:4px;background:#f59e0b;color:#000;border:none;padding:4px 12px;border-radius:6px;cursor:pointer;font-weight:600;font-size:11px">Reconnecter</button>
</div>
<script>
async function checkSentinel(){try{const r=await fetch('/api/blade-poll.php?k=BLADE2026&action=status');const d=await r.json();const on=d.heartbeat&&d.heartbeat.ts;document.getElementById('sentinel-dot').style.background=on?'#22c55e':'#ef4444';document.getElementById('sentinel-status').textContent=on?'Sentinel ONLINE v'+(d.heartbeat&&d.heartbeat.agent_version)||'2.0':'Sentinel OFFLINE';document.getElementById('sentinel-install').style.display=on?'none':'inline-block';document.getElementById('sentinel-fix').style.display=on?'none':'inline-block'}catch(e){document.getElementById('sentinel-status').textContent='Sentinel: error'}}
checkSentinel();setInterval(checkSentinel,15000);
</script>
<div class="app">
<!-- TOPBAR -->
<div class="topbar">
<span class="dot" id="dot"></span>
<h1>BLADE AI</h1>
<span class="status" id="top-status">connecting...</span>
<div class="tabs">
<button class="tab active" onclick="showPanel('ai')">AI Chat</button>
<button class="tab" onclick="showPanel('actions')">Actions</button>
<button class="tab" onclick="showPanel('recipes')">Automations</button>
<button class="tab" onclick="showPanel('tasks')">Tasks</button>
<button class="tab" onclick="showPanel('files')">Files</button>
</div>
</div>
<!-- SIDEBAR: Quick Actions -->
<div class="sidebar">
<div class="sb-section">
<div class="sb-title">System</div>
<button class="sb-btn" onclick="pushQ('sysinfo','System Info')"><span class="ico">💻</span>System Info</button>
<button class="sb-btn" onclick="pushQ('screenshot','Screenshot')"><span class="ico">📸</span>Screenshot</button>
<button class="sb-btn" onclick="pushQ('powershell','Top Procs','Get-Process|Sort CPU -Desc|Select -First 15 Name,CPU,WS|Format-Table')"><span class="ico">⚙️</span>Processes</button>
<button class="sb-btn" onclick="pushQ('powershell','Disk Space','Get-PSDrive C,D,E -EA 0|Select Name,@{N=\"Free(GB)\";E={[math]::Round($_.Free/1GB)}},@{N=\"Used(GB)\";E={[math]::Round($_.Used/1GB)}}|FT')"><span class="ico">💾</span>Disk Space</button>
<button class="sb-btn" onclick="pushQ('powershell','Network','Get-NetIPAddress -AddressFamily IPv4|Select IPAddress,InterfaceAlias|FT')"><span class="ico">🌐</span>Network IPs</button>
<button class="sb-btn" onclick="pushQ('powershell','WiFi','netsh wlan show interfaces|Select-String SSID,Signal,Speed')"><span class="ico">📶</span>WiFi Status</button>
<button class="sb-btn" onclick="pushQ('powershell','Battery','(Get-CimInstance Win32_Battery|Select EstimatedChargeRemaining,BatteryStatus)|FT')"><span class="ico">🔋</span>Battery</button>
<button class="sb-btn" onclick="pushQ('powershell','GPU Info','(Get-CimInstance Win32_VideoController|Select Name,AdapterRAM,DriverVersion)|FT')"><span class="ico">🎮</span>GPU Info</button>
</div>
<div class="sb-section">
<div class="sb-title">Apps & URLs</div>
<button class="sb-btn" onclick="pushQ('open_url','WEVAL Site','https://weval-consulting.com')"><span class="ico">🏠</span>WEVAL Site</button>
<button class="sb-btn" onclick="pushQ('open_url','Arsenal','https://weval-consulting.com/arsenal-proxy/ceo-dashboard.html')"><span class="ico">🎯</span>Arsenal CEO</button>
<button class="sb-btn" onclick="pushQ('open_url','WEVIA','https://weval-consulting.com/wevia')"><span class="ico">🤖</span>WEVIA Chat</button>
<button class="sb-btn" onclick="pushQ('open_url','Analytics','https://analytics.weval-consulting.com')"><span class="ico">📊</span>Plausible</button>
<button class="sb-btn" onclick="pushQ('open_url','CRM','https://crm.weval-consulting.com')"><span class="ico">👥</span>Twenty CRM</button>
<button class="sb-btn" onclick="pushQ('open_url','n8n','https://n8n.weval-consulting.com')"><span class="ico">🔗</span>n8n Workflows</button>
<button class="sb-btn" onclick="pushQ('open_url','Monitoring','https://weval-consulting.com/monitoring.html')"><span class="ico">📡</span>Uptime Kuma</button>
<button class="sb-btn" onclick="pushQ('open_url','WEVADS IA','https://weval-consulting.com/wevads-ia/')"><span class="ico">🧠</span>WEVADS IA</button>
<button class="sb-btn" onclick="pushQ('open_url','DeerFlow','https://deerflow.weval-consulting.com')"><span class="ico">🦌</span>DeerFlow</button>
</div>
<div class="sb-section">
<div class="sb-title">Git & Dev</div>
<button class="sb-btn" onclick="pushQ('git_pull','Git Pull')"><span class="ico">⬇️</span>Git Pull</button>
<button class="sb-btn" onclick="pushQ('git_push','Git Push')"><span class="ico">⬆️</span>Git Push</button>
<button class="sb-btn" onclick="pushQ('powershell','VS Code','code C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting')"><span class="ico">📝</span>Open VS Code</button>
<button class="sb-btn" onclick="pushQ('list_dir','CLAUDE Dir','C:\\Users\\Yace\\Desktop\\CLAUDE')"><span class="ico">📁</span>CLAUDE Folder</button>
</div>
<div class="sb-section">
<div class="sb-title">Clipboard</div>
<button class="sb-btn" onclick="pushQ('clipboard_get','Get Clipboard')"><span class="ico">📋</span>Read Clipboard</button>
<button class="sb-btn" onclick="clipSet()"><span class="ico">✏️</span>Set Clipboard</button>
</div>
<div class="sb-section">
<div class="sb-title">Notifications</div>
<button class="sb-btn" onclick="pushQ('notify','Reminder','N oublie pas de faire la revue quotidienne')"><span class="ico">🔔</span>Daily Reminder</button>
<button class="sb-btn" onclick="customNotify()"><span class="ico">💬</span>Custom Notify</button>
</div>
</div>
<!-- MAIN PANELS -->
<div class="main">
<!-- AI CHAT PANEL -->
<div class="panel active" id="panel-ai">
<div class="chat-messages" id="chat-messages">
<div class="chat-msg"><div class="avatar ai">AI</div><div class="bubble">Blade AI Controller actif. Je suis l'IA de WEVAL — dis-moi ce que tu veux faire sur le Blade Razer en langage naturel.<br><br>Exemples :<br><code>Prends un screenshot</code><br><code>Ouvre le site WEVAL et le CRM</code><br><code>Montre-moi les gros processus</code><br><code>Installe Node.js</code><br><code>Nettoie les fichiers temp</code><br><code>Quel est l'espace disque?</code></div></div>
</div>
<div class="chat-input">
<textarea id="chat-in" placeholder="Dis ce que tu veux faire sur le Blade..." rows="2" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendChat()}"></textarea>
<button onclick="sendChat()">Envoyer</button>
</div>
</div>
<!-- QUICK ACTIONS PANEL -->
<div class="panel" id="panel-actions">
<h2 style="font-size:16px;margin-bottom:12px">Actions rapides</h2>
<div class="cards">
<div class="card" onclick="pushQ('powershell','Cleanup Temp','Remove-Item $env:TEMP\\* -Recurse -Force -EA 0;Write-Host \"Temp cleaned: $((Get-ChildItem $env:TEMP -Recurse -EA 0|Measure).Count) items remaining\"')"><div class="card-ico">🧹</div><div class="card-title">Nettoyer Temp</div><div class="card-desc">Supprime les fichiers temporaires Windows</div></div>
<div class="card" onclick="pushQ('powershell','Installed Apps','Get-ItemProperty HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*|Select DisplayName,DisplayVersion|Sort DisplayName|FT -Auto')"><div class="card-ico">📦</div><div class="card-title">Apps installées</div><div class="card-desc">Liste tous les logiciels</div></div>
<div class="card" onclick="pushQ('powershell','Services','Get-Service|Where Status -eq Running|Select Name,DisplayName|Sort DisplayName|FT -Auto')"><div class="card-ico">🔧</div><div class="card-title">Services actifs</div><div class="card-desc">Services Windows en cours</div></div>
<div class="card" onclick="pushQ('powershell','Startup Items','Get-CimInstance Win32_StartupCommand|Select Name,Command,Location|FT -Auto')"><div class="card-ico">🚀</div><div class="card-title">Programs Startup</div><div class="card-desc">Apps au démarrage</div></div>
<div class="card" onclick="pushQ('powershell','Ports ouverts','Get-NetTCPConnection -State Listen|Select LocalPort,OwningProcess|Sort LocalPort|FT')"><div class="card-ico">🔌</div><div class="card-title">Ports ouverts</div><div class="card-desc">TCP Listen</div></div>
<div class="card" onclick="pushQ('powershell','Firewall Rules','Get-NetFirewallRule -Enabled True -Direction Inbound|Select DisplayName,Action|FT -Auto')"><div class="card-ico">🛡️</div><div class="card-title">Firewall</div><div class="card-desc">Règles entrantes actives</div></div>
<div class="card" onclick="pushQ('powershell','Recent Files','Get-ChildItem $env:USERPROFILE\\Downloads,$env:USERPROFILE\\Desktop -File|Sort LastWriteTime -Desc|Select -First 20 Name,Length,LastWriteTime|FT')"><div class="card-ico">📄</div><div class="card-title">Fichiers récents</div><div class="card-desc">Downloads + Desktop</div></div>
<div class="card" onclick="pushQ('powershell','Updates','(New-Object -ComObject Microsoft.Update.AutoUpdate).DetectNow();Write-Host \"Windows Update check triggered\"')"><div class="card-ico">🔄</div><div class="card-title">Check Updates</div><div class="card-desc">Déclencher Windows Update</div></div>
<div class="card" onclick="pushQ('powershell','IP Public','(Invoke-WebRequest -Uri https://api.ipify.org -UseBasicParsing).Content')"><div class="card-ico">🌍</div><div class="card-title">IP Publique</div><div class="card-desc">Adresse IP externe</div></div>
<div class="card" onclick="pushQ('powershell','Speedtest','$r=Invoke-WebRequest -Uri https://speed.cloudflare.com/__down?bytes=10000000 -UseBasicParsing;Write-Host \"10MB in $([math]::Round($r.Headers[\"X-Time-Taken\"]))ms\"')"><div class="card-ico"></div><div class="card-title">Speed Test</div><div class="card-desc">Test débit rapide</div></div>
<div class="card" onclick="pushQ('powershell','Hosts File','Get-Content C:\\Windows\\System32\\drivers\\etc\\hosts|Where{$_ -notmatch \"^#\" -and $_.Trim()}')"><div class="card-ico">📋</div><div class="card-title">Hosts File</div><div class="card-desc">Entrées DNS locales</div></div>
<div class="card" onclick="pushQ('powershell','Env Vars','Get-ChildItem Env:|Sort Name|FT -Auto')"><div class="card-ico">🔤</div><div class="card-title">Variables Env</div><div class="card-desc">Toutes les variables d'environnement</div></div>
</div>
</div>
<!-- AUTOMATIONS/RECIPES PANEL -->
<div class="panel" id="panel-recipes">
<h2 style="font-size:16px;margin-bottom:12px">Automations Blade</h2>
<div class="recipe" onclick="runRecipe('morning')"><div class="r-ico">🌅</div><div class="r-info"><div class="r-title">Routine matinale</div><div class="r-desc">Ouvre WEVAL, Arsenal CEO, CRM, Analytics + Git Pull + Screenshot</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('devsetup')"><div class="r-ico">💻</div><div class="r-info"><div class="r-title">Setup Dev</div><div class="r-desc">Git Pull + Ouvre VS Code + Terminal + WEVADS IA</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('cleanup')"><div class="r-ico">🧹</div><div class="r-info"><div class="r-title">Nettoyage complet</div><div class="r-desc">Temp + Downloads vieux + Corbeille + Cache navigateur</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('backup')"><div class="r-ico">💾</div><div class="r-info"><div class="r-title">Backup local</div><div class="r-desc">Git Push + Archive CLAUDE folder + Sync WEVIA LIFE</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('security')"><div class="r-ico">🔒</div><div class="r-info"><div class="r-title">Audit sécurité</div><div class="r-desc">Firewall + Ports ouverts + Services + Startup + Updates</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('endday')"><div class="r-ico">🌙</div><div class="r-info"><div class="r-title">Fin de journée</div><div class="r-desc">Git Push + Screenshot + Top Processes + Battery + Notification</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('monitoring')"><div class="r-ico">📊</div><div class="r-info"><div class="r-title">Monitoring complet</div><div class="r-desc">SysInfo + RAM Top 15 + Réseau + Connections actives + Screenshot</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('gitfull')"><div class="r-ico">🔄</div><div class="r-info"><div class="r-title">Git Sync complet</div><div class="r-desc">Pull + Status + Log 5 derniers commits + Notification</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('alltools')"><div class="r-ico">🚀</div><div class="r-info"><div class="r-title">Tous les outils WEVAL</div><div class="r-desc">Site + Arsenal + CRM + WEVADS + Analytics + LIFE + Claude + GitHub</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('performance')"><div class="r-ico"></div><div class="r-info"><div class="r-title">Performance Live</div><div class="r-desc">CPU/RAM/Disk en temps réel + Batterie + Espace disque + Uptime</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('quickdeploy')"><div class="r-ico">🚢</div><div class="r-info"><div class="r-title">Quick Deploy</div><div class="r-desc">Git Pull + Commit all + Push + Screenshot confirmation</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('monitoring')"><div class="r-ico">📊</div><div class="r-info"><div class="r-title">Health Check</div><div class="r-desc">CPU + RAM + Disk + GPU + Battery + Network + Processes</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('present')"><div class="r-ico">🎤</div><div class="r-info"><div class="r-title">Mode Présentation</div><div class="r-desc">Ouvre WEVAL site + Ferme apps inutiles + Plein écran Chrome</div></div><div><button class="r-go">GO</button></div></div>
<div class="recipe" onclick="runRecipe('endday')"><div class="r-ico">🌙</div><div class="r-info"><div class="r-title">Fin de journée</div><div class="r-desc">Git Push + Screenshot final + Cleanup + Lock screen</div></div><div><button class="r-go">GO</button></div></div>
</div>
<!-- TASKS LIST PANEL -->
<div class="panel" id="panel-tasks">
<h2 style="font-size:16px;margin-bottom:12px">Historique des tâches <button onclick="loadTasks()" style="background:var(--s3);border:none;color:var(--t2);padding:4px 10px;border-radius:4px;cursor:pointer;font-size:11px;margin-left:8px">Reload</button> <button onclick="clearTasks()" style="background:var(--s3);border:none;color:var(--t2);padding:4px 10px;border-radius:4px;cursor:pointer;font-size:11px">Clear done</button></h2>
<div id="tasks-full" class="loading">Chargement...</div>
</div>
<!-- FILES PANEL -->
<div class="panel" id="panel-files">
<h2 style="font-size:16px;margin-bottom:12px">File Manager</h2>
<div style="display:flex;gap:8px;margin-bottom:12px">
<input type="text" id="file-path" value="C:\Users\Yace\Desktop" style="flex:1;background:var(--s2);border:1px solid var(--brd);color:var(--t1);padding:8px;border-radius:6px;font-family:var(--mono);font-size:12px">
<button onclick="browseDir()" style="background:var(--blue);color:#fff;border:none;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600">Browse</button>
</div>
<div id="file-list" class="loading">Entrez un chemin et cliquez Browse</div>
<div style="margin-top:12px;display:flex;gap:8px">
<input type="text" id="file-write-path" placeholder="Chemin du fichier..." style="flex:1;background:var(--s2);border:1px solid var(--brd);color:var(--t1);padding:8px;border-radius:6px;font-family:var(--mono);font-size:12px">
<button onclick="writeFile()" style="background:var(--purple);color:#fff;border:none;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600">Write</button>
</div>
<textarea id="file-content" placeholder="Contenu du fichier..." style="width:100%;height:120px;margin-top:8px;background:var(--s2);border:1px solid var(--brd);color:var(--t1);padding:8px;border-radius:6px;font-family:var(--mono);font-size:12px;resize:vertical"></textarea>
</div>
</div>
<!-- RIGHT PANEL: Monitoring -->
<div class="rpanel">
<div class="rp-section">
<div class="rp-title">Blade Health</div>
<div class="rp-metric"><span>CPU</span><span class="v" id="r-cpu"></span></div>
<div class="rp-bar"><div class="fill" id="bar-cpu" style="width:0;background:var(--blue)"></div></div>
<div class="rp-metric"><span>RAM</span><span class="v" id="r-ram"></span></div>
<div class="rp-bar"><div class="fill" id="bar-ram" style="width:0;background:var(--purple)"></div></div>
<div class="rp-metric"><span>Disk</span><span class="v" id="r-disk"></span></div>
<div class="rp-bar"><div class="fill" id="bar-disk" style="width:0;background:var(--cyan)"></div></div>
<div class="rp-metric"><span>Uptime</span><span class="v" id="r-up"></span></div>
<div class="rp-metric"><span>User</span><span class="v" id="r-user"></span></div>
<div class="rp-metric"><span>Last seen</span><span class="v" id="r-ts"></span></div>
</div>
<div class="rp-section">
<div class="rp-title">Queue <span id="r-count" style="color:var(--amber)"></span></div>
<div id="r-tasks"></div>
</div>
<div class="rp-section">
<div class="rp-title">Liens rapides</div>
<div style="font-size:11px;line-height:2">
<a href="/blade-center.html" style="color:var(--cyan);text-decoration:none">Blade Center (simple)</a><br>
<a href="/ops-center.html" style="color:var(--cyan);text-decoration:none">WEVAL Manager</a><br>
<a href="/api/blade-api.php?action=status" style="color:var(--cyan);text-decoration:none">Blade API JSON</a><br>
<a href="/products/weval-sentinel-agent.ps1" style="color:var(--cyan);text-decoration:none">Download Agent v2.2</a><br>
<a href="/api/nonreg-report.html" style="color:var(--cyan);text-decoration:none">NonReg Report</a>
</div>
</div>
</div>
</div>
<script>
const API='/api/blade-api.php', KEY='BLADE2026';
async function api(p){p.k=KEY;const b=Object.entries(p).map(([k,v])=>`${k}=${encodeURIComponent(v)}`).join('&');const r=await fetch(API,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:b});return r.json()}
function showPanel(id){document.querySelectorAll('.panel').forEach(p=>p.classList.remove('active'));document.getElementById('panel-'+id).classList.add('active');document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active'));event.target.classList.add('active');if(id==='tasks')loadTasks()}
async function pushQ(type,label,cmd){
const r=await api({action:'push',type,cmd:cmd||type,label,source:'blade-ai',priority:'7'});
addChat('user',label);
addChat('ai',`Tâche <code>${type}</code> envoyée au Blade. ID: <code>${r.task?.id||'?'}</code>`);
loadStatus();
if(r.task)pollResult(r.task.id);
}
async function pollResult(id,attempts=0){
if(attempts>40)return;
await new Promise(r=>setTimeout(r,3000));
const d=await api({action:'list'});
const t=d.tasks?.find(t=>t.id===id);
if(t&&t.status==='done'){
addChat('ai',`Résultat de <code>${t.label}</code>:<div class="task-result">${esc(t.result||'OK')}</div>`);
loadStatus();
}else if(t&&t.status==='failed'){
addChat('ai',`Erreur sur <code>${t.label}</code>:<div class="task-result" style="color:var(--red)">${esc(t.error||t.result||'Error')}</div>`);
}else{pollResult(id,attempts+1)}
}
function addChat(who,html){
const el=document.getElementById('chat-messages');
el.innerHTML+=`<div class="chat-msg"><div class="avatar ${who==='ai'?'ai':'user'}">${who==='ai'?'AI':'Y'}</div><div class="bubble">${html}</div></div>`;
el.scrollTop=el.scrollHeight;
}
async function sendChat(){
const input=document.getElementById('chat-in');
const msg=input.value.trim();if(!msg)return;
input.value='';
addChat('user',esc(msg));
// AI: parse natural language → Blade task
let parsed=null,provider='local';
try{const br=await fetch('/api/blade-brain.php?msg='+encodeURIComponent(msg));const bd=await br.json();if(bd.ok&&bd.tasks&&bd.tasks.length>0){parsed=bd.tasks;provider=bd.provider||'ai';}}catch(e){}
if(!parsed){const kp=parseIntent(msg);if(kp){parsed=[kp];provider='keyword';}}
if(parsed&&parsed.length>0){
const pBadge=provider==='keyword'?'🔤 keyword':'🧠 '+provider;
const hasReal=parsed.some(t=>t.type&&!['none','greeting','notify'].includes(t.type));
if(hasReal){
addChat('ai',`<div style="margin-bottom:6px;font-size:12px;color:var(--cyan)"><b>${pBadge}</b></div>${parsed.filter(t=>t.type!=='none').map(t=>'<div style="margin:4px 0;padding:8px 12px;background:var(--s2);border-radius:8px;border-left:3px solid var(--green);font-size:12px"><b style=\"color:var(--green)\">['+t.type+']</b> <span style=\"color:var(--t1)\">'+(t.label||t.cmd||'')+'</span></div>').join('')}`);
}else{
addChat('ai',`<span style="color:var(--cyan)">${pBadge}</span> — Salut ! Je suis Blade AI, l'assistant WEVAL. Dis-moi ce que tu veux faire :<br><br><div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:8px"><span style="background:var(--s2);padding:4px 10px;border-radius:6px;font-size:11px;cursor:pointer" onclick="document.getElementById('chat-in').value='screenshot';sendChat()">📸 Screenshot</span><span style="background:var(--s2);padding:4px 10px;border-radius:6px;font-size:11px;cursor:pointer" onclick="document.getElementById('chat-in').value='ouvre weval et le CRM';sendChat()">🌐 Ouvrir sites</span><span style="background:var(--s2);padding:4px 10px;border-radius:6px;font-size:11px;cursor:pointer" onclick="document.getElementById('chat-in').value='espace disque';sendChat()">💾 Espace disque</span><span style="background:var(--s2);padding:4px 10px;border-radius:6px;font-size:11px;cursor:pointer" onclick="document.getElementById('chat-in').value='processus lourds';sendChat()">⚙️ Processus</span></div>`);
}
for(const t of parsed){
if(['none','greeting','notify'].includes(t.type))continue;
const r=await api({action:'push',type:t.type,cmd:t.cmd||t.type,label:t.label||t.type,source:'ai-'+provider,priority:'8'});
if(r.task)pollResult(r.task.id);
}
}else{
addChat('ai','Pas compris. Essaie: <code>screenshot</code>, <code>ouvre weval</code>, <code>espace disque</code>, <code>processus</code>, <code>ps: Get-Something</code>');
}
loadStatus();
}
function parseIntent(msg){
const m=msg.toLowerCase();
if(m.match(/screenshot|capture|ecran|écran/))return{type:'screenshot',cmd:'screenshot',label:'Screenshot'};
if(m.match(/^(hi|hello|salut|bonjour|hey|yo|coucou|bonsoir|ca va|slt)$/i))return{type:'greeting',cmd:'',label:'Greeting'};if(m.match(/sysinfo|system|système|config machine/))return{type:'sysinfo',cmd:'sysinfo',label:'System Info'};
if(m.match(/process|processus|cpu|top/))return{type:'powershell',cmd:'Get-Process|Sort CPU -Desc|Select -First 15 Name,CPU,WS|FT',label:'Top Processes'};
if(m.match(/disque|disk|espace|space|stockage/))return{type:'powershell',cmd:'Get-PSDrive C,D,E -EA 0|Select Name,@{N="Free(GB)";E={[math]::Round($_.Free/1GB)}},@{N="Used(GB)";E={[math]::Round($_.Used/1GB)}}|FT',label:'Disk Space'};
if(m.match(/nettoie|clean|temp|temporaire/))return{type:'powershell',cmd:'Remove-Item $env:TEMP\\* -Recurse -Force -EA 0;"Temp cleaned"',label:'Cleanup Temp'};
if(m.match(/^ouvre |^open |^lance |^start /i)){const url=msg.replace(/^(ouvre|open|lance|start)\s+/i,'').trim();if(url.match(/^https?:\/\//))return{type:'open_url',cmd:url,label:'Open '+url};
const map={weval:'https://weval-consulting.com',arsenal:'https://weval-consulting.com/arsenal-proxy/ceo-dashboard.html',crm:'https://crm.weval-consulting.com',wevia:'https://weval-consulting.com/wevia',analytics:'https://analytics.weval-consulting.com',n8n:'https://n8n.weval-consulting.com',deerflow:'https://deerflow.weval-consulting.com',wevads:'https://weval-consulting.com/wevads-ia/',mattermost:'https://mm.weval-consulting.com'};
for(const[k,v]of Object.entries(map)){if(url.toLowerCase().includes(k))return{type:'open_url',cmd:v,label:'Open '+k}}
return{type:'open_app',cmd:url,label:'Open '+url}}
if(m.match(/git pull|pull git/))return{type:'git_pull',cmd:'C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting',label:'Git Pull'};
if(m.match(/git push|push git/))return{type:'git_push',cmd:'C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting',label:'Git Push'};
if(m.match(/clipboard|presse-papier|copié/))return{type:'clipboard_get',cmd:'clipboard_get',label:'Get Clipboard'};
if(m.match(/notif|rappel|remind/)){const text=msg.replace(/^(notif|notification|rappel|remind)\s*/i,'').trim()||'Rappel WEVAL';return{type:'notify',cmd:text,label:'Notify: '+text.substring(0,30)}}
if(m.match(/wifi|réseau|network/))return{type:'powershell',cmd:'netsh wlan show interfaces|Select-String SSID,Signal,Speed,Channel',label:'WiFi Status'};
if(m.match(/ip pub|adresse ip|mon ip/))return{type:'powershell',cmd:'(Invoke-WebRequest -Uri https://api.ipify.org -UseBasicParsing).Content',label:'IP Publique'};
if(m.match(/batterie|battery/))return{type:'powershell',cmd:'(Get-CimInstance Win32_Battery|Select EstimatedChargeRemaining,BatteryStatus)|FT',label:'Battery'};
if(m.match(/^ps:|^powershell:/i)){const cmd=msg.replace(/^(ps|powershell):\s*/i,'');return{type:'powershell',cmd,label:'PS: '+cmd.substring(0,40)}}
if(m.match(/^dl:|^download:/i)){const url=msg.replace(/^(dl|download):\s*/i,'');return{type:'download',cmd:url,label:'Download '+url.substring(0,40)}}
if(m.match(/ferme|kill|stop/)){const app=msg.replace(/^(ferme|kill|stop)\s+/i,'').trim();return{type:'powershell',cmd:`Stop-Process -Name "${app}" -Force -EA 0;"Killed ${app}"`,label:'Kill '+app}}
if(m.match(/liste|ls|dir|dossier|folder/)){const path=msg.replace(/^(liste|ls|dir|dossier|folder)\s*/i,'').trim()||'C:\\Users\\Yace\\Desktop';return{type:'list_dir',cmd:path,label:'List '+path}}
if(m.match(/routine|matin/)){runRecipe('morning');return null}
if(m.match(/fin.*(journ|day)|soir/)){runRecipe('endday');return null}
return null;
}
async function runRecipe(name){
const recipes={
morning:[{type:'git_pull',cmd:'C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting',label:'Git Pull'},{type:'open_url',cmd:'https://weval-consulting.com',label:'Open WEVAL'},{type:'open_url',cmd:'https://weval-consulting.com/arsenal-proxy/ceo-dashboard.html',label:'Open Arsenal'},{type:'open_url',cmd:'https://crm.weval-consulting.com',label:'Open CRM'},{type:'open_url',cmd:'https://analytics.weval-consulting.com',label:'Open Analytics'},{type:'screenshot',cmd:'screenshot',label:'Morning Screenshot'}],
devsetup:[{type:'git_pull',cmd:'C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting',label:'Git Pull'},{type:'open_app',cmd:'code',label:'VS Code'},{type:'open_url',cmd:'https://weval-consulting.com/wevads-ia/',label:'WEVADS IA'},{type:'open_app',cmd:'terminal',label:'Terminal'}],
endday:[{type:'git_push',cmd:'End of day auto-commit',label:'Git Push'},{type:'screenshot',cmd:'screenshot',label:'Final Screenshot'},{type:'powershell',cmd:'Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name,CPU,WorkingSet',label:'Top Processes'},{type:'battery_report',cmd:'battery',label:'Battery Report'},{type:'notify',cmd:'Fin de journee - tout est sauvegarde',label:'Notification'}],
monitoring:[{type:'sysinfo',cmd:'sysinfo',label:'System Info'},{type:'powershell',cmd:'Get-Process | Sort-Object WorkingSet -Desc | Select-Object -First 15 Name,@{N="RAM_MB";E={[math]::Round($_.WorkingSet/1MB)}}',label:'Top RAM'},{type:'network_info',cmd:'network',label:'Network Info'},{type:'powershell',cmd:'Get-NetTCPConnection -State Established | Group-Object RemoteAddress | Sort-Object Count -Desc | Select-Object -First 10 Count,Name',label:'Active Connections'},{type:'screenshot',cmd:'screenshot',label:'Screenshot'}],
gitfull:[{type:'git_pull',cmd:'C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting',label:'Git Pull'},{type:'git_status',cmd:'status',label:'Git Status'},{type:'powershell',cmd:'cd C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting; git log --oneline -5',label:'Git Log'},{type:'notify',cmd:'Git sync complete',label:'Done'}],
alltools:[{type:'open_url',cmd:'https://weval-consulting.com',label:'WEVAL Site'},{type:'open_url',cmd:'https://weval-consulting.com/arsenal-proxy/ceo-dashboard.html',label:'Arsenal'},{type:'open_url',cmd:'https://crm.weval-consulting.com',label:'CRM'},{type:'open_url',cmd:'https://weval-consulting.com/wevads-ia/',label:'WEVADS IA'},{type:'open_url',cmd:'https://analytics.weval-consulting.com',label:'Analytics'},{type:'open_url',cmd:'https://weval-consulting.com/products/wevialife-app.html',label:'WEVIA LIFE'},{type:'open_url',cmd:'https://claude.ai',label:'Claude AI'},{type:'open_url',cmd:'https://github.com/Yacineutt',label:'GitHub'}],
performance:[{type:'powershell',cmd:'Get-Counter "\\Processor(_Total)\\% Processor Time","\\Memory\\Available MBytes","\\PhysicalDisk(_Total)\\% Disk Time" -SampleInterval 2 -MaxSamples 3',label:'CPU/RAM/Disk Live'},{type:'powershell',cmd:'Get-CimInstance Win32_Battery | Select EstimatedChargeRemaining,BatteryStatus',label:'Battery'},{type:'powershell',cmd:'Get-CimInstance Win32_LogicalDisk | Select DeviceID,@{N="Free_GB";E={[math]::Round($_.FreeSpace/1GB,1)}},@{N="Total_GB";E={[math]::Round($_.Size/1GB,1)}}',label:'Disks'},{type:'powershell',cmd:'(Get-CimInstance Win32_OperatingSystem).LastBootUpTime',label:'Uptime'}],
quickdeploy:[{type:'git_pull',cmd:'C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting',label:'Git Pull'},{type:'powershell',cmd:'cd C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting; git add -A; git commit -m "Quick deploy $(Get-Date -Format yyyy-MM-dd-HHmm)"',label:'Git Commit'},{type:'git_push',cmd:'Quick deploy',label:'Git Push'},{type:'screenshot',cmd:'screenshot',label:'Confirm Screenshot'}],
cleanup:[{type:'powershell',cmd:'Remove-Item $env:TEMP\\* -Recurse -Force -EA 0;Remove-Item C:\\Users\\Yace\\Downloads\\*.tmp -Force -EA 0;Clear-RecycleBin -Force -EA 0;"Cleanup done"',label:'Full Cleanup'}],
backup:[{type:'git_push',cmd:'C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting',label:'Git Push'},{type:'screenshot',cmd:'screenshot',label:'Backup Screenshot'}],
security:[{type:'powershell',cmd:'Get-NetFirewallRule -Enabled True -Direction Inbound|Select DisplayName,Action|FT -Auto',label:'Firewall'},{type:'powershell',cmd:'Get-NetTCPConnection -State Listen|Select LocalPort,OwningProcess|Sort LocalPort|FT',label:'Ports'},{type:'powershell',cmd:'Get-CimInstance Win32_StartupCommand|Select Name,Command|FT -Auto',label:'Startup'}],
monitoring:[{type:'sysinfo',cmd:'sysinfo',label:'Full SysInfo'},{type:'powershell',cmd:'Get-Process|Sort WS -Desc|Select -First 10 Name,@{N="RAM(MB)";E={[math]::Round($_.WS/1MB)}}|FT',label:'RAM Usage'}],
present:[{type:'open_url',cmd:'https://weval-consulting.com',label:'Open Site'},{type:'powershell',cmd:'Stop-Process -Name "slack","discord","telegram" -Force -EA 0;"Distractions killed"',label:'Kill Distractions'}],
endday:[{type:'git_push',cmd:'C:\\Users\\Yace\\Desktop\\CLAUDE\\weval-consulting',label:'Git Push'},{type:'screenshot',cmd:'screenshot',label:'End Screenshot'},{type:'powershell',cmd:'Remove-Item $env:TEMP\\* -Recurse -Force -EA 0;"Cleaned"',label:'Cleanup'},{type:'powershell',cmd:'rundll32.exe user32.dll,LockWorkStation',label:'Lock Screen'}]
};
const steps=recipes[name];if(!steps)return;
addChat('ai',`Lancement automation <code>${name}</code> (${steps.length} étapes)...`);
for(const s of steps){
await api({action:'push',...s,source:'recipe-'+name,priority:'9'});
}
addChat('ai',`${steps.length} tâches envoyées au Blade.`);
loadStatus();
}
function esc(s){if(!s)return'';const d=document.createElement('div');d.textContent=s;return d.innerHTML}
function clipSet(){const t=prompt('Texte à mettre dans le clipboard:');if(t)pushQ('clipboard_set','Set Clipboard',t)}
function customNotify(){const t=prompt('Message de notification:');if(t)pushQ('notify','Custom Notify',t)}
function browseDir(){const p=document.getElementById('file-path').value;pushQ('list_dir','Browse: '+p,p)}
function writeFile(){const p=document.getElementById('file-write-path').value;const c=document.getElementById('file-content').value;if(!p||!c){alert('Path + content requis');return}pushQ('file_write','Write File',p+'|||'+c)}
async function loadStatus(){
try{
const d=await api({action:'status'});
const b=d.blade||{online:!!(d.heartbeat&&d.heartbeat.ts),heartbeat:d.heartbeat,stats:d.stats};
if(b&&b.force_reload&&!window._reloaded){window._reloaded=1;location.reload()}document.getElementById('dot').className='dot '+((b.online||(b.heartbeat&&b.heartbeat.ts))?'on':'');
document.getElementById('top-status').textContent=(b.heartbeat&&b.heartbeat.ts)?'ONLINE — Sentinel v2.2':'EN ATTENTE — installer Sentinel Agent';document.getElementById('top-status').style.color=(b.heartbeat&&b.heartbeat.ts)?'#22c55e':'#ef4444';
document.getElementById('top-status').style.color=b.online?'var(--green)':'var(--red)';
if(b.heartbeat){
const h=b.heartbeat;
document.getElementById('r-cpu').textContent=h.cpu||'—';
document.getElementById('r-ram').textContent=h.ram||'—';
document.getElementById('r-disk').textContent=h.disk||'—';
document.getElementById('r-up').textContent=h.uptime||'—';
document.getElementById('r-user').textContent=h.user||'—';
document.getElementById('r-ts').textContent=h.ts?new Date(h.ts).toLocaleTimeString('fr-FR'):'—';
const cpuPct=parseInt(h.cpu)||0;
const ramPct=parseInt(h.ram)||0;
const diskPct=parseInt(h.disk)||0;
document.getElementById('bar-cpu').style.width=cpuPct+'%';
document.getElementById('bar-ram').style.width=ramPct+'%';
document.getElementById('bar-disk').style.width=diskPct+'%';
document.getElementById('bar-cpu').style.background=cpuPct>80?'var(--red)':cpuPct>50?'var(--amber)':'var(--blue)';
document.getElementById('bar-ram').style.background=ramPct>85?'var(--red)':ramPct>60?'var(--amber)':'var(--purple)';
}
document.getElementById('r-count').textContent=`(${b.tasks.pending} pending)`;
// Mini tasks
const tl=await api({action:'list'});
document.getElementById('r-tasks').innerHTML=(tl.tasks||[]).slice(0,8).map(t=>`<div class="task-mini"><span class="tm-status ${t.status}"></span><strong>${esc(t.label).substring(0,25)}</strong><br><span style="color:var(--t3)">${t.type} · ${t.status}</span></div>`).join('');
}catch(e){}
}
async function loadTasks(){
const d=await api({action:'list'});
const el=document.getElementById('tasks-full');
if(!d.tasks||!d.tasks.length){el.innerHTML='<div class="loading">Aucune tâche</div>';return}
el.innerHTML=d.tasks.map(t=>`<div style="background:var(--s2);border:1px solid var(--brd);border-radius:8px;padding:12px;margin-bottom:8px"><div style="display:flex;justify-content:space-between;align-items:center"><strong>${esc(t.label)}</strong><span style="font-size:10px;padding:2px 8px;border-radius:99px;background:${t.status==='done'?'rgba(0,224,158,.12)':t.status==='failed'?'rgba(255,77,106,.12)':'rgba(255,181,71,.12)'};color:${t.status==='done'?'var(--green)':t.status==='failed'?'var(--red)':'var(--amber)'}">${t.status}</span></div><div style="font-family:var(--mono);font-size:11px;color:var(--t2);margin-top:4px">${esc(t.cmd).substring(0,100)}</div>${t.result?`<pre style="font-family:var(--mono);font-size:11px;color:var(--cyan);margin-top:6px;background:var(--s1);padding:8px;border-radius:4px;max-height:100px;overflow:auto;white-space:pre-wrap">${esc(t.result).substring(0,500)}</pre>`:``}${t.error?`<pre style="font-family:var(--mono);font-size:11px;color:var(--red);margin-top:4px">${esc(t.error)}</pre>`:``}<div style="font-size:10px;color:var(--t3);margin-top:4px">${t.created?new Date(t.created).toLocaleString('fr-FR'):''} · ${t.source||'api'} · ${t.type}</div></div>`).join('');
}
async function clearTasks(){await api({action:'clear'});loadTasks();loadStatus()}
loadStatus();setInterval(loadStatus,12000);
</script>
<script>
// === UX: Dynamic health bar colors ===
function updateBarColor(id, pct) {
var el = document.getElementById(id);
if (!el) return;
el.className = 'fill ' + (pct > 85 ? 'crit' : pct > 70 ? 'warn' : 'ok');
}
// Patch existing loadStatus to add bar colors
var _origLoadStatus = typeof loadStatus === 'function' ? loadStatus : null;
if (_origLoadStatus) {
var _newLoadStatus = async function() {
await _origLoadStatus();
// Apply colors after status loads
['r-cpu','r-ram','r-disk'].forEach(function(id) {
var el = document.getElementById(id);
if (el) {
var pct = parseInt(el.textContent);
if (!isNaN(pct)) {
var barId = 'bar-' + id.split('-')[1];
updateBarColor(barId, pct);
}
}
});
};
loadStatus = _newLoadStatus;
}
// Update agent download link
document.querySelectorAll('a[href*="sentinel-agent"]').forEach(function(a) {
a.textContent = a.textContent.replace('v2.1', 'v2.2');
});
</script>
<script src="/blade-upgrade.js?v=5"></script>
<script>
// WEVIA Task Poller — auto-fetch and execute pending tasks
(function(){
const API = "/api/blade-poll.php?k=BLADE2026";
const POLL_INTERVAL = 15000; // 15 seconds
async function pollAndExecute() {
try {
const r = await fetch(API + "&action=poll");
const d = await r.json();
if (d.task && d.task.command) {
console.log("[WEVIA] Task received:", d.task.name || d.task.id);
// Execute via the existing Blade AI controller
const taskFile = d.task._file || "";
try {
// If PowerShell task, send to Blade via existing mechanism
if (d.task.type === "powershell" && window.bladeExec) {
const result = await window.bladeExec(d.task.command);
// Report done
await fetch(API + "&action=task_done", {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: "k=BLADE2026&task_file=" + taskFile + "&result=" + encodeURIComponent(result || "executed")
});
} else {
console.log("[WEVIA] Task type:", d.task.type, "— needs PowerShell executor");
// Show in UI
const queueEl = document.querySelector("[data-queue]") || document.querySelector(".queue");
if (queueEl) queueEl.innerHTML += "<div class=\"task executing\">" + (d.task.name||"task") + " — EXECUTING</div>";
}
} catch(e) {
console.error("[WEVIA] Task exec error:", e);
}
}
} catch(e) { /* silent */ }
}
// Start polling
setInterval(pollAndExecute, POLL_INTERVAL);
console.log("[WEVIA] Task poller active — every " + (POLL_INTERVAL/1000) + "s");
window.taskPoller = { poll: pollAndExecute };
})();
</script>
<script src="/api/blade-tasks/poller.js?v=1775429494"></script>
<script>
// WEVIA Auto-upgrade: check server version every 30s
(function(){
let currentVer = "1775429494";
setInterval(async()=>{
try{
const r = await fetch("/api/blade-tasks/version.txt?t="+Date.now());
const v = await r.text();
if(v.trim() && v.trim() !== currentVer){
console.log("[WEVIA] New version detected! Reloading...");
location.reload(true);
}
}catch(e){}
}, 30000);
window._pageVer = currentVer;
})();
</script>
<!-- ECOSYSTEM WIRE — Opus 9avr -->
<div id="eco-panel" style="position:fixed;bottom:10px;right:10px;width:380px;max-height:500px;overflow-y:auto;background:rgba(15,23,42,0.95);border:1px solid #334155;border-radius:12px;padding:12px;font-family:monospace;font-size:11px;color:#94a3b8;z-index:9999;backdrop-filter:blur(10px)">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
<span style="color:#38bdf8;font-weight:bold;font-size:13px">🧠 WEVAL Ecosystem</span>
<span id="eco-score" style="color:#22c55e;font-weight:bold">...</span>
</div>
<div id="eco-body" style="line-height:1.6">Loading...</div>
</div>
<script>
(function(){
var B=document.getElementById('eco-body'),S=document.getElementById('eco-score');
function load(){
Promise.all([
fetch('/api/wevia-master-autonomous.php').then(r=>r.json()).catch(()=>({})),
fetch('/api/ecosystem-health.php').then(r=>r.json()).catch(()=>({})),
fetch('/api/wevia-master-brain.json').then(r=>r.json()).catch(()=>({}))
]).then(function(d){
var a=d[0],e=d[1],b=d[2];
var mb=b.wevia_master_brain||{};
S.textContent=(a.score||0)+'% '+(a.summary?a.summary.pass+'/'+a.summary.total:'');
S.style.color=(a.score||0)>=90?'#22c55e':(a.score||0)>=70?'#f59e0b':'#ef4444';
var h='';
h+='<div style="color:#38bdf8">L99: '+(e.l99?e.l99.pass+'/'+e.l99.total:'?')+'</div>';
h+='<div>Docker: '+(e.infra?e.infra.docker:'?')+' | Disk: '+(e.infra?e.infra.disk:'?')+'</div>';
h+='<div>Pages: '+(e.ecosystem?e.ecosystem.pages:'?')+' | APIs: '+(e.ecosystem?e.ecosystem.apis:'?')+'</div>';
h+='<div>OSS: '+(e.ecosystem?e.ecosystem.oss:'?')+' | Skills: '+(e.ecosystem?e.ecosystem.skills:'?')+'</div>';
h+='<div>Providers: '+Object.keys(mb.providers||{}).length+' | Crons: '+(e.infra?e.infra.crons:'?')+'</div>';
h+='<div style="margin-top:4px;color:#818cf8">Exec: '+(Object.keys(mb.execution_rights||{}).join(', '))+'</div>';
h+='<div style="margin-top:4px;color:#a78bfa">Auto: '+Object.keys(mb.auto_systems||{}).length+' systems</div>';
if(a.actions&&a.actions.length>0){
h+='<div style="margin-top:6px;color:#ef4444;font-weight:bold">DOWN:</div>';
a.actions.forEach(function(x){h+='<div style="color:#f87171"> '+x.service+' ('+x.code+')</div>';});
}
h+='<div style="margin-top:6px;font-size:10px;color:#475569">Updated: '+new Date().toLocaleTimeString()+'</div>';
B.innerHTML=h;
});
}
load();setInterval(load,30000);
})();
</script>
</body>
</html>