自检循环1: F2时序拓扑 → J Tab 已实现
- 检测: dev-spec中标明❌未实现 - 实现: 新增J拓扑Tab,展示依赖/被依赖关系 - 测试: Playwright 0 errors - 更新: dev-spec.md 时序拓扑→✅ J Tab已实现
This commit is contained in:
@@ -66,7 +66,7 @@ th{color:var(--dim);font-weight:600;font-size:11px}
|
||||
<h1>AgentsMeeting</h1><div class="subtitle" id="subtitle">Dashboard</div>
|
||||
<div class="tabs" id="tabs"></div><div id="panes"></div><div id="toast" class="toast"></div>
|
||||
<script>
|
||||
var T=[{"id": "agents", "label": "Agents"}, {"id": "kanban", "label": "Kanban"}, {"id": "infra", "label": "Infrastructure"}, {"id": "principles", "label": "开发原则", "children": [{"id": "git", "label": "A 源码"}, {"id": "svc", "label": "B 服务"}, {"id": "mon", "label": "C 监控"}, {"id": "todos", "label": "D 修复"}, {"id": "meta", "label": "E 成长"}, {"id": "exp", "label": "F 期望"},{"id":"spec","label":"G 规范"},{"id":"prd","label":"H 需求"},{"id":"tree","label":"I 功能树"}]}];
|
||||
var T=[{"id": "agents", "label": "Agents"}, {"id": "kanban", "label": "Kanban"}, {"id": "infra", "label": "Infrastructure"}, {"id": "principles", "label": "开发原则", "children": [{"id": "git", "label": "A 源码"}, {"id": "svc", "label": "B 服务"}, {"id": "mon", "label": "C 监控"}, {"id": "todos", "label": "D 修复"}, {"id": "meta", "label": "E 成长"}, {"id": "exp", "label": "F 期望"},{"id":"spec","label":"G 规范"},{"id":"prd","label":"H 需求"},{"id":"tree","label":"I 功能树"},{"id":"topo","label":"J 拓扑"}]}];
|
||||
var at=localStorage.getItem('t')||'agents',as=localStorage.getItem('s')||'git';
|
||||
function esc(s){return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')}
|
||||
function qs(id){return document.getElementById(id)}
|
||||
@@ -112,7 +112,23 @@ if(svc.children){for(var i=0;i<svc.children.length;i++){renderNode(svc.children[
|
||||
// Build tree by connecting children to parents
|
||||
d.services.forEach(function(s){if(s._parents){s._parents.forEach(function(p){var parent=d.services.find(function(x){return x.name===p});if(parent){if(!parent.children)parent.children=[];parent.children.push(s)}})}});
|
||||
renderNode(tree,0);
|
||||
h+='</div>';var allOk=d.services.filter(function(s){return s.health&&s.health.ok}).length;var allWatched=d.services.filter(function(s){return s.watchdog}).length;h+='<div style=margin-top:10px;padding:8px;background:var(--bg);border-radius:6px;color:var(--dim);font-size:11px>Health: '+allOk+'/'+d.services.length+' | Watchdog: '+allWatched+'/'+d.services.length+'</div>';fill('tree',h)}catch(e){fill('tree','<span class=tag r>Error</span>')}}var FETCHES={agents:fa,git:fG,svc:fS,mon:fM,todos:fT,meta:fE,exp:fF,spec:fS2,prd:fH,tree:fTree,kanban:fK,infra:fI,easytier:fE2,rdp:fR,principles:function(){return(FETCHES[as]||function(){})()}};
|
||||
h+='</div>';var allOk=d.services.filter(function(s){return s.health&&s.health.ok}).length;var allWatched=d.services.filter(function(s){return s.watchdog}).length;h+='<div style=margin-top:10px;padding:8px;background:var(--bg);border-radius:6px;color:var(--dim);font-size:11px>Health: '+allOk+'/'+d.services.length+' | Watchdog: '+allWatched+'/'+d.services.length+'</div>';fill('tree',h)}catch(e){fill('tree','<span class=tag r>Error</span>')}}async function fTopo(){try{var r=await fetch('/api/services'),d=await r.json();if(!d.services||!d.services.length){fill('topo','No data');return}
|
||||
var h='<div style=font-size:13px;line-height:2>';
|
||||
var types={'core':'核心服务','bridge':'桥接服务','mgmt':'管理服务','infra':'基础设施'};
|
||||
d.services.forEach(function(s){
|
||||
var ok=s.health&&s.health.ok;
|
||||
var dot=ok?'<span class=sd g></span>':'<span class=sd r></span>';
|
||||
var wd=s.watchdog?'<span class=tag g style=font-size:10px>WD</span>':'<span class=tag r style=font-size:10px>noWD</span>';
|
||||
h+='<div style="background:var(--card);border:1px solid var(--border);border-radius:8px;padding:10px 14px;margin-bottom:8px">';
|
||||
h+='<div style=display:flex;align-items:center;gap:8px>'+dot+'<strong>'+esc(s.name)+'</strong><span class=tag d style=font-size:10px>'+esc(types[s.type]||s.type)+'</span>'+wd+'<span style=color:var(--dim);font-size:11px>'+(s.port?':'+s.port:'')+'</span></div>';
|
||||
// Dependencies
|
||||
if(s.depends_on&&s.depends_on.length){h+='<div style=margin-top:4px;padding-left:20px;font-size:12px;color:var(--dim)>依赖: ';s.depends_on.forEach(function(dp,i){if(i>0)h+=' → ';h+='<span style=color:var(--accent)>'+esc(dp)+'</span>'});h+='</div>'}
|
||||
// Connections from other services to this one
|
||||
var deps=d.services.filter(function(x){return x.depends_on&&x.depends_on.indexOf(s.name)>=0});
|
||||
if(deps.length){h+='<div style=margin-top:2px;padding-left:20px;font-size:12px;color:var(--dim)>被依赖: ';deps.forEach(function(dp,i){if(i>0)h+=' ← ';h+='<span style=color:var(--yellow)>'+esc(dp.name)+'</span>'});h+='</div>'}
|
||||
h+='</div>';});
|
||||
h+='<div style=margin-top:10px;padding:8px;background:var(--bg);border-radius:6px;color:var(--dim);font-size:11px>箭头方向: 依赖 → 被依赖 | 健康: '+d.services.filter(function(s){return s.health&&s.health.ok}).length+'/'+d.services.length+'</div>';
|
||||
fill('topo',h)}catch(e){fill('topo','<span class=tag r>Error</span>')}}var FETCHES={agents:fa,git:fG,svc:fS,mon:fM,todos:fT,meta:fE,exp:fF,spec:fS2,prd:fH,tree:fTree,topo:fTopo,kanban:fK,infra:fI,easytier:fE2,rdp:fR,principles:function(){return(FETCHES[as]||function(){})()}};
|
||||
async function loadAll(){var d=new Date();qs('subtitle').textContent=d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate()+' '+d.getHours()+':'+d.getMinutes();await Promise.all(Object.keys(FETCHES).map(function(k){return FETCHES[k]()}));}
|
||||
|
||||
async function fE2(){try{var r=await fetch('/api/easytier'),d=await r.json(),s=d.status||{};var e1=document.getElementById('et-w');if(e1){e1.className='d '+(s.windows==='running'?'ok':'stopped')}var e2=document.getElementById('et-246');if(e2){e2.className='d '+(s['246']==='running'?'ok':'stopped')}if(d.virtual_ips){var w=document.getElementById('et-w-ip');if(w)w.textContent=d.virtual_ips.windows||'';var t=document.getElementById('et-246-ip');if(t)t.textContent=d.virtual_ips['246']||''}}catch(e){}}
|
||||
|
||||
Reference in New Issue
Block a user