feat(web): render portable executor execution center
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
"""Unified portal HTML export with V2R1C2 resource-center extension.
|
||||
"""Unified portal HTML export with V2R1C2 resource and execution centers.
|
||||
|
||||
The canonical dependency-free portal remains in ``aios_v2.console``. This
|
||||
module applies deterministic, fail-fast extensions at import time so the
|
||||
V2R1C1 production entrypoint remains unchanged while the V2R1C2 candidate gets
|
||||
its tenant/Agent resource view.
|
||||
resource and portable-executor views.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -13,27 +13,29 @@ from aios_v2.console import CONSOLE_HTML as _BASE_PORTAL_HTML
|
||||
def _extend_portal(source: str) -> str:
|
||||
replacements = (
|
||||
(
|
||||
'<section id="overview" class="view active"></section>\n <section id="tasks" class="view"></section>',
|
||||
'<section id="overview" class="view active"></section>\n <section id="resources" class="view"></section>\n <section id="tasks" class="view"></section>',
|
||||
'<section id="overview" class="view active"></section>\n <section id="tasks" class="view"></section>\n <section id="authorization" class="view"></section>',
|
||||
'<section id="overview" class="view active"></section>\n <section id="resources" class="view"></section>\n <section id="tasks" class="view"></section>\n <section id="execution" class="view"></section>\n <section id="authorization" class="view"></section>',
|
||||
),
|
||||
(
|
||||
"const labels={overview:'系统总览',tasks:'任务中心'",
|
||||
"const labels={overview:'系统总览',resources:'租户与 Agent',tasks:'任务中心'",
|
||||
"const labels={overview:'系统总览',tasks:'任务中心',authorization:'授权中心'",
|
||||
"const labels={overview:'系统总览',resources:'租户与 Agent',tasks:'任务中心',execution:'执行中心',authorization:'授权中心'",
|
||||
),
|
||||
(
|
||||
"if(view==='overview')await overview();if(view==='tasks')await tasks();",
|
||||
"if(view==='overview')await overview();if(view==='resources')await resources();if(view==='tasks')await tasks();",
|
||||
"if(view==='overview')await overview();if(view==='tasks')await tasks();if(view==='authorization')await authorizations();",
|
||||
"if(view==='overview')await overview();if(view==='resources')await resources();if(view==='tasks')await tasks();if(view==='execution')await execution();if(view==='authorization')await authorizations();",
|
||||
),
|
||||
(
|
||||
"async function tasks(){const d=await api('/api/v2/portal/tasks');",
|
||||
"""async function resources(){const d=await api('/api/v2/portal/resources');const t=d.tenant||{},agents=d.agents||[],ops=d.operations||{};const agentButton=ops.agent_create?'<button class=\"btn\" id=\"newAgent\">注册 Agent</button>':'';$('#resources').innerHTML=`<div class=\"cards\">${card('租户',t.tenant_id||'—',t.status||'')}${card('Agent',d.summary?.agents||0,`启用 ${d.summary?.enabled_agents||0}`)}${card('任务',d.summary?.tasks||0)}${card('Runtime 类型',(d.summary?.runtimes||[]).length,(d.summary?.runtimes||[]).join(', '))}</div><div class=\"grid\"><div class=\"panel\"><div class=\"panel-head\"><h2>当前租户</h2></div><div class=\"kv\"><div>Tenant ID</div><div>${esc(t.tenant_id||'—')}</div><div>名称</div><div>${esc(t.display_name||'—')}</div><div>状态</div><div>${pill(t.status||'unknown')}</div><div>创建时间</div><div>${esc(t.created_at||'—')}</div><div>更新时间</div><div>${esc(t.updated_at||'—')}</div></div></div><div class=\"panel\"><div class=\"panel-head\"><h2>Agent Registry</h2><div>${agentButton}</div></div>${table([['Agent',x=>esc(x.agent_id)],['名称',x=>esc(x.display_name)],['Runtime',x=>pill(x.runtime)],['能力',x=>(x.capabilities||[]).map(pill).join('')],['启用',x=>pill(x.enabled?'enabled':'disabled')],['任务数',x=>esc(x.task_count)]],agents)}</div></div>`;$('#newAgent')?.addEventListener('click',()=>{const agent_id=prompt('Agent ID');const display_name=prompt('显示名称',agent_id||'');const runtime=prompt('Runtime(openclaw/hermes)','openclaw');const capabilities=prompt('能力,逗号分隔','');if(!agent_id||!display_name||!runtime)return;api('/api/v2/portal/agents',{method:'POST',body:JSON.stringify({agent_id,display_name,runtime,capabilities:String(capabilities||'').split(',').map(x=>x.trim()).filter(Boolean),metadata:{source:'unified-portal'}})}).then(resources).catch(err)})}
|
||||
async function tasks(){const d=await api('/api/v2/portal/tasks');""",
|
||||
"""async function resources(){const d=await api('/api/v2/portal/resources');const t=d.tenant||{},agents=d.agents||[],ops=d.operations||{};const agentButton=ops.agent_create?'<button class=\"btn\" id=\"newAgent\">注册 Agent</button>':'';$('#resources').innerHTML=`<div class=\"cards\">${card('租户',t.tenant_id||'—',t.status||'')}${card('Agent',d.summary?.agents||0,`启用 ${d.summary?.enabled_agents||0}`)}${card('任务',d.summary?.tasks||0)}${card('Runtime 类型',(d.summary?.runtimes||[]).length,(d.summary?.runtimes||[]).join(', '))}</div><div class=\"grid\"><div class=\"panel\"><div class=\"panel-head\"><h2>当前租户</h2></div><div class=\"kv\"><div>Tenant ID</div><div>${esc(t.tenant_id||'—')}</div><div>名称</div><div>${esc(t.display_name||'—')}</div><div>状态</div><div>${pill(t.status||'unknown')}</div><div>创建时间</div><div>${esc(t.created_at||'—')}</div><div>更新时间</div><div>${esc(t.updated_at||'—')}</div></div></div><div class=\"panel\"><div class=\"panel-head\"><h2>Agent Registry</h2><div>${agentButton}</div></div>${table([['Agent',x=>esc(x.agent_id)],['名称',x=>esc(x.display_name)],['Runtime',x=>pill(x.runtime)],['能力',x=>(x.capabilities||[]).map(pill).join('')],['启用',x=>pill(x.enabled?'enabled':'disabled')],['任务数',x=>esc(x.task_count)]],agents)}</div></div>`;$('#newAgent')?.addEventListener('click',()=>{const agent_id=prompt('Agent ID');const display_name=prompt('显示名称',agent_id||'');const runtime=prompt('Runtime Provider','openclaw');const capabilities=prompt('能力,逗号分隔','');if(!agent_id||!display_name||!runtime)return;api('/api/v2/portal/agents',{method:'POST',body:JSON.stringify({agent_id,display_name,runtime,capabilities:String(capabilities||'').split(',').map(x=>x.trim()).filter(Boolean),metadata:{source:'unified-portal'}})}).then(resources).catch(err)})}
|
||||
+function executionStage(x){const s=x.stage||{},t=x.truth||{},d=x.dependency_gate||{},notifications=t.notifications||[],evidence=t.evidence||{};const stateName=t.state||'WAITING_DEPENDENCY';const deps=(s.depends_on||[]).join(', ')||'根阶段';return `<div class=\"panel\" style=\"background:#fbfcfe\"><div class=\"panel-head\"><h2>${esc(s.stage_id)} · ${esc(s.role)}</h2><div>${pill(stateName)} ${pill(s.executor_type)}</div></div><div class=\"chain\"><div class=\"chain-item\"><b>Provider</b><div>${esc(s.executor_type)}</div><small>${esc(deps)}</small></div><span class=\"chain-arrow\">→</span><div class=\"chain-item\"><b>Run</b><div>${esc(t.executor_run_id||'未注册')}</div><small>${esc(t.executor_id||'')}</small></div><span class=\"chain-arrow\">→</span><div class=\"chain-item\"><b>Progress</b><div>${esc(t.current_step||0)}/${esc(t.total_steps||(s.steps||[]).length)}</div><small>${esc(t.heartbeat_at||'无心跳')}</small></div><span class=\"chain-arrow\">→</span><div class=\"chain-item\"><b>Evidence</b><div>${Object.keys(evidence).length} 项</div><small>${Object.keys(evidence).slice(0,2).map(esc).join(', ')}</small></div><span class=\"chain-arrow\">→</span><div class=\"chain-item\"><b>Notification</b><div>${notifications.length} 条</div><small>${notifications.map(n=>n.status).join(', ')||'无'}</small></div><span class=\"chain-arrow\">→</span><div class=\"chain-item\"><b>Gate</b><div>${pill(t.passed?'PASS':'BLOCKED')}</div><small>${d.passed?'依赖满足':'等待依赖'}</small></div></div>${Object.keys(t.error||{}).length?json(t.error):''}</div>`}
|
||||
+async function execution(){const d=await api('/api/v2/portal/execution');const providers=d.providers||[],plans=d.plans||[];$('#execution').innerHTML=`<div class=\"cards\">${card('Provider',providers.length)}${card('Execution Plan',plans.length)}${card('协议',d.protocol_version||'—')}${card('Plan Schema',d.plan_schema_version||'—')}</div><div class=\"panel\"><div class=\"panel-head\"><h2>Executor Provider Catalog</h2></div>${table([['Provider',x=>esc(x.executor_type)],['Adapter',x=>esc(x.adapter_version)],['协议',x=>esc(x.protocol_version)],['Trust Roles',x=>(x.trust_roles||[]).map(pill).join('')],['Capabilities',x=>(x.capabilities||[]).map(pill).join('')]],providers)}</div>${plans.map(p=>`<div class=\"panel\"><div class=\"panel-head\"><h2>${esc(p.plan_id)} · ${esc(p.target_version)}</h2><div>${pill(p.gate?.passed?'PASS':'BLOCKED')}</div></div><div class=\"kv\"><div>Source Branch</div><div>${esc(p.source_branch)}</div><div>Source OID</div><div>${esc(p.source_oid)}</div><div>Release Gate</div><div>${pill(p.gate?.passed?'PASS':'BLOCKED')}</div></div></div>${(p.snapshot?.stages||[]).map(executionStage).join('')}`).join('')||'<div class=\"empty\">尚未声明执行计划</div>'}`}
|
||||
+async function tasks(){const d=await api('/api/v2/portal/tasks');""",
|
||||
),
|
||||
)
|
||||
result = source
|
||||
for old, new in replacements:
|
||||
if old not in result:
|
||||
raise RuntimeError(f"unified portal extension anchor not found: {old[:80]}")
|
||||
raise RuntimeError(f"unified portal extension anchor not found: {old[:100]}")
|
||||
result = result.replace(old, new, 1)
|
||||
return result
|
||||
|
||||
|
||||
Reference in New Issue
Block a user