auto-sync-all
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled

This commit is contained in:
opus
2026-04-16 17:04:53 +02:00
parent 2ccaa43523
commit a929d549ce
11 changed files with 1593 additions and 26 deletions

View File

@@ -1,5 +1,5 @@
{
"timestamp": "2026-04-16T17:00:12.845088",
"timestamp": "2026-04-16T17:04:52.895997",
"layers": {
"DOCKER": {
"pass": 19,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 KiB

After

Width:  |  Height:  |  Size: 942 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 KiB

After

Width:  |  Height:  |  Size: 797 KiB

View File

@@ -1 +1 @@
{"ts":"17:02","status":"offline"}
{"ts":"17:04","status":"offline"}

View File

@@ -1486,6 +1486,39 @@ switch($action) {
];
break;
case "batch_html_guard":
// Batch HTML_GUARD: protect all .html from displaying raw <!DOCTYPE on 502
$results = ["scanned"=>0,"patched"=>[],"already"=>[],"failed"=>[]];
$files = array_filter(array_map("trim", explode("\n",
shell_exec("grep -rln 'await res.json()\|r\.json()' /var/www/html/*.html 2>/dev/null") ?: "")));
foreach ($files as $fp) {
if (!file_exists($fp)) continue;
$results["scanned"]++;
$ct = file_get_contents($fp);
if (strpos($ct,"HTML_GUARD")!==false) { $results["already"][]=basename($fp); continue; }
$orig = $ct;
// Pattern A: "await X.json()" → text() + check
$ct = preg_replace(
'/(\s*)(var|let|const)\s+(\w+)\s*=\s*await\s+(\w+)\.json\(\)\s*;/',
'$1/* HTML_GUARD_V2_BATCH */ $2 _t_$3=await $4.text(); $2 $3; {var _q=(_t_$3||\'\').trim();if(_q.startsWith(\'<!DOCTYPE\')||_q.startsWith(\'<html\')){$3={error:\'[HTTP \'+($4.status||\'?\')+\'] Backend indisponible\',isHtmlError:true};}else{try{$3=JSON.parse(_q)}catch(e){$3={error:\'[JSON] \'+e.message}}}}',
$ct);
// Pattern B: ".then(r=>r.json())"
$ct = preg_replace(
'/\.then\(\s*(\w+)\s*=>\s*\1\.json\(\)\s*\)/',
'.then($1=>$1.text().then(t=>{var q=(t||\'\').trim();if(q.startsWith(\'<!DOCTYPE\')||q.startsWith(\'<html\')){return{error:\'[HTTP \'+$1.status+\']\',isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:\'JSON \'+e.message}}}))',
$ct);
if ($ct !== $orig) {
$r = function_exists("opus_write_safe")
? opus_write_safe($fp, $ct, "HTML_GUARD_V2_BATCH")
: ["ok"=>(@file_put_contents($fp,$ct)!==false)];
if ($r["ok"]??false) { $results["patched"][]=basename($fp); }
else { $results["failed"][]=basename($fp); }
}
}
$results["summary"]=["scanned"=>$results["scanned"],"patched"=>count($results["patched"]),"already"=>count($results["already"])];
break;
default:
$results=["actions"=>["test_providers","webchat","nonreg","reconcile","git_push","ethica","docker_list","git_log","disk","ports","crons","services","playwright_scan","paperclip","slack"]];
}

File diff suppressed because it is too large Load Diff

View File

@@ -114,7 +114,7 @@ async function load() {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({message:'verifyfp'})
});
const j = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_j=await r.text(); const j; {var _q=(_t_j||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){j={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{j=JSON.parse(_q)}catch(e){j={error:'[JSON] '+e.message}}}}
if (j.engine === 'NL-Priority' || j.tool === 'fast-path') {
document.getElementById('awBadge').textContent = 'OPERATIONAL';
document.getElementById('awDetail').textContent = 'pipeline v6 priority JSON live';
@@ -127,7 +127,7 @@ async function load() {
// Sovereign cascade
try {
const r = await fetch('/api/sovereign/v1/models');
const j = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_j=await r.text(); const j; {var _q=(_t_j||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){j={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{j=JSON.parse(_q)}catch(e){j={error:'[JSON] '+e.message}}}}
const n = (j.data || []).length;
document.getElementById('sovBadge').textContent = n + ' providers';
document.getElementById('sovDetail').textContent = 'cascade :4000/v1 OK';
@@ -140,7 +140,7 @@ async function load() {
// NonReg
try {
const r = await fetch('/api/nonreg-latest.json');
const j = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_j=await r.text(); const j; {var _q=(_t_j||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){j={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{j=JSON.parse(_q)}catch(e){j={error:'[JSON] '+e.message}}}}
document.getElementById('nrBadge').textContent = j.score + '%';
document.getElementById('nrBadge').className = 'badge ' + (j.fail === 0 ? 'ok' : 'warn');
document.getElementById('nrDetail').textContent = j.pass + '/' + j.total + ' &mdash; ts ' + j.ts;
@@ -157,7 +157,7 @@ async function load() {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({message:'listprio'})
});
const j2 = await r2.json();
/* HTML_GUARD_V2_BATCH */ const _t_j2=await r2.text(); const j2; {var _q=(_t_j2||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){j2={error:'[HTTP '+(r2.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{j2=JSON.parse(_q)}catch(e){j2={error:'[JSON] '+e.message}}}}
if (j2.content) {
const list = JSON.parse(j2.content);
document.getElementById('prioBadge').textContent = list.length + ' intents';

View File

@@ -419,7 +419,7 @@ async function init() {
async function loadStats() {
try {
const r = await fetch(API_VAULT + '?action=stats');
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
document.getElementById('sNotes').textContent = d.files || d.notes || 0;
document.getElementById('hFiles').textContent = d.files || 0;
document.getElementById('sDirs').textContent = (d.dirs||[]).length;
@@ -440,14 +440,14 @@ async function loadStats() {
async function loadDirs() {
try {
const r = await fetch(API_VAULT + '?action=list');
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
const sb = document.getElementById('dirList');
const icons = {doctrines:'📜',tools:'🔧',sessions:'📅',decisions:'⚖️',ethica:'💊',infra:'🖥️',kb:'📚',arena:'⚡',daily:'📊'};
sb.innerHTML = `<div class="dir-item ${!currentDir?'active':''}" onclick="loadDir('')"><span class="icon">🏠</span> All<span class="count">${d.count}</span></div>`;
(d.files||[]).filter(f=>f.type==='dir').forEach(f => {
sb.innerHTML += `<div class="dir-item ${currentDir===f.name?'active':''}" onclick="loadDir('${f.name}')"><span class="icon">${icons[f.name]||'📁'}</span> ${f.name}<span class="count" id="dc_${f.name}">…</span></div>`;
// Load file count
fetch(API_VAULT+'?action=list&dir='+f.name).then(r=>r.json()).then(dd=>{
fetch(API_VAULT+'?action=list&dir='+f.name).then(r=>r.text().then(t=>{var q=(t||'').trim();if(q.startsWith('<!DOCTYPE')||q.startsWith('<html')){return{error:'[HTTP '+r.status+']',isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:'JSON '+e.message}}})).then(dd=>{
const el = document.getElementById('dc_'+f.name);
if(el) el.textContent = dd.count;
});
@@ -459,7 +459,7 @@ async function loadDir(dir) {
currentDir = dir;
loadDirs();
const r = await fetch(API_VAULT + '?action=list&dir=' + dir);
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
const res = document.getElementById('results');
document.getElementById('noteView').style.display = 'none';
res.innerHTML = '';
@@ -486,7 +486,7 @@ async function doSearch() {
? API_SEMANTIC + '?q=' + encodeURIComponent(q)
: API_VAULT + '?action=search&q=' + encodeURIComponent(q);
const r = await fetch(url);
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
if (!d.results || d.results.length === 0) {
res.innerHTML = '<div style="text-align:center;color:var(--fg3);padding:20px">No results</div>';
return;
@@ -510,7 +510,7 @@ async function doSearch() {
async function loadNote(file) {
try {
const r = await fetch(API_VAULT + '?action=read&file=' + encodeURIComponent(file));
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
if (d.error) { alert(d.error); return; }
currentFile = file;
document.getElementById('results').innerHTML = '';
@@ -552,7 +552,7 @@ async function saveNote() {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: `action=write&file=${encodeURIComponent(currentFile)}&content=${encodeURIComponent(content)}`
});
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
if (d.ok) {
document.getElementById('noteContent').textContent = content;
document.getElementById('noteContent').style.display = 'block';
@@ -584,7 +584,7 @@ async function createNote() {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: `action=write&file=${encodeURIComponent(dir+'/'+name+'.md')}&content=${encodeURIComponent(content)}`
});
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
if (d.ok) {
document.getElementById('modalBg').classList.remove('show');
document.getElementById('newFilename').value = '';
@@ -607,7 +607,7 @@ async function reEmbed() {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=exec_s204&cmd=timeout+30+python3+/opt/weval-l99/tools/vault-embed.py+2>&1'
});
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
btn.textContent = '🧠 Re-Embed ✅';
setTimeout(()=>{ btn.textContent='🧠 Re-Embed'; btn.disabled=false; }, 3000);
} catch(e) { btn.textContent = '🧠 Error'; btn.disabled = false; }
@@ -624,7 +624,7 @@ async function masterCmd(msg) {
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: msg})
});
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
res.innerHTML = `<div class="result-item"><div class="file">Master Response (${d.source||'?'})</div><pre class="snippet" style="white-space:pre-wrap;max-height:400px;overflow:auto">${typeof d.content==='string'?d.content:JSON.stringify(d.content,null,2)}</pre></div>`;
} catch(e) {
res.innerHTML = `<div style="color:var(--red);padding:20px">Master timeout</div>`;
@@ -695,13 +695,13 @@ async function buildHomepage() {
try {
const r = await fetch(API_VAULT + '?action=list');
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
const dirs = (d.files||[]).filter(f=>f.type==='dir');
for (const dir of dirs) {
try {
const r2 = await fetch(API_VAULT + '?action=list&dir=' + dir.name);
const d2 = await r2.json();
/* HTML_GUARD_V2_BATCH */ const _t_d2=await r2.text(); const d2; {var _q=(_t_d2||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d2={error:'[HTTP '+(r2.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d2=JSON.parse(_q)}catch(e){d2={error:'[JSON] '+e.message}}}}
const files = (d2.files||[]).filter(f=>f.type==='file'&&f.name.endsWith('.md'));
if (!files.length) continue;
@@ -762,7 +762,7 @@ async function syncPromptToMaster() {
try {
// Read the current system prompt
const r = await fetch(API_VAULT + '?action=read&file=doctrines/000-system-prompt.md');
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
const content = d.content || '';
const tokens = Math.ceil(content.length / 4);
@@ -794,7 +794,7 @@ function viewDigest() {
async function loadPromptPreviewEnhanced() {
try {
const r = await fetch(API_VAULT + '?action=read&file=doctrines/000-system-prompt.md');
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
const content = d.content || '';
const tokens = Math.ceil(content.length / 4);
const el = document.getElementById('promptPreview');
@@ -826,7 +826,7 @@ saveNote = async function() {
async function loadPromptPreview() {
try {
const r = await fetch('/api/wevia-prompt.php?format=json');
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
const el = document.getElementById('promptPreview');
if (el) {
const lines = d.prompt.split('\n').filter(l=>l.trim()).slice(0,5);
@@ -907,7 +907,7 @@ async function moveNote() {
// Read content, write to new path, delete old
try {
const r = await fetch(API_VAULT + '?action=read&file=' + encodeURIComponent(currentFile));
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
const content = d.content;
const newPath = newDir + '/' + name;
@@ -944,7 +944,7 @@ async function renameNote() {
try {
const r = await fetch(API_VAULT + '?action=read&file=' + encodeURIComponent(currentFile));
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
await fetch(API_VAULT, {
method: 'POST',
@@ -1019,7 +1019,7 @@ async function duplicateNote() {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=write&file=' + encodeURIComponent(newName) + '&content=' + encodeURIComponent(content)
});
const d = await r.json();
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); const d; {var _q=(_t_d||'').trim();if(_q.startsWith('<!DOCTYPE')||_q.startsWith('<html')){d={error:'[HTTP '+(r.status||'?')+'] Backend indisponible',isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:'[JSON] '+e.message}}}}
if (d.ok) { toast('Duplique: ' + newName, 'ok'); loadStats(); loadDir(currentDir||''); }
} catch(e) { toast(e.message, 'err'); }
}

View File

@@ -71,6 +71,48 @@ html,body{height:100%;overflow:hidden;font-family:'Outfit',system-ui;background:
.s4-bt{display:inline-flex;align-items:center;gap:6px;background:linear-gradient(135deg,var(--ac),var(--cy));color:#fff;border:none;border-radius:100px;padding:9px 26px;font-family:'Outfit';font-size:12px;font-weight:700;cursor:pointer;transition:.3s;box-shadow:0 6px 22px rgba(79,125,249,.3);z-index:1;text-decoration:none;animation:glw 3s infinite}
.s4-bt:hover{transform:translateY(-2px)}.s4-ft{margin-top:8px;font-size:.42rem;color:var(--t3);z-index:1}
</style>
<style id="video-export-css">
/* Video export mode: fit 1080x1080 carré LinkedIn */
body.video-export { overflow: hidden !important; }
body.video-export .dk, body.video-export html, body.video-export body { height: 1080px !important; width: 1080px !important; }
body.video-export .sl { display: flex !important; flex-direction: column !important; justify-content: center !important; align-items: center !important; padding: 60px 40px !important; height: 1080px !important; }
body.video-export .s1 { text-align: center; }
body.video-export .s1-hd, body.video-export .s3-hu { position: absolute !important; top: 20px !important; }
body.video-export .nv { bottom: 20px !important; }
/* hide parasites in video mode */
body.video-export #carto-banner,
body.video-export .carto-banner,
body.video-export [class*="cartographie"],
body.video-export [id*="logout"],
body.video-export .logout,
body.video-export [class*="Logout"],
body.video-export button[onclick*="logout"] { display: none !important; visibility: hidden !important; }
body.video-export canvas#cv { max-height: 820px !important; }
body.video-export .s2-sk { max-width: 900px !important; width: 100% !important; }
body.video-export .s4-gr { width: 100% !important; }
</style>
<script id="video-export-js">
(function(){
// Activate video mode if ?video=1 in URL
if (location.search.indexOf('video=1') >= 0) {
document.documentElement.classList.add('video-export');
document.body && document.body.classList.add('video-export');
document.addEventListener('DOMContentLoaded', function(){
document.body.classList.add('video-export');
// Hide any absolute-positioned topright buttons (Logout etc)
document.querySelectorAll('button, a').forEach(function(el){
var t = (el.textContent||'').trim().toLowerCase();
if (t === 'logout' || t === 'déconnexion') el.style.display = 'none';
});
// Hide banner anchored bottom-right that looks like carto-live
document.querySelectorAll('div').forEach(function(el){
var t = (el.textContent||'').toLowerCase();
if (t.indexOf('cartographie live') >= 0 || t.indexOf('carto live') >= 0) el.style.display = 'none';
});
});
}
})();
</script>
</head>
<body>
<div class="dk">
@@ -91,7 +133,7 @@ html,body{height:100%;overflow:hidden;font-family:'Outfit',system-ui;background:
<div class="sl s2" data-i="1">
<h2 class="s2-h">Architecture SOA - 6 Couches</h2>
<p class="s2-p">Chaque couche pilotee par des agents specialises</p>
<div class="s2-sk"><div class="s2-r gl" style="border-left:3px solid var(--cy)"><div class="s2-i">&#128421;</div><div><div class="s2-nm">Presentation</div><div class="s2-ds">Dashboards - Portails - Chat</div></div><div class="s2-k" style="color:var(--cy)">12 apps</div></div><div class="s2-r gl" style="border-left:3px solid var(--or)"><div class="s2-i">&#127919;</div><div><div class="s2-nm">Orchestration</div><div class="s2-ds">WEVIA Master - BPMN - 31 Intents</div></div><div class="s2-k" style="color:var(--or)">17 flux</div></div><div class="s2-r gl" style="border-left:3px solid var(--gr)"><div class="s2-i">&#129302;</div><div><div class="s2-nm">Agents IA</div><div class="s2-ds">Finance - RH - Marketing - Commerce - Supply - Prod</div></div><div class="s2-k" style="color:var(--gr)">930</div></div><div class="s2-r gl" style="border-left:3px solid var(--ac)"><div class="s2-i">&#129504;</div><div><div class="s2-nm">Intelligence</div><div class="s2-ds">LLM Souverain 0EUR - RAG - KB 2484 skills</div></div><div class="s2-k" style="color:var(--ac)">12 LLMs</div></div><div class="s2-r gl" style="border-left:3px solid var(--t2)"><div class="s2-i">&#128268;</div><div><div class="s2-nm">Data et Integration</div><div class="s2-ds">API Gateway - ETL - 141 661 HCPs Ethica</div></div><div class="s2-k" style="color:var(--t2)">412 Tools</div></div><div class="s2-r gl" style="border-left:3px solid var(--pu)"><div class="s2-i">&#128737;</div><div><div class="s2-nm">Gouvernance</div><div class="s2-ds">Securite - Compliance - NonReg 153/153</div></div><div class="s2-k" style="color:var(--pu)">100%</div></div></div>
<div class="s2-sk"><div class="s2-r gl" style="border-left:3px solid var(--cy)"><div class="s2-i">&#128421;</div><div><div class="s2-nm">Presentation</div><div class="s2-ds">Dashboards - Portails - Chat</div></div><div class="s2-k" style="color:var(--cy)">12 apps</div></div><div class="s2-r gl" style="border-left:3px solid var(--or)"><div class="s2-i">&#127919;</div><div><div class="s2-nm">Orchestration</div><div class="s2-ds">WEVIA Master - BPMN - 31 Intents</div></div><div class="s2-k" style="color:var(--or)">28 intents</div></div><div class="s2-r gl" style="border-left:3px solid var(--gr)"><div class="s2-i">&#129302;</div><div><div class="s2-nm">Agents IA</div><div class="s2-ds">Finance - RH - Marketing - Commerce - Supply - Prod</div></div><div class="s2-k" style="color:var(--gr)">930</div></div><div class="s2-r gl" style="border-left:3px solid var(--ac)"><div class="s2-i">&#129504;</div><div><div class="s2-nm">Intelligence</div><div class="s2-ds">LLM Souverain 0EUR - RAG - KB 2484 skills</div></div><div class="s2-k" style="color:var(--ac)">12 LLMs</div></div><div class="s2-r gl" style="border-left:3px solid var(--t2)"><div class="s2-i">&#128268;</div><div><div class="s2-nm">Data et Integration</div><div class="s2-ds">API Gateway - ETL - 141 661 HCPs Ethica</div></div><div class="s2-k" style="color:var(--t2)">412 Tools</div></div><div class="s2-r gl" style="border-left:3px solid var(--pu)"><div class="s2-i">&#128737;</div><div><div class="s2-nm">Gouvernance</div><div class="s2-ds">Securite - Compliance - NonReg 153/153</div></div><div class="s2-k" style="color:var(--pu)">100%</div></div></div>
<div class="s2-gv">GOVERNANCE - SECURITY - COMPLIANCE</div>
</div>
<!-- S3 -->
@@ -152,7 +194,7 @@ document.getElementById('hA').textContent=A.length;document.getElementById('hD')
</div>
<script>
(function(){
fetch('/api/screens-health.php?_='+Date.now(),{cache:'no-store'}).then(r=>r.json()).then(d=>{
fetch('/api/screens-health.php?_='+Date.now(),{cache:'no-store'}).then(r=>r.text().then(t=>{var q=(t||'').trim();if(q.startsWith('<!DOCTYPE')||q.startsWith('<html')){return{error:'[HTTP '+r.status+']',isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:'JSON '+e.message}}})).then(d=>{
const c=d.counts||{}; const up=c.UP||0; const slow=c.SLOW||0; const br=c.BROKEN||0;
const el=document.getElementById('carto-banner-count');
if(el) el.innerHTML=`<span style="color:#22c55e">${up} UP</span> / <span style="color:#f59e0b">${slow} Lent</span> / <span style="color:#ef4444">${br} 5xx</span>`;