+5641
-379
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,8 @@
|
||||
|
||||
<div class="sectionTitle"><h2>Service Status</h2><span>10-Sekunden-Refresh · optionale Engineering-Komponenten beeinflussen Readiness nicht</span></div><div id="services" class="grid"></div>
|
||||
|
||||
<div class="sectionTitle"><h2>Orchestrator & Knowledge Graph</h2><span>Master/Subagent-Leases · Queue-Zustand · n:m-Graphgesundheit</span></div><div id="orchestration" class="grid"></div>
|
||||
|
||||
<div class="card graphCard"><div class="graphHead">
|
||||
<select id="scope"><option value="runtime">Runtime & Trust</option><option value="ticket">Ticket Evidence</option><option value="learning">Learning Lineage</option><option value="research">Research Provenance</option><option value="brain">NeuroForge Brain</option><option value="engineering">Engineering Graph</option><option value="impact">Change Impact</option></select>
|
||||
<select id="runSelect" hidden><option value="">Run auswählen…</option></select>
|
||||
@@ -29,7 +31,7 @@ const palette={ticket:'#7eb6ff',run:'#9a8cff',knowledge:'#70d5ae',validated_outc
|
||||
function nodeColor(n){if(n.status==='problem'||n.status==='error'||n.status==='fail'||n.status==='failed')return '#ff7f92';if(n.status==='superseded'||n.status==='archived')return '#65758a';if(n.status==='corrected'||n.status==='corroborated')return '#ffd16d';return palette[n.kind]||palette[n.group]||'#9ab0c8'}
|
||||
function resize(){let r=canvas.getBoundingClientRect(),d=devicePixelRatio||1;canvas.width=Math.round(r.width*d);canvas.height=Math.round(r.height*d);ctx.setTransform(d,0,0,d,0,0);draw()}addEventListener('resize',resize);
|
||||
async function jget(url){let r=await fetch(url,{cache:'no-store'});if(!r.ok)throw new Error(await r.text()||r.statusText);return r.json()}
|
||||
async function loadStatus(){try{const [c,r]=await Promise.all([jget('/api/config'),jget('/api/status')]);config=c;mode.textContent=c.vector_backend;controlled.textContent=c.controlled_learning==='true'?'ON':'OFF';outcomeRetrieval.textContent=(c.outcome_retrieval==='true'?'ON':'OFF')+' · K='+c.outcome_retrieval_search_k+' · min '+c.outcome_retrieval_min_similarity;research.textContent=(c.research_enabled==='true'?'ON':'OFF')+' / '+(c.searxng_enabled==='true'?'SearXNG':'kein SearXNG')+' / '+(c.kb_staging_bridge==='true'?'Staging':'kein Staging');autonomy.textContent=c.autonomy_enabled==='true'?'ON':'OFF';services.innerHTML=(r.services||[]).map(s=>`<div class="card service"><h2>${esc(s.name)}${s.optional?' <span class="muted">(optional)</span>':''}</h2><div class="${s.ok?'ok':s.optional?'warn':'bad'}"><b>${s.ok?'ONLINE':s.optional?'OPTIONAL/OFFLINE':'PROBLEM'}</b> · HTTP ${s.status||'–'} · ${s.latency_ms} ms</div>${s.public_url?`<p><a href="${esc(s.public_url)}" target="_blank" rel="noopener">Oberfläche öffnen</a></p>`:''}<div class="detail">${esc(JSON.stringify(s.detail||s.error||{},null,2))}</div></div>`).join('')}catch(e){services.innerHTML=`<div class="card bad">Status nicht verfügbar: ${esc(e.message)}</div>`}}
|
||||
async function loadStatus(){try{const [c,r,o,g]=await Promise.all([jget('/api/config'),jget('/api/status'),jget('/api/neuroforge/orchestrator'),jget('/api/neuroforge/graph-status')]);config=c;mode.textContent=c.vector_backend;controlled.textContent=c.controlled_learning==='true'?'ON':'OFF';outcomeRetrieval.textContent=(c.outcome_retrieval==='true'?'ON':'OFF')+' · K='+c.outcome_retrieval_search_k+' · min '+c.outcome_retrieval_min_similarity;research.textContent=(c.research_enabled==='true'?'ON':'OFF')+' / '+(c.searxng_enabled==='true'?'SearXNG':'kein SearXNG')+' / '+(c.kb_staging_bridge==='true'?'Staging':'kein Staging');autonomy.textContent=c.autonomy_enabled==='true'?'ON':'OFF';services.innerHTML=(r.services||[]).map(s=>`<div class="card service"><h2>${esc(s.name)}${s.optional?' <span class="muted">(optional)</span>':''}</h2><div class="${s.ok?'ok':s.optional?'warn':'bad'}"><b>${s.ok?'ONLINE':s.optional?'OPTIONAL/OFFLINE':'PROBLEM'}</b> · HTTP ${s.status||'–'} · ${s.latency_ms} ms</div>${s.public_url?`<p><a href="${esc(s.public_url)}" target="_blank" rel="noopener">Oberfläche öffnen</a></p>`:''}<div class="detail">${esc(JSON.stringify(s.detail||s.error||{},null,2))}</div></div>`).join('');let os=o.status||{},gs=g.stats||{},workers=os.workers||[],online=workers.filter(x=>x.status==='online').length,stale=workers.filter(x=>x.status==='stale').length;orchestration.innerHTML=`<div class="card service"><h2>Master / Scheduler</h2><div class="${o.leader?'ok':'warn'}"><b>${o.leader?'LEADER':'FOLLOWER'}</b></div><div class="detail">${esc(JSON.stringify({queued:os.queued||0,claimed:os.claimed||0,retrying:os.retrying||0,applying:os.applying||0,blocked:os.blocked||0,failed:os.failed||0},null,2))}</div></div><div class="card service"><h2>Subagents</h2><div class="${online?'ok':'warn'}"><b>${online} ONLINE</b> · ${stale} stale</div><div class="detail">${esc(JSON.stringify(workers,null,2))}</div></div><div class="card service"><h2>Knowledge Graph</h2><div class="${gs.multi_linked_memories>0?'ok':'warn'}"><b>${gs.synapses||0} Kanten</b> · Grad Ø ${Number(gs.average_degree||0).toFixed(2)}</div><div class="detail">${esc(JSON.stringify(gs,null,2))}</div></div><div class="card service"><h2>Graph Connectivity</h2><div class="${gs.largest_component>1?'ok':'warn'}"><b>${gs.multi_linked_memories||0} n:m-Knoten</b></div><div class="detail">${esc(JSON.stringify({linked:gs.linked_memories||0,isolated:gs.isolated_memories||0,max_degree:gs.max_degree||0,components:gs.connected_components||0,largest_component:gs.largest_component||0},null,2))}</div></div>`}catch(e){services.innerHTML=`<div class="card bad">Status nicht verfügbar: ${esc(e.message)}</div>`;orchestration.innerHTML=`<div class="card bad">Orchestrator/Graph-Status nicht verfügbar: ${esc(e.message)}</div>`}}
|
||||
async function loadRuns(){try{let xs=await jget('/api/graph/runs?limit=80');runSelect.innerHTML='<option value="">Run auswählen…</option>'+xs.map(x=>`<option value="${esc(x.run_id)}">#${x.ticket_id} · ${esc(x.ticket_name||'')} · ${esc(x.outcome||'')}</option>`).join('')}catch(e){runSelect.innerHTML='<option value="">Run-Liste nicht verfügbar</option>'}}
|
||||
function endpoint(){let s=scope.value,b=budget.value,q=search.value.trim();if(s==='runtime')return '/api/graph/runtime';if(s==='ticket')return runSelect.value?'/api/graph/ticket?run_id='+encodeURIComponent(runSelect.value):'';if(s==='learning')return '/api/graph/learning?limit='+Math.min(500,Number(b)||180);if(s==='research')return '/api/graph/research?runs=8&max_events='+Math.min(800,Number(b)||320);if(s==='brain')return '/api/graph/brain?max_nodes='+Math.min(700,Number(b)||320);if(s==='engineering')return '/api/graph/engineering?max_nodes='+encodeURIComponent(b)+(q?'&q='+encodeURIComponent(q):'');if(s==='impact')return q?'/api/graph/impact?max_nodes='+encodeURIComponent(b)+'&depth=2&q='+encodeURIComponent(q):'';return ''}
|
||||
async function loadGraph(){let u=endpoint();if(!u){graph={nodes:[],edges:[],title:'Run auswählen'};applyGraph();return}graphMeta.textContent='lädt…';try{graph=await jget(u);selected='';neighborhood=null;applyGraph()}catch(e){graph={nodes:[],edges:[],title:'Fehler',meta:{error:e.message}};applyGraph();graphMeta.textContent='Fehler: '+e.message}}
|
||||
|
||||
@@ -105,6 +105,8 @@ func main() {
|
||||
mux.HandleFunc("GET /api/graph/learning", s.handleLearningGraph)
|
||||
mux.HandleFunc("GET /api/graph/research", s.handleResearchGraph)
|
||||
mux.HandleFunc("GET /api/graph/brain", s.handleBrainGraph)
|
||||
mux.HandleFunc("GET /api/neuroforge/orchestrator", s.handleNeuroForgeOrchestrator)
|
||||
mux.HandleFunc("GET /api/neuroforge/graph-status", s.handleNeuroForgeGraphStatus)
|
||||
mux.HandleFunc("GET /api/graph/engineering", s.handleEngineeringGraph)
|
||||
mux.HandleFunc("GET /api/graph/impact", s.handleEngineeringImpact)
|
||||
mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -144,6 +146,14 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) handleNeuroForgeOrchestrator(w http.ResponseWriter, r *http.Request) {
|
||||
s.proxyJSON(w, r, s.neuroforgeURL+"/api/v1/integrations/orchestrator/status", bearerHeader(s.neuroforgeKey))
|
||||
}
|
||||
|
||||
func (s *server) handleNeuroForgeGraphStatus(w http.ResponseWriter, r *http.Request) {
|
||||
s.proxyJSON(w, r, s.neuroforgeURL+"/api/v1/integrations/graph/status", bearerHeader(s.neuroforgeKey))
|
||||
}
|
||||
|
||||
func validateControlSecrets() error {
|
||||
check := func(name string, min int) error {
|
||||
v := strings.TrimSpace(os.Getenv(name))
|
||||
|
||||
Reference in New Issue
Block a user