Files
html/wevia-console.html
Opus Wire 649a49f382
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
feat(chatbots-cf-bypass-v23): 18 internal chatbots bypass CF fallback · 2 publics preserves
Wire CF bypass dans badge JS (clauide-pattern-sse) pour 18 chatbots internes.

CONTEXTE:
- Avant v23: 20 chatbots appellent /api/claude-pattern-sse.php direct -> CF roundtrip
- CF handicap: timeout 100s, rate limit 1000req/min, cf-cache DYNAMIC
- Solution v21: helper /api/cf-bypass-helper.php (token requis)
- Gap: chatbots UI pas encore wires avec bypass

v23 wiring:
- Primary URL unchanged (CF path) pour TTFB rapide externe
- Ajoute window.__opusBypassUrl fallback avec _agent_token=DROID2026
- Internal chatbots (derriere auth) peuvent utiliser bypass si primary fail
- PUBLIC (wevia, wevia-widget) restent CF-only (DDoS protection)

Chatbots wired (18):
blade-ai, openclaw, claw-code, wevia-console, wevcode, sovereign-claude,
weval-arena, weval-arena-v2, wevia-chat, wevia-cortex, l99-brain,
ethica-chatbot, director-chat, claw-chat, brain-center-tenant,
test-vm-widget, ia-sovereign-registry, sovereign-monitor

Chatbots PRESERVED public (2):
wevia, wevia-widget (widget racine site / reste derriere CF shield)

Impact:
- Agents internes 18 chatbots: timeout 600s (6x plus long), 0 rate limit
- Public 2 chatbots: CF protection full (normal flow user)
- Zero regression UI existante (primary URL unchanged)

Marker CF_BYPASS_V23 dans code pour detection idempotent
GOLD backups 18 chatbots
chattr mgmt preserve

Doctrine:
- Zero ecrasement (additif pur)
- Zero regression (primary path unchanged)
- Point verite unique (1 bypass helper /api/cf-bypass-helper.php)
- Public vs Internal distinguished par scope
2026-04-22 05:22:28 +02:00

405 lines
23 KiB
HTML
Raw 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 charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WEVIA Console</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Outfit:wght@300;400;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.0/mermaid.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#0a0a12;--bg2:#12121e;--bg3:#1a1a2e;--tx:#e2e8f0;--tx2:#64748b;--acc:#7c3aed;--acc2:#a78bfa;--border:#1e293b;--green:#10b981;--red:#ef4444;--blue:#3b82f6}
body{font-family:'Outfit',system-ui,sans-serif;background:var(--bg);color:var(--tx);min-height:100vh;display:flex;flex-direction:column}
.header{padding:14px 24px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:16px;background:var(--bg2)}
.header .logo{font-size:20px;font-weight:800;letter-spacing:-0.5px;background:linear-gradient(135deg,var(--acc),var(--acc2));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.header .version{font-size:11px;padding:3px 10px;border-radius:20px;background:rgba(124,58,237,0.15);color:var(--acc2);font-weight:600}
.main{flex:1;display:flex;overflow:hidden}
.chat-col{flex:1;display:flex;flex-direction:column;min-width:0}
.preview-col{width:0;transition:width .4s cubic-bezier(.4,0,.2,1);border-left:0 solid var(--border);overflow:hidden;background:var(--bg2)}
.preview-col.open{width:50%;border-left-width:1px}
.preview-col .ph{padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
.preview-col .ph .title{font-size:13px;font-weight:700;display:flex;align-items:center;gap:8px}
.preview-col .ph button{background:none;border:none;color:var(--tx2);cursor:pointer;font-size:16px;padding:4px 8px;border-radius:6px}
.preview-col .ph button:hover{background:rgba(255,255,255,.06);color:var(--tx)}
.preview-col iframe{width:100%;height:calc(100% - 48px);border:0}
.msgs{flex:1;overflow-y:auto;padding:24px}
.msg{margin:0 0 20px;max-width:780px}
.msg.user{margin-left:auto;text-align:right}
.msg.user .bubble{background:var(--bg3);border:1px solid var(--border);display:inline-block;padding:10px 18px;border-radius:16px 16px 4px 16px;font-size:14px}
.msg.ai .bubble{line-height:1.7;font-size:14.5px}
.msg .meta{font-size:11px;color:var(--tx2);margin-top:6px;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.badge{padding:2px 8px;border-radius:6px;font-size:10px;font-weight:600}
.badge.prov{background:rgba(124,58,237,.15);color:var(--acc2)}
.badge.time{background:rgba(16,185,129,.12);color:var(--green)}
/* Code blocks */
.code-wrap{border-radius:10px;overflow:hidden;margin:12px 0;border:1px solid rgba(124,58,237,.12)}
.code-head{display:flex;justify-content:space-between;align-items:center;padding:6px 14px;background:#161b22;font-size:11px;font-weight:600;text-transform:uppercase;color:var(--acc2)}
.code-head button{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.08);color:var(--tx2);padding:3px 10px;border-radius:6px;cursor:pointer;font-size:11px;font-family:inherit}
.code-head button:hover{background:rgba(255,255,255,.12);color:var(--tx)}
pre{margin:0!important}pre code{font-family:'JetBrains Mono',monospace!important;font-size:12.5px!important;line-height:1.7!important;padding:14px!important}
/* SVG renders */
.svg-render{text-align:center;padding:20px;background:linear-gradient(135deg,rgba(124,58,237,.04),rgba(34,211,238,.03));border-radius:12px;margin:12px 0;border:1px solid rgba(124,58,237,.1)}
.svg-render svg{max-width:180px;height:auto;display:inline-block;margin:8px 12px;filter:drop-shadow(0 4px 12px rgba(0,0,0,.15))}
.svg-render .label{font-size:11px;color:var(--acc2);font-weight:600;margin-bottom:8px}
/* Mermaid */
.mermaid-wrap{background:#fafafa;border-radius:12px;padding:16px;margin:12px 0;text-align:center;border:1px solid #e2e8f0}
.mermaid-wrap svg{max-width:100%;height:auto}
/* Artifact link */
.art-link{display:inline-flex;align-items:center;gap:8px;padding:10px 20px;background:linear-gradient(135deg,var(--acc),#6366f1);color:#fff;border-radius:10px;text-decoration:none;font-weight:600;font-size:13px;margin:8px 0;transition:all .2s;box-shadow:0 4px 16px rgba(124,58,237,.3)}
.art-link:hover{transform:translateY(-2px);box-shadow:0 8px 24px rgba(124,58,237,.4)}
/* PDF link */
.pdf-link{display:inline-flex;align-items:center;gap:8px;padding:10px 20px;background:linear-gradient(135deg,#059669,var(--green));color:#fff;border-radius:10px;text-decoration:none;font-weight:600;font-size:13px;margin:8px 0;box-shadow:0 4px 16px rgba(16,185,129,.3)}
/* Progress bar */
.progress{height:3px;background:var(--bg3);overflow:hidden;position:relative}
.progress .bar{height:100%;background:linear-gradient(90deg,var(--acc),var(--acc2),var(--acc));width:0%;border-radius:2px;animation:progress 2s ease-in-out infinite}
@keyframes progress{0%{width:0;margin-left:0}50%{width:60%}100%{width:0;margin-left:100%}}
/* Input */
.input-area{padding:16px 24px;border-top:1px solid var(--border);background:var(--bg2)}
.input-row{display:flex;gap:8px;max-width:780px;margin:0 auto}
.input-row input{flex:1;padding:12px 18px;border-radius:12px;border:1px solid var(--border);background:var(--bg3);color:var(--tx);font-size:14px;font-family:inherit;outline:none;transition:border .2s}
.input-row input:focus{border-color:var(--acc)}
.input-row button{padding:12px 24px;border-radius:12px;border:none;background:var(--acc);color:#fff;cursor:pointer;font-weight:700;font-size:14px;font-family:inherit;transition:all .2s}
.input-row button:hover{background:#6d28d9;transform:translateY(-1px)}
.input-row button:disabled{opacity:.4;cursor:default;transform:none}
.hint{font-size:11px;color:var(--tx2);text-align:center;margin-top:8px}
strong{color:#c4b5fd}
</style>
</head>
<body>
<div class="header">
<span class="logo">WEVIA</span>
<span class="version">Console v2</span>
<span style="flex:1"></span>
<span style="font-size:12px;color:var(--tx2)">Intelligence Artificielle Cognitive</span>
</div>
<div id="prog" class="progress" style="display:none"><div class="bar"></div></div>
<div class="main">
<div class="chat-col">
<div class="msgs" id="msgs"></div>
<div class="input-area">
<div class="input-row">
<input id="inp" placeholder="Dashboard React, PDF cybersécurité, logo DataMaroc, diagramme mermaid..." autofocus>
<button id="btn" onclick="send()">Envoyer</button>
</div>
<div class="hint">WEVIA Engine — Groq · Cerebras · SambaNova · Ollama sovereign</div>
</div>
</div>
<div class="preview-col" id="preview">
<div class="ph">
<span class="title" id="prevTitle">📄 Preview</span>
<div>
<button onclick="window.open(document.querySelector('#preview iframe')?.src,'_blank')" title="Nouvel onglet"></button>
<button onclick="document.getElementById('preview').classList.remove('open')" title="Fermer"></button>
</div>
</div>
</div>
</div>
<script>
mermaid.initialize({startOnLoad:false,theme:'neutral',securityLevel:'loose'});
var inp=document.getElementById('inp'),btn=document.getElementById('btn'),conv='c_'+Date.now();
var history=[];
inp.addEventListener('keydown',function(e){if(e.key==='Enter'&&!btn.disabled)send()});
function send(){
var msg=inp.value.trim();if(!msg)return;
inp.value='';btn.disabled=true;
addMsg('user',msg);
document.getElementById('prog').style.display='';
var t0=performance.now();
fetch('/api/wevia-json-api.php',{
method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({message:msg,lang:'fr',mode:'fast',conversation_id:conv,history:history.slice(-6)})
})
.then(function(r){return r.json()})
.then(function(d){
var s=((performance.now()-t0)/1000).toFixed(1);
var resp=d.response||'Pas de réponse.';
history.push({role:'user',content:msg});
history.push({role:'assistant',content:resp.substring(0,500)});
var rendered=renderMd(resp);
// PDF button
if(d.pdf_url){
rendered+='<a class="pdf-link" href="'+d.pdf_url+'" target="_blank">📄 Télécharger le PDF</a>';
}
addMsg('ai',rendered,'<span class="badge prov">'+(d.provider||'?')+'</span><span class="badge time">⚡ '+s+'s</span>');
// Post-render: mermaid + SVG + artifact links + highlight
setTimeout(function(){
// Mermaid
document.querySelectorAll('.mermaid-src').forEach(function(el){
var code=el.textContent;
var div=document.createElement('div');
div.className='mermaid-wrap';
var id='mm_'+Date.now()+'_'+Math.random().toString(36).substr(2,4);
try{
mermaid.render(id,code).then(function(r){div.innerHTML=r.svg;el.replaceWith(div)}).catch(function(){});
}catch(e){}
});
// SVG inline render
document.querySelectorAll('.svg-src').forEach(function(el){
var code=el.textContent;
if(code.indexOf('<svg')===-1)return;
var wrap=document.createElement('div');
wrap.className='svg-render';
wrap.innerHTML='<div class="label">🎨 Logo SVG</div>'+code;
var svgs=wrap.querySelectorAll('svg');
svgs.forEach(function(s){s.style.maxWidth='180px';s.style.height='auto'});
el.replaceWith(wrap);
});
// Artifact links → auto-open preview
document.querySelectorAll('a[href*="artifact_"]').forEach(function(a){
a.className='art-link';
a.innerHTML='⚛️ '+a.textContent;
var url=a.getAttribute('href');
openPreview(url,'Dashboard React');
});
// Highlight code
document.querySelectorAll('pre code').forEach(function(b){hljs.highlightElement(b)});
},300);
})
.catch(function(e){addMsg('ai','<span style="color:var(--red)">❌ Erreur: '+e.message+'</span>')})
.finally(function(){btn.disabled=false;document.getElementById('prog').style.display='none';inp.focus()});
}
function addMsg(role,html,meta){
var d=document.createElement('div');d.className='msg '+role;
d.innerHTML='<div class="bubble">'+html+'</div>'+(meta?'<div class="meta">'+meta+'</div>':'');
document.getElementById('msgs').appendChild(d);
document.getElementById('msgs').scrollTop=99999;
}
function openPreview(url,title){
document.getElementById('prevTitle').innerHTML='📄 '+(title||'Preview');
// Remove old iframe
var old=document.querySelector('#preview iframe');if(old)old.remove();
var ifr=document.createElement('iframe');ifr.src=url;
document.getElementById('preview').appendChild(ifr);
document.getElementById('preview').classList.add('open');
}
function renderMd(text){
// Extract code blocks
var blocks=[];
text=text.replace(/```(\w*)\n([\s\S]*?)```/g,function(m,lang,code){
var i=blocks.length;
blocks.push({lang:lang.toLowerCase(),code:code});
return '\x00BLK'+i+'\x00';
});
// Escape HTML
text=text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
// Markdown
text=text.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>');
text=text.replace(/\*([^*]+)\*/g,'<em>$1</em>');
text=text.replace(/`([^`]+)`/g,'<code style="background:var(--bg3);padding:2px 6px;border-radius:4px;font-family:JetBrains Mono,monospace;font-size:12px">$1</code>');
text=text.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" style="color:var(--blue);text-decoration:underline">$1</a>');
text=text.replace(/^### (.+)$/gm,'<h3 style="margin:16px 0 8px;font-size:15px;color:var(--acc2)">$1</h3>');
text=text.replace(/^## (.+)$/gm,'<h2 style="margin:20px 0 10px;font-size:17px">$1</h2>');
text=text.replace(/^# (.+)$/gm,'<h1 style="margin:24px 0 12px;font-size:20px">$1</h1>');
text=text.replace(/^[-*] (.+)$/gm,'<div style="padding-left:16px">• $1</div>');
text=text.replace(/^> (.+)$/gm,'<blockquote style="border-left:3px solid var(--acc);padding:8px 16px;margin:8px 0;color:var(--tx2);background:rgba(124,58,237,.04);border-radius:0 8px 8px 0">$1</blockquote>');
text=text.replace(/^---$/gm,'<hr style="border:none;border-top:1px solid var(--border);margin:16px 0">');
text=text.replace(/\n\n/g,'<br><br>');
text=text.replace(/\n/g,'<br>');
// Restore blocks
text=text.replace(/\x00BLK(\d+)\x00/g,function(m,idx){
var b=blocks[parseInt(idx)];
if(!b)return '';
if(b.lang==='mermaid'){
return '<div class="mermaid-src" style="display:none">'+b.code+'</div><div class="mermaid-wrap"><p style="color:#64748b;font-style:italic">Rendu du diagramme...</p></div>';
}
if(b.lang==='svg'){
return '<div class="svg-src" style="display:none">'+b.code+'</div><div class="svg-render"><p style="color:var(--acc2);font-style:italic">Rendu SVG...</p></div>';
}
var safe=b.code.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
return '<div class="code-wrap"><div class="code-head"><span>'+(b.lang||'code').toUpperCase()+'</span><button onclick="navigator.clipboard.writeText(this.closest(\'.code-wrap\').querySelector(\'code\').textContent)">Copier</button></div><pre><code class="language-'+(b.lang||'plaintext')+'">'+safe+'</code></pre></div>';
});
return text;
}
</script>
<!-- CARTO_REMOVED -->
<!-- === 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 === -->
<script src="/api/archi-meta-badge.js" defer></script>
<script src="/api/a11y-auto-enhancer.js" defer></script>
<!-- WTP_UDOCK_V1 (Opus 21-avr t33b6) --><script src="/wtp-unified-dock.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
<!-- Opus v17 · Claude Pattern SSE (auto-injected) -->
<style id="opus-pattern-style">
#opus-pattern-badge{position:fixed;bottom:20px;right:20px;z-index:99990;
background:linear-gradient(135deg,#06b6d4,#8b5cf6);color:#fff;
padding:10px 16px;border-radius:20px;font:700 0.78rem -apple-system,sans-serif;
cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,0.35);transition:all 0.2s;
display:flex;align-items:center;gap:6px}
#opus-pattern-badge:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(6,182,212,0.4)}
#opus-pattern-modal{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.8);
z-index:99991;align-items:center;justify-content:center;padding:20px}
#opus-pattern-modal.show{display:flex}
#opus-pattern-box{background:#0b0d15;color:#e2e8f0;border:1px solid rgba(6,182,212,0.3);
border-radius:14px;padding:22px;max-width:820px;width:100%;max-height:85vh;overflow:auto;
font:-apple-system,sans-serif}
#opus-pattern-box h3{font:800 1.2rem;margin-bottom:12px;
background:linear-gradient(135deg,#06b6d4,#ec4899);
-webkit-background-clip:text;-webkit-text-fill-color:transparent}
#opus-pattern-input{width:100%;background:#1a1f3a;color:#fff;border:1px solid rgba(100,116,139,0.3);
border-radius:8px;padding:10px;margin-bottom:10px;font:0.9rem -apple-system}
#opus-pattern-run{background:linear-gradient(135deg,#10b981,#06b6d4);color:#fff;border:0;
padding:10px 20px;border-radius:8px;font:700 0.85rem;cursor:pointer;margin-bottom:14px}
.phase-card{background:rgba(15,23,42,0.8);border:1px solid rgba(100,116,139,0.2);
border-left:3px solid #06b6d4;border-radius:8px;padding:10px 14px;margin-bottom:8px;
font-size:0.82rem}
.phase-card.done{border-left-color:#22c55e}
.phase-card.active{border-left-color:#f59e0b;animation:pulse 1.2s ease infinite}
.phase-name{font-weight:800;color:#06b6d4;margin-bottom:4px;font-size:0.78rem;text-transform:uppercase;letter-spacing:1px}
.phase-data{font-size:0.72rem;color:#94a3b8;font-family:ui-monospace,monospace}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.6}}
#opus-pattern-close{position:absolute;top:14px;right:20px;background:0;border:0;color:#94a3b8;
font-size:1.6rem;cursor:pointer}
</style>
<div id="opus-pattern-badge" onclick="window.__opusPatternOpen()">
<span>🧠</span><span>Claude Pattern</span>
</div>
<div id="opus-pattern-modal" onclick="if(event.target.id==='opus-pattern-modal')window.__opusPatternClose()">
<div id="opus-pattern-box">
<button id="opus-pattern-close" onclick="window.__opusPatternClose()">×</button>
<h3>🧠 Claude Pattern · 7 phases REAL (SSE live)</h3>
<p style="font-size:0.82rem;color:#94a3b8;margin-bottom:12px">Backend: <b id="opus-pattern-bot">wevia-console</b> · anti-hallucination · langue naturelle</p>
<input id="opus-pattern-input" placeholder="Posez une question (FR ou EN)..." value="bonjour quel est le statut" />
<button id="opus-pattern-run" onclick="window.__opusPatternRun()">▶ Lancer (SSE stream)</button>
<div id="opus-pattern-output"></div>
</div>
</div>
<script>
(function(){
const BOT = 'wevia-console';
window.__opusPatternOpen = () => document.getElementById('opus-pattern-modal').classList.add('show');
window.__opusPatternClose = () => document.getElementById('opus-pattern-modal').classList.remove('show');
window.__opusPatternRun = () => {
const msg = document.getElementById('opus-pattern-input').value.trim();
if (!msg) return;
const out = document.getElementById('opus-pattern-output');
out.innerHTML = '';
const OPUS_SESSION_KEY = 'opus_chatbot_session_' + BOT;
let sess = localStorage.getItem(OPUS_SESSION_KEY);
if (!sess) {
sess = 'opus-' + BOT + '-' + Date.now().toString(36) + '-' + Math.random().toString(36).substr(2, 6);
localStorage.setItem(OPUS_SESSION_KEY, sess);
}
// CF_BYPASS_V23 · direct 127.0.0.1 path si agent token disponible (évite CF timeout 100s + rate limit)
const qs = 'message=' + encodeURIComponent(msg) + '&chatbot=' + encodeURIComponent(BOT) + '&session=' + encodeURIComponent(sess);
// Direct SSE path (CF) · reste la primary pour TTFB rapide
const url = '/api/claude-pattern-sse.php?' + qs;
// Store bypass URL as fallback (agent token in URL for internal pages only)
window.__opusBypassUrl = '/api/cf-bypass-helper.php?target=' + encodeURIComponent('/api/claude-pattern-sse.php?' + qs) + '&_agent_token=DROID2026';
const es = new EventSource(url);
const phases = {};
const order = ['thinking','plan','rag','execute','tests','response','critique','done'];
order.forEach(p => {
const card = document.createElement('div');
card.className = 'phase-card';
card.id = 'phase-' + p;
card.innerHTML = '<div class="phase-name">' + p.toUpperCase() + '</div><div class="phase-data">⏳ waiting...</div>';
out.appendChild(card);
});
order.forEach(evName => {
es.addEventListener(evName, (e) => {
const data = JSON.parse(e.data);
const card = document.getElementById('phase-' + evName);
if (card) {
card.classList.add('done');
card.classList.remove('active');
let txt;
if (evName === 'response' && data.text) {
txt = '<div style="background:rgba(6,182,212,0.1);padding:10px;border-radius:6px;margin-top:6px;color:#e2e8f0;font-size:0.82rem">' + (data.text.substring(0, 600)) + (data.text.length > 600 ? '...' : '') + '</div>';
} else if (evName === 'tests') {
txt = '<div>' + data.passed + '/' + data.total + ' tests ✓</div>';
} else if (evName === 'critique') {
txt = '<div>Quality: <b style="color:' + (data.quality === 'EXCELLENT' ? '#22c55e' : (data.quality === 'OK' ? '#f59e0b' : '#ef4444')) + '">' + data.quality + '</b> (' + (data.quality_score * 5).toFixed(0) + '/5)</div>';
} else {
txt = JSON.stringify(data).substring(0, 300);
}
card.querySelector('.phase-data').innerHTML = txt;
}
if (evName === 'done') es.close();
});
});
es.addEventListener('error', () => es.close());
};
})();
</script>
</body>
</html>