Files
html/automation-hub.html
Opus 16895fa821
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
phase43 doctrine 181 - 19 hubs UX doctrine 60 inject direct
2026-04-24 14:14:51 +02:00

410 lines
19 KiB
HTML
Raw Permalink 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="fr"><head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta charset="UTF-8"><title>Automation Hub · Doctrine 64 ZERO-MANUAL-TASK</title>
<style>
body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;background:#0a0e27;color:#e4e8f7;margin:0;padding:0;line-height:1.5}
.container{max-width:1400px;margin:0 auto;padding:24px}
h1{color:#6ba3ff;border-bottom:2px solid #1e3a8a;padding-bottom:8px;margin-top:0}
h2{color:#c084fc;margin-top:24px}
.card{background:#141933;border:1px solid #263161;border-radius:8px;padding:16px;margin:12px 0}
.flex{display:flex;gap:16px;flex-wrap:wrap}
.flex > div{flex:1;min-width:250px}
.badge{padding:3px 10px;border-radius:4px;font-size:11px;font-weight:bold;display:inline-block}
.ok{background:#10b981;color:#fff}.warn{background:#f59e0b;color:#111}.ko{background:#ef4444;color:#fff}.info{background:#3b82f6;color:#fff}.auto{background:#8b5cf6;color:#fff}
table{width:100%;border-collapse:collapse}th,td{padding:10px;border-bottom:1px solid #263161;text-align:left;font-size:13px;vertical-align:top}
th{background:#1e2549;color:#9ca8d3;font-size:11px;text-transform:uppercase}
button{background:#1e3a8a;color:#fff;border:0;padding:8px 14px;border-radius:4px;cursor:pointer;font-size:12px;margin:2px}
button:hover{background:#2950a7}button.primary{background:#10b981}
a{color:#6ba3ff}
code{background:#0f1529;padding:2px 6px;border-radius:3px;color:#fbbf24;font-family:Monaco,monospace;font-size:12px}
.step{background:#0f1529;padding:12px;border-radius:4px;margin-bottom:8px;border-left:3px solid #6ba3ff}
/* === WEVIA Gemini Rolling v2 VISIBLE Enrichment (wave 306 batch) === */
.kpi,[class*="card"],[class*="panel"],[class*="room"],.stat-card,.metric-card,.hub-card,.widget,.stat,.box{position:relative!important}
.kpi,[class*="card"],.stat-card,.metric-card,.hub-card{animation:geV2Entrance .8s cubic-bezier(.34,1.56,.64,1) backwards}
.kpi:nth-child(1),[class*="card"]:nth-child(1){animation-delay:0s}
.kpi:nth-child(2),[class*="card"]:nth-child(2){animation-delay:.09s}
.kpi:nth-child(3),[class*="card"]:nth-child(3){animation-delay:.18s}
.kpi:nth-child(4),[class*="card"]:nth-child(4){animation-delay:.27s}
.kpi:nth-child(5),[class*="card"]:nth-child(5){animation-delay:.36s}
.kpi:nth-child(6),[class*="card"]:nth-child(6){animation-delay:.45s}
@keyframes geV2Entrance{from{opacity:0;transform:translateY(24px) scale(.94)}to{opacity:1;transform:translateY(0) scale(1)}}
.kpi,[class*="card"],.stat-card,.metric-card,.hub-card,.widget{border:1px solid transparent!important;box-shadow:0 0 0 1px rgba(236,72,153,.15),0 4px 16px rgba(0,0,0,.25)!important;transition:box-shadow .4s,transform .3s cubic-bezier(.34,1.56,.64,1),filter .3s!important}
.kpi:hover,[class*="card"]:hover,.stat-card:hover,.metric-card:hover,.hub-card:hover{transform:translateY(-6px) scale(1.03)!important;filter:brightness(1.2)!important;box-shadow:0 0 0 2px rgba(236,72,153,.6),0 12px 32px rgba(236,72,153,.25),0 0 24px rgba(78,205,196,.2)!important}
.kpi::before,[class*="card"]::before,.stat-card::before,.metric-card::before,.hub-card::before{content:"";position:absolute;top:12px;right:12px;width:10px;height:10px;border-radius:50%;background:radial-gradient(circle,#2ed573,#1a9a4e);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5);animation:geV2Pulse 1.6s ease-out infinite;z-index:100;pointer-events:none}
@keyframes geV2Pulse{0%{transform:scale(1);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5)}50%{transform:scale(1.4);box-shadow:0 0 20px #2ed573,0 0 40px rgba(46,213,115,.8)}100%{transform:scale(1);box-shadow:0 0 12px #2ed573,0 0 24px rgba(46,213,115,.5)}}
body::after{content:"";position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at 70% 30%,transparent 40%,rgba(236,72,153,.06) 100%),radial-gradient(ellipse at 30% 70%,transparent 40%,rgba(78,205,196,.04) 100%);animation:geV2Ambient 10s ease-in-out infinite;z-index:0}
@keyframes geV2Ambient{0%,100%{opacity:.5}50%{opacity:1}}
h1,.header-title,.main-title,.hub-title,.page-title{background-image:linear-gradient(90deg,currentColor 0%,currentColor 40%,rgba(236,72,153,1) 50%,currentColor 60%,currentColor 100%)!important;background-size:200% auto!important;-webkit-background-clip:text!important;background-clip:text!important;-webkit-text-fill-color:transparent!important;animation:geV2Shimmer 5s linear infinite!important}
@keyframes geV2Shimmer{0%{background-position:200% center}100%{background-position:-200% center}}
/* Doctrine zero chevauchement - hide common offenders */
.opus-x-btn,.toggle-top-right-btn,.fab-corner{display:none!important}
/* === end WEVIA Gemini Rolling v2 batch === */
</style><!-- DOCTRINE-60-UX-ENRICH direct-inject-20260424-140611 -->
<style id="doctrine60-ux-direct">
/* DOCTRINE-60-UX-ENRICH injected-direct */
body::before {
content: '';
position: fixed;
top: 0; left: 0; width: 100vw; height: 100vh;
background: radial-gradient(circle at 50% 50%, rgba(100,180,255,0.08), transparent 60%);
pointer-events: none;
z-index: -1;
}
.card, .kpi, .panel, .btn {
transition: all 0.3s cubic-bezier(0.2,0,0.1,1);
}
.card:hover, .kpi:hover, .panel:hover {
box-shadow: 0 4px 20px rgba(100,180,255,0.2);
border-color: rgba(100,180,255,0.5);
}
@keyframes pulseD60 {
0%,100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.05); }
}
.pulse, .live-indicator, .active, .online {
animation: pulseD60 3s ease-in-out infinite;
}
.modal, .chat, .speech, .overlay {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.enter-stagger {
animation: enterStagD60 0.5s cubic-bezier(0.2,0,0.1,1) forwards;
}
@keyframes enterStagD60 {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div class="container">
<h1>🤖 Automation Hub · Doctrine 64 ZERO-MANUAL-TASK</h1>
<p>Plus aucune action humaine. Toutes les tâches sont automatisées via WEVIA + Selenium/Playwright + Blade IA.</p>
<h2>📊 État automations</h2>
<div id="status" class="card">Chargement…</div>
<h2>🔧 Automations wired</h2>
<div class="card">
<table>
<tr><th>Task</th><th>Script</th><th>Cron</th><th>Trigger WEVIA</th><th>Statut</th></tr>
<tr>
<td><strong>CRM pipeline sync</strong></td>
<td><code>/usr/local/bin/deliverads-guard.sh</code> (S95)</td>
<td>every 30min + 1h + 4×/jour</td>
<td><code>observe crm</code> / <code>run crm</code></td>
<td><span class="badge ok">ACTIVE</span></td>
</tr>
<tr>
<td><strong>CRM observation daily</strong></td>
<td><code>/opt/weval-l99/crm-observation.py</code> (Playwright)</td>
<td>daily 9am</td>
<td><code>observe crm pipeline</code></td>
<td><span class="badge ok">ACTIVE</span></td>
</tr>
<tr>
<td><strong>Blade self-heal</strong></td>
<td><code>/opt/weval-l99/blade-selfheal.py</code></td>
<td>every 5min</td>
<td><code>blade health</code></td>
<td><span class="badge ok">ACTIVE</span></td>
</tr>
<tr>
<td><strong>OVH S151 cancel</strong></td>
<td><code>/opt/weval-l99/ovh-s151-cancel.py</code> (Selenium)</td>
<td>daily 10am (check+queue)</td>
<td><code>cancel ovh</code></td>
<td><span class="badge warn">PENDING creds</span></td>
</tr>
<tr>
<td><strong>Azure AD rotation</strong></td>
<td><code>/api/azure-reregister-api.php</code></td>
<td>on-demand</td>
<td><code>rotate azure</code> / page UI</td>
<td><span class="badge ok">DONE (3 tenants rotated)</span></td>
</tr>
<tr>
<td><strong>Blade task observer</strong></td>
<td>Task queue + Chrome headless Windows</td>
<td>daily (via Blade agent)</td>
<td>queue WEVIA</td>
<td><span class="badge ok">ACTIVE</span></td>
</tr>
<tr>
<td><strong>NonReg guard</strong></td>
<td><code>/opt/weval-l99/wevia-nonreg-agent.php</code></td>
<td>every 15min</td>
<td><code>nonreg</code></td>
<td><span class="badge ok">153/153</span></td>
</tr>
<tr>
<td><strong>L99 auto-update</strong></td>
<td><code>/opt/weval-l99/l99-state-updater.py</code></td>
<td>continuous</td>
<td><code>l99</code></td>
<td><span class="badge ok">100%</span></td>
</tr>
<tr>
<td><strong>Ethica enrich boost</strong></td>
<td><code>/opt/ethica-*-boost.sh</code></td>
<td>4×/jour</td>
<td><code>ethica stats</code></td>
<td><span class="badge ok">+2,933/jour</span></td>
</tr>
</table>
</div>
<h2>🚨 Automations en attente de credentials</h2>
<div class="card" id="pending-list">Chargement…</div>
<h2>🎯 Exceptions doctrine 64 (légitimes)</h2>
<div class="card">
<p>Doctrine 64 tolère certaines exceptions :</p>
<ol>
<li><strong>Décision stratégique pure</strong> : ex. "option A vs B vs C" — Yacine doit vraiment trancher</li>
<li><strong>Authentification physique</strong> : biométrie, hardware key, SMS OTP setup initial</li>
<li><strong>Envoi email commercial au client</strong> (ordre GODMODE spécifique "SAUF SEND MAIL REEL AUTO") — drafts prêts, trigger manuel</li>
</ol>
</div>
<h2>📋 Tâches restantes (avec automation prévue)</h2>
<div class="card">
<div class="step">
<strong>1. Envoyer 3 emails Kaouther</strong> (<span class="badge warn">exception GODMODE</span>)
<br>→ Drafts prêts dans <a href="/kaouther-compose.html">/kaouther-compose.html</a>
<br>→ Clic "Ouvrir dans mail client" → Outlook/Gmail → Send
<br><em>Doctrine 64 exception #3 : envoi commercial réel requiert validation humaine</em>
</div>
<div class="step">
<strong>2. OVH S151 cancel</strong> (<span class="badge auto">AUTOMATED si creds fournies</span>)
<br>→ Script Selenium prêt <code>ovh-s151-cancel.py</code>
<br>→ Ajouter <code>OVH_EMAIL</code> + <code>OVH_PASSWORD</code> dans <code>/etc/weval/secrets.env</code>
<br>→ Cron 10h daily fera le reste
<br><em>Alternative : intent WEVIA <code>setup ovh creds email=X password=Y</code></em>
</div>
<div class="step">
<strong>3. OVH SMS provider</strong> (<span class="badge auto">AUTOMATABLE</span>)
<br>→ Décision stratégique Yacine : A(OVH) / B(Twilio) / C(Infobip recommandé) / D(pas SMS)
<br>→ Après décision : API signup auto via provider
<br><em>Recommandation : Infobip ~€0.04/SMS (MA/DZ/TN)</em>
</div>
<div class="step">
<strong>4. Blade wake</strong> (<span class="badge ok">AUTO DONE</span>)
<br>→ Blade déjà online (heartbeat 1s)
<br>→ Self-heal cron actif : auto-wake si offline &gt; 10min
<br><em>Plus besoin d'action humaine</em>
</div>
</div>
<h2>🔗 Liens</h2>
<div class="card">
<ul>
<li><a href="/api/automation-status-live.php" target="_blank">API status JSON</a></li>
<li><a href="/wiki/DOCTRINE-64-ZERO-MANUAL-TASK.md" target="_blank">Doctrine 64</a></li>
<li><a href="/crm-pipeline-live.html">CRM pipeline</a></li>
<li><a href="/blade-control.html">Blade control</a></li>
<li><a href="/azure-reregister.html">Azure AD re-register</a></li>
<li><a href="/kaouther-compose.html">Kaouther compose</a></li>
<li><a href="/tasks-live.html">Tasks & logs live</a></li>
<li><a href="/wevia-master.html">WEVIA Master (chat)</a></li>
</ul>
</div>
</div>
<script>
async function load() {
try {
const r = await fetch('/api/automation-status-live.php');
const d = await r.json();
const sc = document.getElementById('status');
const auto_count = Object.keys(d.automations || {}).length;
const active = Object.values(d.automations || {}).filter(a => a.status === 'active').length;
sc.innerHTML = `
<div class="flex">
<div><strong>${active}/${auto_count}</strong> automations actives</div>
<div>Crons installés: <strong>${d.crons_count || 0}</strong></div>
<div>Scripts: <strong>${d.scripts_count || 0}</strong></div>
<div>Pending creds: <strong>${(d.pending || []).length}</strong></div>
</div>`;
const pl = document.getElementById('pending-list');
if (d.pending && d.pending.length) {
pl.innerHTML = d.pending.map(p => `
<div class="step">
<strong>${p.task}</strong>
<span class="badge warn">${p.status}</span>
<br><em>${p.reason}</em>
<br><small style="color:#9ca8d3">Depuis ${p.ts}</small>
</div>
`).join('');
} else {
pl.innerHTML = '<p style="color:#10b981">✅ Aucune automation bloquée</p>';
}
} catch(e) {
document.getElementById('status').innerHTML = 'Erreur: ' + e.message;
}
}
load();
setInterval(load, 30000);
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
<script>
(function(){
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
var d = document;
var m = d.createElement('div');
m.id = 'opus-udrill';
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
var inner = d.createElement('div');
inner.id = 'opus-udrill-in';
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
inner.addEventListener('click', function(e){ e.stopPropagation(); });
m.appendChild(inner);
m.addEventListener('click', function(){ m.style.display='none'; });
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
(d.body || d.documentElement).appendChild(m);
function openCard(card) {
// Clone card content + show close btn + increase font-size
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
inner.innerHTML = html;
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
m.style.display = 'flex';
}
function wire(root) {
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
var cards = root.querySelectorAll(sels);
for (var i = 0; i < cards.length; i++) {
var c = cards[i];
if (c.__opusWired) continue;
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
var r = c.getBoundingClientRect();
if (r.width < 60 || r.height < 40) continue;
c.__opusWired = true;
c.style.cursor = 'pointer';
c.setAttribute('role','button');
c.setAttribute('tabindex','0');
c.addEventListener('click', function(ev){
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
if (ev.target.closest('a,button,input,select')) return;
ev.preventDefault(); ev.stopPropagation();
openCard(this);
});
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
}
}
// Initial + mutation observer
var initRun = function(){ wire(d.body || d.documentElement); };
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
else initRun();
var mo = new MutationObserver(function(muts){
var newCard = false;
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
if (newCard) initRun();
});
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
})();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
<!-- === OPUS HONEST NR/L99 OVERLAY v1 19avr - append-only doctrine #14 === -->
<script>
(function(){
if (window.__opusHonestOverlay) return; window.__opusHonestOverlay = true;
async function updateHonestValues(){
try {
const r = await fetch('/api/l99-honest.php', {cache:'no-store'});
const d = await r.json();
if (!d.ok) return;
const realNR = `${d.combined.pass}/${d.combined.total}`;
const realSigma = d.sigma;
// Find elements showing the myth values
const mythRegex = /(153\/153|304\/304|NR status 153\/153|L99 status 304\/304|NR 153\/153|L99 304\/304)/g;
// Walk text nodes
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
const toReplace = [];
let node;
while (node = walker.nextNode()) {
if (node.nodeValue && mythRegex.test(node.nodeValue)) toReplace.push(node);
}
toReplace.forEach(textNode => {
const parent = textNode.parentNode;
if (!parent || parent.hasAttribute('data-opus-honest-applied')) return;
const newText = textNode.nodeValue.replace(/153\/153/g, realNR).replace(/304\/304/g, realNR);
textNode.nodeValue = newText;
parent.setAttribute('data-opus-honest-applied', '1');
});
// Add a small badge bottom-right showing honest live status
if (!document.getElementById('opus-honest-badge')) {
const b = document.createElement('div');
b.id = 'opus-honest-badge';
b.style.cssText = 'position:fixed;bottom:12px;right:12px;background:linear-gradient(90deg,#14b8a6,#a855f7);color:#05060a;padding:6px 12px;font:10px/1.3 Inter,system-ui,sans-serif;font-weight:700;border-radius:8px;z-index:99993;box-shadow:0 4px 12px rgba(0,0,0,0.3);cursor:pointer;max-width:280px';
b.title = 'Cliquer pour détails';
b.innerHTML = `✓ NR ${realNR} · ${realSigma} live`;
b.onclick = () => {
alert(`HONEST NonReg (doctrine #4):\n\nmaster: ${d.master.pass}/${d.master.total}\nopus: ${d.opus.pass}/${d.opus.total}\ncombined: ${realNR}\nsigma: ${realSigma}\n\n${d.myth_153}\n${d.myth_304}`);
};
document.body.appendChild(b);
}
} catch(e){console.error('L99-honest fetch error:', e);}
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', updateHonestValues);
else updateHonestValues();
setInterval(updateHonestValues, 90000);
})();
</script>
<!-- === OPUS HONEST END === -->
<script src="/api/archi-meta-badge.js" defer></script>
<script src="/api/a11y-auto-enhancer.js" defer></script>
<!-- WTP_UDOCK_V1 (Opus 21-avr t31b3) --><script src="/wtp-unified-dock.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
<!-- DOCTRINE-60-UX-JS --><script id="doctrine60-ux-js-direct">
// DOCTRINE-60-UX-JS staggered entrance
(function(){
if (!('IntersectionObserver' in window)) return;
const obs = new IntersectionObserver((entries) => {
entries.forEach((e, i) => {
if (e.isIntersecting) {
setTimeout(() => e.target.classList.add('enter-stagger'), i * 80);
obs.unobserve(e.target);
}
});
});
document.querySelectorAll('.card, .kpi, .panel').forEach(el => obs.observe(el));
})();
</script>
</body></html>