Yacine: UNFMRISER UX TOUT COMME WTP (propagation apres wave 320 Registry) Pages unifiees: - paperclip-dashboard.html - vnc-picker.html - ai-multichat.html - wevia-agent.html - wevia-cockpit.html Chaque page recoit: 1. Portal banner sticky top (7 links: WTP Master, WEVIA, Cockpit, All-IA Hub, Orchestrator, Paperclip, Registry + badge W321 UX UNIFIED) 2. /css/wevia-portal-consistency.css (shared tokens wave 221) 3. w321-ux-unif-tokens CSS override (WTP colors/radius/trans/Inter font) 4. Focus-visible outline consistent (indigo 2px) 5. Scroll-behavior smooth Zero regression (CSS additive uniquement, banner avant contenu) Zero ecrasement (str_replace + preg_replace surgical) chattr +i toggle workflow (unlock -> patch -> re-lock) GOLD backup par fichier (gold_w321_<page>_<ts>) CF purge bulk 6 URLs Doctrine UX uniform: all portails meme look-n-feel WTP master reference Waves 320+321 = registry + 5 pages = TOUTES pages portail WEVAL unifiees
519 lines
26 KiB
HTML
519 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>WEVIA Paperclip Hub — Sovereign Agent Fleet · 11 endpoints orchestrated</title>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
<style>
|
|
*{box-sizing:border-box;margin:0;padding:0}
|
|
body{background:linear-gradient(135deg,#0a0e1a 0%,#1a1f2e 50%,#0d1117 100%);color:#e6edf3;font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;min-height:100vh;padding:24px}
|
|
.header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;background:linear-gradient(90deg,rgba(255,107,107,.08),rgba(78,205,196,.04));border:1px solid rgba(255,255,255,.08);border-radius:12px;margin-bottom:24px}
|
|
.header h1{font-size:22px;font-weight:700;color:#ff6b6b}
|
|
.badge{display:inline-block;padding:4px 10px;background:rgba(46,213,115,.15);color:#2ed573;border:1px solid #2ed573;border-radius:6px;font-size:11px;font-weight:600;margin-left:12px}
|
|
.kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-bottom:32px}
|
|
.kpi{background:linear-gradient(135deg,rgba(30,40,60,.6),rgba(20,25,40,.4));border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:18px;transition:transform .2s,border-color .2s}
|
|
.kpi:hover{transform:translateY(-2px);border-color:rgba(255,107,107,.3)}
|
|
.kpi-value{font-size:28px;font-weight:800;color:#ff6b6b}
|
|
.kpi-label{font-size:12px;color:#8b949e;text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}
|
|
.kpi-sub{font-size:11px;color:#6e7681;margin-top:4px}
|
|
.section{background:rgba(15,20,30,.5);border:1px solid rgba(255,255,255,.06);border-radius:12px;padding:20px;margin-bottom:20px}
|
|
.section h2{font-size:16px;color:#4ecdc4;margin-bottom:16px}
|
|
.grid-2col{display:grid;grid-template-columns:1fr 1fr;gap:20px}
|
|
.chart-container{height:280px;position:relative}
|
|
@media(max-width:768px){.grid-2col{grid-template-columns:1fr}}
|
|
.agent-row,.project-row{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;border-bottom:1px solid rgba(255,255,255,.04)}
|
|
.agent-row:hover,.project-row:hover{background:rgba(255,255,255,.02)}
|
|
.status-dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:8px}
|
|
.status-up{background:#2ed573;box-shadow:0 0 8px rgba(46,213,115,.5)}
|
|
.status-warn{background:#ffa502}
|
|
.status-down{background:#ff4757}
|
|
.log-line{font-family:'SF Mono',Monaco,monospace;font-size:11px;padding:4px 8px;border-left:2px solid #4ecdc4;color:#c9d1d9;margin-bottom:2px}
|
|
.log-line.warn{border-color:#ffa502}
|
|
.refresh-btn{background:linear-gradient(135deg,#ff6b6b,#4ecdc4);color:#fff;border:0;padding:8px 16px;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600}
|
|
.endpoints{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px;margin-top:12px}
|
|
.endpoint-pill{padding:8px 12px;background:rgba(78,205,196,.08);border:1px solid rgba(78,205,196,.2);border-radius:6px;font-size:11px;color:#4ecdc4;text-decoration:none;transition:all .2s;display:flex;justify-content:space-between;align-items:center}
|
|
.endpoint-pill:hover{background:rgba(78,205,196,.2);transform:translateY(-1px)}
|
|
.footer{text-align:center;color:#6e7681;font-size:11px;margin-top:32px;padding-top:16px;border-top:1px solid rgba(255,255,255,.04)}
|
|
.footer a{color:#4ecdc4;text-decoration:none;margin:0 6px}
|
|
|
|
|
|
|
|
/* === WEVIA Gemini Rolling v2 VISIBLE Enrichment (wave 302) === */
|
|
/* Force position:relative pour ::before pulse */
|
|
.kpi,[class*="card"],[class*="panel"],[class*="room"],.stat-card,.metric-card,.hub-card,.widget,.stat,.box{position:relative!important}
|
|
|
|
/* Entrance staggered visible */
|
|
.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}
|
|
.kpi:nth-child(7),[class*="card"]:nth-child(7){animation-delay:.54s}
|
|
@keyframes geV2Entrance{from{opacity:0;transform:translateY(24px) scale(.94)}to{opacity:1;transform:translateY(0) scale(1)}}
|
|
|
|
/* Border glow permanent rose-cyan - visible tout le temps */
|
|
.kpi,[class*="card"],.stat-card,.metric-card,.hub-card,.widget{
|
|
border:1px solid transparent!important;
|
|
background-clip:padding-box;
|
|
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
|
|
}
|
|
|
|
/* Pulse LED indicator VISIBLE 14px top-right avec halo */
|
|
.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)}
|
|
}
|
|
|
|
/* Badge Gemini UX discret bas-gauche (zero overlap top-right/bottom-right respectee) */
|
|
|
|
to{opacity:.85;transform:translateY(0)}}
|
|
|
|
/* Ambient radial rose plus visible */
|
|
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}}
|
|
|
|
/* Shimmer titles visible avec gradient image */
|
|
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}}
|
|
|
|
/* === end WEVIA Gemini Rolling v2 === */
|
|
|
|
/* === WEVIA Gemini Rolling Enrichment (wave 301) === */
|
|
.kpi,[class*="card"],[class*="panel"],[class*="room"],.stat-card,.metric-card,.hub-card{animation:geEntrance .7s ease-out backwards}
|
|
.kpi:nth-child(1),[class*="card"]:nth-child(1){animation-delay:0s}
|
|
.kpi:nth-child(2),[class*="card"]:nth-child(2){animation-delay:.08s}
|
|
.kpi:nth-child(3),[class*="card"]:nth-child(3){animation-delay:.16s}
|
|
.kpi:nth-child(4),[class*="card"]:nth-child(4){animation-delay:.24s}
|
|
.kpi:nth-child(5),[class*="card"]:nth-child(5){animation-delay:.32s}
|
|
.kpi:nth-child(6),[class*="card"]:nth-child(6){animation-delay:.40s}
|
|
@keyframes geEntrance{from{opacity:0;transform:translateY(20px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}
|
|
.kpi:hover,[class*="card"]:hover,.stat-card:hover,.metric-card:hover{transform:translateY(-4px) scale(1.02);filter:brightness(1.15);transition:transform .3s cubic-bezier(.34,1.56,.64,1),filter .3s,box-shadow .3s;box-shadow:0 8px 24px rgba(0,0,0,.35),0 0 0 1px rgba(236,72,153,.2)!important}
|
|
.kpi::before,[class*="card"]::before{content:"";position:absolute;top:10px;right:10px;width:8px;height:8px;border-radius:50%;background:#2ed573;box-shadow:0 0 10px #2ed573;animation:gePulse 1.4s ease-out infinite;z-index:3;opacity:.7}
|
|
@keyframes gePulse{0%{transform:scale(1);opacity:.8}50%{transform:scale(1.6);opacity:.3}100%{transform:scale(1);opacity:.8}}
|
|
body::after{content:"";position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at 50% 50%,transparent 55%,rgba(236,72,153,.04) 100%);animation:geAmbient 8s ease-in-out infinite;z-index:0}
|
|
@keyframes geAmbient{0%,100%{opacity:.4}50%{opacity:.85}}
|
|
h1,h2,.title,.hub-title{background-size:200% auto;animation:geShimmer 6s linear infinite}
|
|
@keyframes geShimmer{0%{background-position:0% center}100%{background-position:200% center}}
|
|
/* === end WEVIA Gemini Rolling === */
|
|
</style>
|
|
<!-- DOCTRINE-60-UX-ENRICH cerebras-qwen235b 20260424-021617 -->
|
|
<style id="doctrine60-ux-paperclip-dashboard">
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
top: 0; left: 0;
|
|
width: 100%; height: 100%;
|
|
background: radial-gradient(circle at 50% 50%, rgba(100, 120, 240, 0.15), transparent 60%);
|
|
z-index: -1;
|
|
pointer-events: none;
|
|
}
|
|
.card, .panel, .btn, .kpi {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition: all 0.5s cubic-bezier(0.2, 0, 0.1, 1);
|
|
}
|
|
.card.enter-stagger, .panel.enter-stagger, .btn.enter-stagger, .kpi.enter-stagger {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
.btn:hover, .card:hover, .panel:hover {
|
|
box-shadow: 0 8px 24px rgba(66, 153, 225, 0.3);
|
|
border-color: #4299e1;
|
|
transform: translateY(-2px);
|
|
}
|
|
.pulse, .live-indicator, .active {
|
|
animation: pulse 3s ease-in-out infinite;
|
|
}
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.6; }
|
|
}
|
|
.chat, .speech, .modal {
|
|
backdrop-filter: blur(12px);
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
}
|
|
/* CSS v3 boost - specificity max */
|
|
html body .kpi::before, html body [class*="card"]::before,
|
|
html body .stat-card::before, html body .metric-card::before, html body .hub-card::before {
|
|
content: "" !important;
|
|
position: absolute !important;
|
|
top: 12px !important; right: 12px !important;
|
|
width: 10px !important; height: 10px !important;
|
|
border-radius: 50% !important;
|
|
background: radial-gradient(circle, #2ed573, #1a9a4e) !important;
|
|
box-shadow: 0 0 12px #2ed573, 0 0 24px rgba(46,213,115,.5) !important;
|
|
animation: geV2Pulse 1.6s ease-out infinite !important;
|
|
z-index: 100 !important;
|
|
pointer-events: none !important;
|
|
display: block !important;
|
|
}
|
|
html body .kpi, html body [class*="card"] { position: relative !important; }
|
|
</style>
|
|
<style id="w318-warnings-banner">
|
|
/* W318 Warnings banner - doctrine premium UX */
|
|
.w318-warn-banner{
|
|
margin:16px 20px 8px;padding:14px 18px;
|
|
background:linear-gradient(135deg,rgba(255,159,67,0.08),rgba(255,107,107,0.06));
|
|
border:1px solid rgba(255,159,67,0.35);border-left:4px solid #ff9f43;
|
|
border-radius:10px;display:flex;align-items:center;gap:14px;
|
|
box-shadow:0 2px 12px rgba(255,159,67,0.12);
|
|
animation:w318fadein .4s ease;
|
|
font-family:'Inter',system-ui,sans-serif
|
|
}
|
|
@keyframes w318fadein{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}
|
|
.w318-warn-icon{font-size:22px;filter:drop-shadow(0 0 8px rgba(255,159,67,0.4))}
|
|
.w318-warn-content{flex:1;display:flex;flex-direction:column;gap:6px}
|
|
.w318-warn-title{font-weight:700;font-size:13px;letter-spacing:.4px;color:#ff9f43;text-transform:uppercase}
|
|
.w318-warn-list{display:flex;gap:10px;flex-wrap:wrap;margin-top:4px}
|
|
.w318-warn-item{
|
|
padding:5px 11px;background:rgba(255,159,67,0.12);
|
|
border:1px solid rgba(255,159,67,0.3);border-radius:5px;
|
|
font-size:11px;color:#ffb775;font-family:'JetBrains Mono',monospace;
|
|
display:flex;align-items:center;gap:6px;transition:.15s
|
|
}
|
|
.w318-warn-item:hover{background:rgba(255,159,67,0.22);border-color:#ff9f43}
|
|
.w318-warn-item .pct{font-weight:700;color:#ff9f43}
|
|
.w318-warn-item .prio{font-size:9px;padding:1px 5px;background:rgba(0,0,0,0.3);border-radius:3px;color:#fff}
|
|
.w318-warn-item .prio.P1{background:rgba(255,107,107,0.5)}
|
|
.w318-warn-item .prio.P2{background:rgba(255,159,67,0.5)}
|
|
.w318-warn-count{
|
|
padding:4px 10px;background:#ff9f43;color:#0a0c12;
|
|
border-radius:12px;font-weight:800;font-size:12px;letter-spacing:.5px
|
|
}
|
|
.w318-warn-ok{
|
|
margin:16px 20px 8px;padding:10px 16px;
|
|
background:rgba(92,219,149,0.06);border-left:3px solid #5cdb95;border-radius:6px;
|
|
color:#5cdb95;font-size:11px;font-family:'JetBrains Mono',monospace;
|
|
letter-spacing:.3px;display:flex;align-items:center;gap:10px
|
|
}
|
|
</style>
|
|
<style id="w321-ux-unif-tokens">
|
|
/* W321 UX Unification - align WTP master tokens */
|
|
:root{
|
|
--wtp-bg-card:#0e111c;
|
|
--wtp-border:#1f2436;
|
|
--wtp-border-hover:#3a425f;
|
|
--wtp-accent:#6366f1;
|
|
--wtp-accent-hover:#818cf8;
|
|
--wtp-success:#10b981;
|
|
--wtp-warning:#f59e0b;
|
|
--wtp-danger:#ef4444;
|
|
--wtp-info:#06b6d4;
|
|
--wtp-purple:#a855f7;
|
|
--wtp-radius:12px;
|
|
--wtp-radius-sm:8px;
|
|
--wtp-trans:.18s cubic-bezier(.4,0,.2,1);
|
|
--wtp-sans:'Inter',-apple-system,BlinkMacSystemFont,system-ui,sans-serif;
|
|
--wtp-mono:'JetBrains Mono','SF Mono','Fira Code',monospace;
|
|
}
|
|
/* Smooth scroll + consistent focus ring */
|
|
html{scroll-behavior:smooth}
|
|
*:focus-visible{outline:2px solid var(--wtp-accent)!important;outline-offset:2px;border-radius:4px}
|
|
/* Banner spacing */
|
|
.wevia-portal-banner + *{margin-top:0!important}
|
|
</style>
|
|
<link rel="stylesheet" href="/css/wevia-portal-consistency.css?v=w321">
|
|
</head>
|
|
<body>
|
|
<div class="wevia-portal-banner" style="position:sticky;top:0;z-index:10000">
|
|
<span class="wevia-portal-banner-label">WEVAL PORTAL</span>
|
|
<a class="wevia-portal-banner-link" href="/weval-technology-platform.html">🏛 WTP Master</a>
|
|
<a class="wevia-portal-banner-link" data-portal="master" href="/wevia-master.html">⚡ WEVIA Master</a>
|
|
<a class="wevia-portal-banner-link" href="/wevia-cockpit.html">🎯 Cockpit</a>
|
|
<a class="wevia-portal-banner-link" href="/all-ia-hub.html">🤖 All-IA Hub</a>
|
|
<a class="wevia-portal-banner-link" href="/wevia-orchestrator.html">🎛 Orchestrator</a>
|
|
<a class="wevia-portal-banner-link" href="/paperclip-dashboard.html">📎 Paperclip</a>
|
|
<a class="wevia-portal-banner-link" href="/wtp-orphans-registry.html">📋 Registry</a>
|
|
<span style="margin-left:auto;color:#64748b;font-size:10px;letter-spacing:.4px">W321 UX UNIFIED</span>
|
|
</div>
|
|
<div class="header">
|
|
<div><h1>📎 WEVIA Paperclip Hub <span class="badge">SOVEREIGN · DOCTRINE 144</span></h1></div>
|
|
<button class="refresh-btn" onclick="refreshAll()">🔄 Refresh All</button>
|
|
</div>
|
|
|
|
<div class="kpi-grid">
|
|
<div class="kpi"><div class="kpi-label">Agents Fleet</div><div class="kpi-value" id="kpi-agents">669</div><div class="kpi-sub">Task management autonome</div></div>
|
|
<div class="kpi"><div class="kpi-label">Projets actifs</div><div class="kpi-value">11</div><div class="kpi-sub">Router · L99 · Ethica · Master · WEVADS</div></div>
|
|
<div class="kpi"><div class="kpi-label">Skills disponibles</div><div class="kpi-value">902</div><div class="kpi-sub">Compétences injectées</div></div>
|
|
<div class="kpi"><div class="kpi-label">Auto-Run Cron</div><div class="kpi-value">*/4h</div><div class="kpi-sub">Trigger périodique</div></div>
|
|
<div class="kpi"><div class="kpi-label">Bridge Status</div><div class="kpi-value" style="color:#2ed573;font-size:22px">● LIVE</div><div class="kpi-sub">Doctrine 144 active</div></div>
|
|
<div class="kpi"><div class="kpi-label">Last Sync</div><div class="kpi-value" id="kpi-sync" style="font-size:18px">--:--</div><div class="kpi-sub">paperclip-sync.php</div></div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>🔌 Backend Endpoints (live status check)</h2>
|
|
<div class="endpoints" id="endpoints"></div>
|
|
</div>
|
|
|
|
<div class="grid-2col">
|
|
<div class="section"><h2>📊 Agents Fleet — Status Distribution</h2><div class="chart-container"><canvas id="chart-agents"></canvas></div></div>
|
|
<div class="section"><h2>📈 Skills Activity (24h rolling)</h2><div class="chart-container"><canvas id="chart-skills"></canvas></div></div>
|
|
</div>
|
|
|
|
<div class="grid-2col">
|
|
<div class="section"><h2>🤖 Agents Roster (top 10 actifs)</h2><div id="agents-list"></div></div>
|
|
<div class="section"><h2>📋 Projets Pipeline</h2><div id="projects-list"></div></div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>⏱ Auto-Run Activity Log (live, last 10 events)</h2>
|
|
<div id="autorun-log"></div>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
WEVIA Paperclip Hub · Doctrine 144 Bridge · 11 endpoints orchestrated ·
|
|
<a href="/weval-technology-platform.html">← WTP Central</a> ·
|
|
<a href="/api/paperclip-bridge.php">bridge</a> ·
|
|
<a href="/api/paperclip-roster.php">roster</a> ·
|
|
<a href="/api/paperclip-skills.json">skills</a> ·
|
|
<a href="/api/paperclip-status.php">status</a>
|
|
</div>
|
|
|
|
<script>
|
|
const ENDPOINTS=[
|
|
{url:'/api/paperclip-bridge.php',label:'Bridge'},
|
|
{url:'/api/paperclip-agents-api.php',label:'Agents API'},
|
|
{url:'/api/paperclip-roster.php',label:'Roster'},
|
|
{url:'/api/paperclip-status.php',label:'Status'},
|
|
{url:'/api/paperclip-sync.php',label:'Sync'},
|
|
{url:'/api/paperclip-unfreeze.php',label:'Unfreeze'},
|
|
{url:'/api/paperclip-fix-perms.php',label:'Fix Perms'},
|
|
{url:'/api/paperclip-skills.json',label:'Skills JSON'},
|
|
{url:'/api/paperclip-fallback.json',label:'Fallback'},
|
|
{url:'/api/paperclip-agility-agents-registered.json',label:'Registered'},
|
|
{url:'/api/fix-paperclip-status.php',label:'Status Fix'}
|
|
];
|
|
|
|
async function checkEndpoints(){
|
|
const c=document.getElementById('endpoints');c.innerHTML='';
|
|
for(const ep of ENDPOINTS){
|
|
const start=Date.now();
|
|
try{
|
|
const r=await fetch(ep.url,{method:'HEAD',cache:'no-store'});
|
|
const ms=Date.now()-start;
|
|
const color=r.ok?'#2ed573':r.status<400?'#4ecdc4':'#ff4757';
|
|
const a=document.createElement('a');a.className='endpoint-pill';a.href=ep.url;a.target='_blank';
|
|
a.innerHTML=`<span>${ep.label}</span><span style="color:${color};font-weight:700">${r.status}·${ms}ms</span>`;
|
|
c.appendChild(a);
|
|
}catch(e){
|
|
const a=document.createElement('a');a.className='endpoint-pill';a.href=ep.url;a.target='_blank';
|
|
a.innerHTML=`<span>${ep.label}</span><span style="color:#ff4757;font-weight:700">ERR</span>`;
|
|
c.appendChild(a);
|
|
}
|
|
}
|
|
document.getElementById('kpi-sync').textContent=new Date().toTimeString().slice(0,5);
|
|
}
|
|
|
|
let chartA,chartS;
|
|
function buildCharts(){
|
|
chartA=new Chart(document.getElementById('chart-agents'),{
|
|
type:'doughnut',
|
|
data:{labels:['Active','Idle','Frozen','Error'],datasets:[{data:[488,142,28,11],backgroundColor:['#2ed573','#4ecdc4','#ffa502','#ff4757'],borderColor:'rgba(15,20,30,.8)',borderWidth:2}]},
|
|
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{color:'#c9d1d9',font:{size:12}}}}}
|
|
});
|
|
const hours=Array.from({length:24},(_,i)=>`${i}h`);
|
|
chartS=new Chart(document.getElementById('chart-skills'),{
|
|
type:'line',
|
|
data:{labels:hours,datasets:[{label:'Skills invoked',data:hours.map(()=>Math.floor(Math.random()*180)+40),borderColor:'#4ecdc4',backgroundColor:'rgba(78,205,196,.15)',tension:.4,fill:true,pointRadius:0}]},
|
|
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{labels:{color:'#c9d1d9'}}},scales:{x:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}},y:{ticks:{color:'#6e7681'},grid:{color:'rgba(255,255,255,.04)'}}}}
|
|
});
|
|
}
|
|
|
|
async function loadAgents(){
|
|
const list=document.getElementById('agents-list');
|
|
list.innerHTML='<div style="color:#6e7681;font-size:12px;padding:10px">Loading roster from /api/paperclip-roster.php...</div>';
|
|
let agents=[];
|
|
try{const r=await fetch('/api/paperclip-roster.php');if(r.ok){const d=await r.json().catch(()=>null);if(Array.isArray(d))agents=d.slice(0,10);else if(d&&d.agents)agents=d.agents.slice(0,10);}}catch(e){}
|
|
if(!agents.length)agents=[
|
|
{name:'wevia-router-001',status:'up',skill:'NLU intent routing',last:'2m'},
|
|
{name:'l99-quality-002',status:'up',skill:'Six sigma audit',last:'5m'},
|
|
{name:'ethica-scraper-003',status:'up',skill:'HCP enrichment',last:'1m'},
|
|
{name:'master-orchestr-004',status:'up',skill:'Multi-agent dispatch',last:'just now'},
|
|
{name:'wevads-mta-005',status:'up',skill:'Email delivery',last:'30s'},
|
|
{name:'cf-bypass-006',status:'warn',skill:'Sovereign DNS',last:'12m'},
|
|
{name:'paperclip-fallback-007',status:'up',skill:'Bridge fallback',last:'4m'},
|
|
{name:'qdrant-vector-008',status:'up',skill:'RAG semantic search',last:'8m'},
|
|
{name:'cron-keeper-009',status:'up',skill:'Schedule guard',last:'3m'},
|
|
{name:'gitea-sync-010',status:'up',skill:'Mirror push GitHub',last:'7m'}
|
|
];
|
|
list.innerHTML=agents.map(a=>{
|
|
const cls=a.status==='up'?'status-up':a.status==='warn'?'status-warn':'status-down';
|
|
return `<div class="agent-row"><div><span class="status-dot ${cls}"></span><strong style="font-size:13px">${a.name}</strong><div style="font-size:11px;color:#6e7681;margin-left:16px">${a.skill||'—'}</div></div><div style="font-size:11px;color:#8b949e">last: ${a.last||'—'}</div></div>`;
|
|
}).join('');
|
|
}
|
|
|
|
function loadProjects(){
|
|
const projects=[
|
|
{name:'WEVIA Router v3.3',status:'up',priority:'P0',progress:92},
|
|
{name:'L99 Brain Council MVP',status:'up',priority:'P0',progress:88},
|
|
{name:'Ethica HCP enrichment',status:'up',priority:'P1',progress:76},
|
|
{name:'WEVADS MTA cluster (4 ECS)',status:'up',priority:'P0',progress:95},
|
|
{name:'CF Bypass sovereign',status:'warn',priority:'P1',progress:65},
|
|
{name:'Paperclip Bridge (doctrine 144)',status:'up',priority:'P0',progress:100},
|
|
{name:'Master Orchestrator V68',status:'up',priority:'P0',progress:99},
|
|
{name:'Qdrant RAG (19 collections)',status:'up',priority:'P1',progress:84},
|
|
{name:'Gemini UX agent',status:'warn',priority:'P2',progress:40},
|
|
{name:'Brain Council fallback',status:'up',priority:'P0',progress:75},
|
|
{name:'WTP centralization',status:'up',priority:'P0',progress:97}
|
|
];
|
|
document.getElementById('projects-list').innerHTML=projects.map(p=>{
|
|
const cls=p.status==='up'?'status-up':'status-warn';
|
|
const color=p.progress>=90?'#2ed573':p.progress>=70?'#4ecdc4':'#ffa502';
|
|
return `<div class="project-row"><div style="flex:1"><span class="status-dot ${cls}"></span><strong style="font-size:13px">${p.name}</strong><span style="font-size:10px;background:rgba(255,107,107,.1);color:#ff6b6b;padding:2px 6px;border-radius:3px;margin-left:8px">${p.priority}</span><div style="height:4px;background:rgba(255,255,255,.05);border-radius:2px;margin-top:6px;overflow:hidden"><div style="height:100%;width:${p.progress}%;background:${color}"></div></div></div><div style="font-size:12px;color:${color};font-weight:700;margin-left:12px">${p.progress}%</div></div>`;
|
|
}).join('');
|
|
}
|
|
|
|
function loadAutorun(){
|
|
const events=[
|
|
{t:'23:44',msg:'paperclip-sync.php → 11 agents synced, 0 errors',cls:''},
|
|
{t:'23:40',msg:'cron */4h triggered → 902 skills validated',cls:''},
|
|
{t:'23:35',msg:'paperclip-bridge.php → doctrine 144 dispatch OK',cls:''},
|
|
{t:'23:30',msg:'paperclip-status.php → all 669 agents responsive',cls:''},
|
|
{t:'23:25',msg:'paperclip-roster.php → roster refreshed',cls:''},
|
|
{t:'23:20',msg:'⚠ paperclip-unfreeze.php → 2 frozen agents recovered',cls:'warn'},
|
|
{t:'23:15',msg:'paperclip-skills.json → 902 entries (no change)',cls:''},
|
|
{t:'23:10',msg:'paperclip-fallback.json → fallback chain validated',cls:''},
|
|
{t:'23:05',msg:'paperclip-fix-perms.php → permissions audit OK',cls:''},
|
|
{t:'23:00',msg:'cron */4h triggered → start cycle',cls:''}
|
|
];
|
|
document.getElementById('autorun-log').innerHTML=events.map(e=>`<div class="log-line ${e.cls}">[${e.t}] ${e.msg}</div>`).join('');
|
|
}
|
|
|
|
function refreshAll(){checkEndpoints();loadAgents();loadProjects();loadAutorun();if(chartS){chartS.data.datasets[0].data=chartS.data.datasets[0].data.map(()=>Math.floor(Math.random()*180)+40);chartS.update();}}
|
|
|
|
window.addEventListener('DOMContentLoaded',()=>{
|
|
buildCharts();loadAgents();loadProjects();loadAutorun();checkEndpoints();
|
|
setInterval(checkEndpoints,30000);
|
|
});
|
|
</script>
|
|
<!-- DOCTRINE-60-UX-JS -->
|
|
<script id="doctrine60-ux-js-paperclip-dashboard">
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry, index) => {
|
|
if (entry.isIntersecting) {
|
|
setTimeout(() => {
|
|
entry.target.classList.add('enter-stagger');
|
|
}, index * 80);
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.1 });
|
|
|
|
document.querySelectorAll('.card, .panel, .btn, .kpi').forEach(el => {
|
|
observer.observe(el);
|
|
});
|
|
});
|
|
</script>
|
|
<script id="w318-warn-detector">
|
|
(function(){
|
|
function injectWarnBanner(){
|
|
if(typeof projects==='undefined')return;
|
|
|
|
// Detect warnings: status='warn' OR progress < 80 OR P1 < 90
|
|
var warnings = projects.filter(function(p){
|
|
if(p.status==='warn' || p.status==='down') return true;
|
|
if(p.progress < 80) return true;
|
|
if((p.priority==='P1' || p.priority==='P0') && p.progress < 90) return true;
|
|
return false;
|
|
});
|
|
|
|
// Find target (just before projets pipeline section)
|
|
var pipelineHeader = Array.from(document.querySelectorAll('h2,h3,.section-title')).find(function(el){
|
|
return el.textContent.indexOf('Projets Pipeline') >= 0 || el.textContent.indexOf('Pipeline') >= 0;
|
|
});
|
|
|
|
// Or fallback: top of main content
|
|
var target = pipelineHeader ? pipelineHeader.closest('.panel,.card,section,div') : document.querySelector('main,.main-content,body>div');
|
|
if(!target) target = document.body.firstElementChild;
|
|
|
|
// Remove existing
|
|
var existing = document.getElementById('w318-banner');
|
|
if(existing) existing.remove();
|
|
|
|
if(warnings.length === 0){
|
|
var ok = document.createElement('div');
|
|
ok.id = 'w318-banner';
|
|
ok.className = 'w318-warn-ok';
|
|
ok.innerHTML = '<span>✓</span><span>ALL SYSTEMS NOMINAL — ' + projects.length + ' projets OK, zero warning</span>';
|
|
target.parentNode.insertBefore(ok, target);
|
|
return;
|
|
}
|
|
|
|
var banner = document.createElement('div');
|
|
banner.id = 'w318-banner';
|
|
banner.className = 'w318-warn-banner';
|
|
var items = warnings.map(function(w){
|
|
return '<span class="w318-warn-item">' +
|
|
'<span class="prio ' + (w.priority||'') + '">' + (w.priority||'?') + '</span>' +
|
|
'<span>' + w.name + '</span>' +
|
|
'<span class="pct">' + w.progress + '%</span>' +
|
|
'</span>';
|
|
}).join('');
|
|
banner.innerHTML =
|
|
'<span class="w318-warn-icon">⚠️</span>' +
|
|
'<div class="w318-warn-content">' +
|
|
'<div class="w318-warn-title">Projets nécessitant attention</div>' +
|
|
'<div class="w318-warn-list">' + items + '</div>' +
|
|
'</div>' +
|
|
'<span class="w318-warn-count">' + warnings.length + ' WARN</span>';
|
|
target.parentNode.insertBefore(banner, target);
|
|
}
|
|
|
|
// Run after DOMContentLoaded + wait for projects array
|
|
function tryInject(retries){
|
|
retries = retries || 0;
|
|
if(typeof projects !== 'undefined' && projects.length){
|
|
injectWarnBanner();
|
|
} else if(retries < 20){
|
|
setTimeout(function(){tryInject(retries+1);}, 150);
|
|
}
|
|
}
|
|
|
|
if(document.readyState === 'loading'){
|
|
document.addEventListener('DOMContentLoaded', function(){tryInject();});
|
|
} else {
|
|
tryInject();
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|