Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
6 pages centrales enrichies via cascade Cerebras: - wevia-chat-v2 (+1328B) - sovereign-monitor (+1271B) - wevia-audit (+1266B) - wevia-console (+1263B) - wevia-autonomy-dashboard (+1292B) - wevia-business-visual-studio (+1300B) Handler /var/www/html/api/enrich-hub-cascade.sh: - Try Cerebras qwen-3-235b primary - Fallback Ollama llama3.2 LOCAL (zero rate limit) - GOLD backup + chattr handling + lint - Markers DOCTRINE-60-UX-ENRICH idempotent Intent wevia_enrich_hub_cascade_cerebras_ollama wired pour chat NL. Total: 18 pages UX doctrine 60 (12 avant + 6 aujourd hui). Cascade zero-rate-limit effective: Cerebras OK + Ollama llama3.2 ready. Disk 87% stable apres recovery phase 29 +19GB.
357 lines
18 KiB
HTML
357 lines
18 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr"><head><meta charset="utf-8">
|
|
<title>WEVIA Audit Trail - v2 - Filters + Search + Export</title>
|
|
<style>
|
|
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#0e1320;color:#e2e8f0;margin:0;padding:24px;line-height:1.5}
|
|
header{max-width:1500px;margin:0 auto 24px}
|
|
h1{color:#e94560;font-size:32px;margin:0 0 4px}
|
|
.subtitle{color:#94a3b8;font-size:13px}
|
|
.container{max-width:1500px;margin:0 auto}
|
|
.kpis{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin:20px 0}
|
|
.kpi{background:#1a2333;border:1px solid #1f2937;border-radius:8px;padding:14px;text-align:center}
|
|
.kpi-value{color:#e94560;font-size:28px;font-weight:900;display:block}
|
|
.kpi-label{color:#94a3b8;font-size:11px;text-transform:uppercase;letter-spacing:1px}
|
|
section{margin-top:32px}
|
|
h2{color:#7c5cff;border-bottom:1px solid #2a3344;padding-bottom:8px;font-size:18px}
|
|
.filters{display:flex;gap:12px;margin:12px 0;flex-wrap:wrap;align-items:center}
|
|
.filters input,.filters select{background:#0a0f1a;color:#e2e8f0;border:1px solid #2a3344;padding:8px 12px;border-radius:6px;font-size:13px;font-family:inherit}
|
|
.filters input:focus,.filters select:focus{border-color:#7c5cff;outline:none}
|
|
.filters label{color:#94a3b8;font-size:11px;text-transform:uppercase;letter-spacing:0.5px}
|
|
.grid2{display:grid;grid-template-columns:repeat(2,1fr);gap:14px}
|
|
.card{background:#1a2333;border:1px solid #1f2937;border-radius:8px;padding:14px;transition:border-color .2s}
|
|
.card:hover{border-color:#7c5cff}
|
|
.card.hidden{display:none}
|
|
.card h3{margin:0 0 8px;color:#e2e8f0;font-size:14px;word-break:break-all}
|
|
.badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:10px;font-weight:700;margin-right:6px;vertical-align:middle}
|
|
.badge-marker{background:linear-gradient(135deg,#7c5cff,#e94560);color:#fff}
|
|
.badge-commit{background:#10b981;color:#fff;font-family:monospace}
|
|
.badge-date{background:#334155;color:#e2e8f0}
|
|
.badge-doctrine{background:#f59e0b;color:#fff}
|
|
.meta{font-size:11px;color:#94a3b8;margin-top:4px}
|
|
.empty{color:#64748b;padding:20px;text-align:center;font-style:italic}
|
|
table{width:100%;border-collapse:collapse;margin-top:12px}
|
|
th{background:#1a2333;color:#7c5cff;text-align:left;padding:8px 12px;font-size:12px;border-bottom:2px solid #2a3344;cursor:pointer;user-select:none}
|
|
th:hover{background:#252f40}
|
|
td{padding:8px 12px;border-bottom:1px solid #1f2937;font-size:12px}
|
|
td code{background:#0a0f1a;padding:2px 6px;border-radius:3px;color:#7c5cff;font-size:11px}
|
|
.btn{display:inline-block;padding:6px 14px;background:linear-gradient(135deg,#7c5cff,#e94560);color:#fff;border-radius:6px;font-size:12px;font-weight:700;text-decoration:none;margin-right:8px;border:none;cursor:pointer;font-family:inherit}
|
|
.btn:hover{opacity:0.85}
|
|
.btn-secondary{background:#1f2937;color:#94a3b8}
|
|
.btn-success{background:linear-gradient(135deg,#10b981,#047857)}
|
|
.refresh-bar{display:flex;gap:10px;align-items:center;margin:12px 0;padding:10px;background:#1a2333;border-radius:8px;font-size:12px;flex-wrap:wrap}
|
|
.loading{color:#94a3b8;font-style:italic}
|
|
.count-info{color:#7c5cff;font-weight:700}
|
|
</style><!-- DOCTRINE-60-UX-ENRICH cerebras-qwen-235b 20260424-104118 --><style id="doctrine60-ux-wevia-audit">
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: radial-gradient(circle, rgba(0,0,0,0.12), transparent 70%);
|
|
z-index: -1;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.card, .btn, .kpi, .panel {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.enter-stagger {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.6; }
|
|
}
|
|
.pulse, .active, .live-indicator, .online {
|
|
animation: pulse 3s ease-in-out infinite;
|
|
}
|
|
|
|
.card:hover {
|
|
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.modal, .chat, .speech, .overlay {
|
|
backdrop-filter: blur(12px);
|
|
}
|
|
|
|
</style>
|
|
</head><body>
|
|
|
|
<header>
|
|
<h1>WEVIA Audit Trail <span style="color:#7c5cff;font-size:18px">v2</span></h1>
|
|
<div class="subtitle">Autonomy dashboard - doctrine 146/147/148/149/150/151/152 - Filters + Search + CSV Export</div>
|
|
</header>
|
|
|
|
<div class="container">
|
|
<div class="refresh-bar">
|
|
<button class="btn" onclick="refresh()">Actualiser</button>
|
|
<button class="btn btn-success" onclick="exportCSV()">Export CSV</button>
|
|
<span id="status" class="loading">Chargement...</span>
|
|
<a class="btn btn-secondary" href="/weval-live-ops.html">Live Ops</a>
|
|
<a class="btn btn-secondary" href="/weval-technology-platform.html">WTP</a>
|
|
<a class="btn btn-secondary" href="/cloudbot-social.html">Cloudbot Social</a>
|
|
</div>
|
|
|
|
<div class="kpis" id="kpis">
|
|
<div class="kpi"><span class="kpi-value" id="kpi-pages">-</span><span class="kpi-label">Pages patchees</span></div>
|
|
<div class="kpi"><span class="kpi-value" id="kpi-markers">-</span><span class="kpi-label">Markers WEVIA</span></div>
|
|
<div class="kpi"><span class="kpi-value" id="kpi-intents">-</span><span class="kpi-label">Intents autonomy</span></div>
|
|
<div class="kpi"><span class="kpi-value" id="kpi-presets">-</span><span class="kpi-label">Presets</span></div>
|
|
<div class="kpi"><span class="kpi-value" id="kpi-scripts">-</span><span class="kpi-label">Scripts shell</span></div>
|
|
</div>
|
|
|
|
<section>
|
|
<h2>0. Timeline des patches <span id="timeline-count" class="count-info"></span></h2>
|
|
<div id="timeline-viz" style="background:#1a2333;border:1px solid #1f2937;border-radius:8px;padding:16px;overflow-x:auto;position:relative"></div>
|
|
<div style="font-size:11px;color:#64748b;margin-top:6px">1 point = 1 patch. Hover pour details. Couleur = doctrine.</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>1. Pages patchees <span id="audit-count" class="count-info"></span></h2>
|
|
<div class="filters">
|
|
<label>Search <input type="text" id="search-audit" placeholder="filtre page ou marker..." oninput="applyAuditFilter()"></label>
|
|
<label>Doctrine <select id="filter-doctrine" onchange="applyAuditFilter()"><option value="">Toutes</option></select></label>
|
|
</div>
|
|
<div id="audit-grid" class="grid2"><div class="empty">Chargement...</div></div>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>2. Intents autonomy <span id="intents-count" class="count-info"></span></h2>
|
|
<div class="filters">
|
|
<label>Search <input type="text" id="search-intent" placeholder="filtre intent ou trigger..." oninput="applyIntentFilter()"></label>
|
|
<label>Source <select id="filter-source" onchange="applyIntentFilter()"><option value="">Toutes</option></select></label>
|
|
</div>
|
|
<table>
|
|
<thead><tr><th>Intent name</th><th>Triggers</th><th>Priority</th><th>Source</th></tr></thead>
|
|
<tbody id="intents-tbody"><tr><td colspan="4" class="empty">Chargement...</td></tr></tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>3. Presets <span id="presets-count" class="count-info"></span></h2>
|
|
<table>
|
|
<thead><tr><th>Slug</th><th>Target</th><th>Marker</th></tr></thead>
|
|
<tbody id="presets-tbody"><tr><td colspan="3" class="empty">Chargement...</td></tr></tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<section>
|
|
<h2>4. Scripts shell <span id="scripts-count" class="count-info"></span></h2>
|
|
<table>
|
|
<thead><tr><th>Script</th><th>Size</th><th>Exec</th></tr></thead>
|
|
<tbody id="scripts-tbody"><tr><td colspan="3" class="empty">Chargement...</td></tr></tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<p style="margin-top:40px;padding-top:16px;border-top:1px solid #2a3344;color:#64748b;font-size:11px">
|
|
Doctrine 149 v2 - GODMODE - WEVAL Consulting - 2026-04-23
|
|
</p>
|
|
</div>
|
|
|
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
|
<script>
|
|
let DATA = null;
|
|
|
|
async function refresh(){
|
|
document.getElementById('status').textContent = 'Chargement...';
|
|
try {
|
|
const r = await fetch('/api/wevia-audit-api.php?_=' + Date.now());
|
|
DATA = await r.json();
|
|
if (!DATA.ok) throw new Error('API returned ok=false');
|
|
|
|
document.getElementById('kpi-pages').textContent = DATA.stats.pages_with_markers;
|
|
document.getElementById('kpi-markers').textContent = DATA.stats.total_markers;
|
|
document.getElementById('kpi-intents').textContent = DATA.stats.intents_autonomy;
|
|
document.getElementById('kpi-presets').textContent = DATA.stats.presets_available;
|
|
document.getElementById('kpi-scripts').textContent = DATA.stats.scripts_available;
|
|
|
|
// Build doctrine filter dropdown (extract from commit message)
|
|
const doctrines = new Set();
|
|
DATA.audit.forEach(a => {
|
|
const m = /doctrine[- ]?(\d{3})/i.exec(a.last_commit_msg || '');
|
|
if (m) doctrines.add(m[1]);
|
|
});
|
|
const sel = document.getElementById('filter-doctrine');
|
|
sel.innerHTML = '<option value="">Toutes</option>' + Array.from(doctrines).sort().map(d => '<option value="' + d + '">' + d + '</option>').join('');
|
|
|
|
// Build source filter
|
|
const sources = new Set();
|
|
DATA.intents.forEach(i => sources.add(i.source || '?'));
|
|
const src = document.getElementById('filter-source');
|
|
src.innerHTML = '<option value="">Toutes</option>' + Array.from(sources).sort().map(s => '<option value="' + s + '">' + s + '</option>').join('');
|
|
|
|
renderTimeline();
|
|
renderAudit();
|
|
renderIntents();
|
|
renderPresets();
|
|
renderScripts();
|
|
|
|
document.getElementById('status').textContent = 'OK - ' + DATA.generated_at;
|
|
} catch (e) {
|
|
document.getElementById('status').textContent = 'Erreur: ' + e.message;
|
|
}
|
|
}
|
|
|
|
function renderTimeline(){
|
|
const container = document.getElementById("timeline-viz");
|
|
if (!container) return;
|
|
container.innerHTML = "";
|
|
if (typeof d3 === "undefined") { container.innerHTML = '<div class="empty">D3 loading...</div>'; return; }
|
|
const events = [];
|
|
DATA.audit.forEach(a => { (a.markers||[]).forEach(m => {
|
|
const iso = a.last_commit_iso || a.last_commit_date;
|
|
if (!iso) return;
|
|
const dt = new Date(iso.replace(" ","T"));
|
|
if (isNaN(dt.getTime())) return;
|
|
events.push({time:dt,file:a.file,marker:m.marker,commit:a.last_commit||"",doctrine:a.doctrine||"?",msg:a.last_commit_msg||""});
|
|
}); });
|
|
events.sort((a,b) => a.time - b.time);
|
|
const cnt = document.getElementById("timeline-count");
|
|
if (cnt) cnt.textContent = "(" + events.length + " events)";
|
|
if (events.length === 0) { container.innerHTML = '<div class="empty">Pas de donnees timeline</div>'; return; }
|
|
const W = container.offsetWidth || 1200, H = 160, M = {top:20,right:30,bottom:40,left:50};
|
|
const svg = d3.select(container).append("svg").attr("width",W).attr("height",H);
|
|
const domain = d3.extent(events, e => e.time);
|
|
if (domain[0].getTime() === domain[1].getTime()) { const pad = 30*60*1000; domain[0] = new Date(domain[0].getTime()-pad); domain[1] = new Date(domain[1].getTime()+pad); }
|
|
const x = d3.scaleTime().domain(domain).range([M.left, W-M.right]);
|
|
const DC = {"146":"#10b981","147":"#3b82f6","148":"#8b5cf6","149":"#f59e0b","150":"#ec4899","151":"#06b6d4","152":"#ef4444","153":"#14b8a6","154":"#a855f7","155":"#eab308","156":"#6366f1","157":"#f472b6","158":"#22d3ee","?":"#64748b"};
|
|
svg.append("g").attr("transform","translate(0,"+(H-M.bottom)+")").call(d3.axisBottom(x).ticks(6).tickFormat(d3.timeFormat("%H:%M"))).attr("color","#94a3b8");
|
|
const tooltip = d3.select(container).append("div").style("position","absolute").style("pointer-events","none").style("background","#0a0f1a").style("border","1px solid #2a3344").style("padding","8px 12px").style("border-radius","6px").style("font-size","11px").style("color","#e2e8f0").style("z-index","100").style("opacity","0").style("max-width","320px");
|
|
svg.selectAll("circle").data(events).enter().append("circle").attr("cx", d => x(d.time)).attr("cy", () => M.top+20+Math.random()*(H-M.top-M.bottom-40)).attr("r",7).attr("fill", d => DC[d.doctrine]||"#64748b").attr("stroke","#fff").attr("stroke-width",1.5).style("cursor","pointer").on("mouseover", function(e,d){ d3.select(this).attr("r",10); tooltip.style("opacity",1).html('<b>'+d.marker+'</b><br>'+d.file+'<br>D'+d.doctrine+' | <code>'+d.commit+'</code><br><span style="color:#94a3b8">'+(d.msg.substring(0,100)).replace(/</g,'<')+'</span>').style("left",(e.offsetX+15)+"px").style("top",(e.offsetY-10)+"px"); }).on("mouseout", function(){ d3.select(this).attr("r",7); tooltip.style("opacity",0); });
|
|
const doctrines = Array.from(new Set(events.map(e => e.doctrine))).sort();
|
|
const leg = svg.append("g").attr("transform","translate("+M.left+","+(H-15)+")");
|
|
doctrines.forEach((d,i) => { const g = leg.append("g").attr("transform","translate("+(i*60)+",0)"); g.append("circle").attr("r",5).attr("fill",DC[d]||"#64748b"); g.append("text").attr("x",10).attr("y",4).attr("fill","#94a3b8").style("font-size","10px").text("D"+d); });
|
|
}
|
|
|
|
function renderAudit(){
|
|
const ag = document.getElementById('audit-grid');
|
|
const search = (document.getElementById('search-audit').value || '').toLowerCase();
|
|
const doctrineF = document.getElementById('filter-doctrine').value;
|
|
|
|
const filtered = DATA.audit.filter(a => {
|
|
const hay = (a.file + ' ' + (a.markers||[]).map(m=>m.marker).join(' ') + ' ' + (a.last_commit_msg||'')).toLowerCase();
|
|
if (search && !hay.includes(search)) return false;
|
|
if (doctrineF) {
|
|
const m = /doctrine[- ]?(\d{3})/i.exec(a.last_commit_msg || '');
|
|
if (!m || m[1] !== doctrineF) return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
document.getElementById('audit-count').textContent = '(' + filtered.length + ' / ' + DATA.audit.length + ')';
|
|
|
|
ag.innerHTML = filtered.map(a => {
|
|
const doctrineMatch = /doctrine[- ]?(\d{3})/i.exec(a.last_commit_msg || '');
|
|
const doctrineBadge = doctrineMatch ? '<span class="badge badge-doctrine">D' + doctrineMatch[1] + '</span>' : '';
|
|
return '<div class="card"><h3>' + a.file + '</h3>' +
|
|
'<div>' + (a.markers||[]).map(m => '<span class="badge badge-marker">' + m.marker + '</span>').join('') + '</div>' +
|
|
'<div class="meta">' + doctrineBadge + '<span class="badge badge-commit">' + (a.last_commit||'?') + '</span> <span class="badge badge-date">' + (a.last_commit_date||'?') + '</span></div>' +
|
|
'<div class="meta" style="margin-top:6px">' + (a.last_commit_msg||'').replace(/</g,'<') + '</div>' +
|
|
'<div style="margin-top:10px"><a class="btn" href="' + a.url + '" target="_blank">Voir la page</a></div>' +
|
|
'</div>';
|
|
}).join('') || '<div class="empty">Aucun resultat</div>';
|
|
}
|
|
|
|
function renderIntents(){
|
|
const tb = document.getElementById('intents-tbody');
|
|
const search = (document.getElementById('search-intent').value || '').toLowerCase();
|
|
const srcF = document.getElementById('filter-source').value;
|
|
|
|
const filtered = DATA.intents.filter(i => {
|
|
const hay = (i.name + ' ' + (i.triggers_sample||[]).join(' ')).toLowerCase();
|
|
if (search && !hay.includes(search)) return false;
|
|
if (srcF && i.source !== srcF) return false;
|
|
return true;
|
|
});
|
|
|
|
document.getElementById('intents-count').textContent = '(' + filtered.length + ' / ' + DATA.intents.length + ')';
|
|
|
|
tb.innerHTML = filtered.map(i =>
|
|
'<tr><td><code>' + i.name + '</code></td>' +
|
|
'<td>' + (i.triggers_sample||[]).map(t => '"' + t.replace(/</g,'<') + '"').join(', ') + '<span style="color:#64748b"> ...' + i.triggers_count + '</span></td>' +
|
|
'<td>' + (i.priority||'?') + '</td>' +
|
|
'<td>' + (i.source||'?') + '</td></tr>'
|
|
).join('') || '<tr><td colspan="4" class="empty">Aucun resultat</td></tr>';
|
|
}
|
|
|
|
function renderPresets(){
|
|
const tb = document.getElementById('presets-tbody');
|
|
document.getElementById('presets-count').textContent = '(' + DATA.presets.length + ')';
|
|
tb.innerHTML = DATA.presets.map(p =>
|
|
'<tr><td><code>' + p.slug + '</code></td><td>' + p.target + '</td><td><span class="badge badge-marker">' + p.marker + '</span></td></tr>'
|
|
).join('');
|
|
}
|
|
|
|
function renderScripts(){
|
|
const tb = document.getElementById('scripts-tbody');
|
|
document.getElementById('scripts-count').textContent = '(' + DATA.scripts.length + ')';
|
|
tb.innerHTML = DATA.scripts.map(s =>
|
|
'<tr><td><code>' + s.script + '</code></td><td>' + s.size_bytes + ' B</td><td>' + (s.executable ? 'Oui' : 'Non') + '</td></tr>'
|
|
).join('');
|
|
}
|
|
|
|
function applyAuditFilter(){ renderAudit(); }
|
|
function applyIntentFilter(){ renderIntents(); }
|
|
|
|
function exportCSV(){
|
|
if (!DATA) return;
|
|
const rows = [
|
|
['section','key','value','detail'],
|
|
['stats','pages_with_markers',DATA.stats.pages_with_markers,''],
|
|
['stats','total_markers',DATA.stats.total_markers,''],
|
|
['stats','intents_autonomy',DATA.stats.intents_autonomy,''],
|
|
['stats','presets_available',DATA.stats.presets_available,''],
|
|
['stats','scripts_available',DATA.stats.scripts_available,''],
|
|
];
|
|
DATA.audit.forEach(a => {
|
|
(a.markers||[]).forEach(m => {
|
|
rows.push(['audit', a.file, m.marker, (a.last_commit || '') + ' | ' + (a.last_commit_date || '') + ' | ' + (a.last_commit_msg || '').replace(/,/g,';')]);
|
|
});
|
|
});
|
|
DATA.intents.forEach(i => {
|
|
rows.push(['intent', i.name, i.triggers_count + ' triggers', i.source || '']);
|
|
});
|
|
DATA.presets.forEach(p => {
|
|
rows.push(['preset', p.slug, p.marker, p.target]);
|
|
});
|
|
DATA.scripts.forEach(s => {
|
|
rows.push(['script', s.script, s.size_bytes + ' B', s.executable ? 'exec' : 'not-exec']);
|
|
});
|
|
|
|
const csv = rows.map(r => r.map(c => '"' + String(c).replace(/"/g,'""') + '"').join(',')).join('\n');
|
|
const blob = new Blob([csv], {type:'text/csv;charset=utf-8'});
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = 'wevia-audit-' + new Date().toISOString().replace(/[:.]/g,'-').slice(0,19) + '.csv';
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
refresh();
|
|
setInterval(refresh, 30000);
|
|
</script>
|
|
<!-- DOCTRINE-60-UX-JS --><script id="doctrine60-ux-js-wevia-audit">
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry, index) => {
|
|
if (entry.isIntersecting) {
|
|
setTimeout(() => {
|
|
entry.target.classList.add('enter-stagger');
|
|
}, index * 80);
|
|
}
|
|
});
|
|
}, { threshold: 0.1 });
|
|
|
|
document.querySelectorAll('.card, .btn, .kpi, .panel').forEach(el => {
|
|
observer.observe(el);
|
|
});
|
|
|
|
</script>
|
|
</body></html>
|