🚀 Initial commit: Rose's custom WebUI with modernization + agent attribution
This commit is contained in:
144
static/panels.js
144
static/panels.js
@@ -17,6 +17,7 @@ async function switchPanel(name) {
|
||||
if (name === 'profiles') await loadProfilesPanel();
|
||||
if (name === 'todos') loadTodos();
|
||||
if (name === 'missioncontrol') await loadMissionControl();
|
||||
if (name === 'agents') await loadAgentsPanel();
|
||||
}
|
||||
|
||||
// ── Cron panel ──
|
||||
@@ -1764,4 +1765,147 @@ async function deleteMCPriority(id) {
|
||||
await refreshMC();
|
||||
}
|
||||
|
||||
// ── Agents Panel (Rose + Tier-2) ─────────────────────────────────────────────
|
||||
let _agentsInterval = null;
|
||||
let _selectedAgent = null;
|
||||
|
||||
async function loadAgentsPanel() {
|
||||
clearInterval(_agentsInterval);
|
||||
await refreshAgents();
|
||||
_agentsInterval = setInterval(refreshAgents, 15000);
|
||||
}
|
||||
|
||||
async function refreshAgents() {
|
||||
try {
|
||||
const data = await api('/api/agents');
|
||||
renderAgentsList(data.agents || []);
|
||||
} catch(e) {
|
||||
const box = $('agentsList');
|
||||
if (box) box.innerHTML = `<div style="padding:12px;color:var(--accent);font-size:12px">Error: ${esc(e.message)}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderAgentsList(agents) {
|
||||
const box = $('agentsList');
|
||||
if (!box) return;
|
||||
|
||||
const html = agents.map(a => {
|
||||
const statusColor = a.running ? '#4caf50' : '#9e9e9e';
|
||||
const statusLabel = a.running ? 'Active' : 'Inactive';
|
||||
const inboxBadge = a.inbox_count > 0
|
||||
? `<span style="background:#ff5722;color:white;border-radius:10px;padding:1px 6px;font-size:9px;font-weight:600">${a.inbox_count}</span>`
|
||||
: '';
|
||||
return `<div class="agent-card" onclick="selectAgent('${a.id}')" style="cursor:pointer">
|
||||
<div class="agent-card-left">
|
||||
<span style="font-size:24px;line-height:1">${a.emoji}</span>
|
||||
</div>
|
||||
<div class="agent-card-body">
|
||||
<div class="agent-card-name">${esc(a.name)}</div>
|
||||
<div class="agent-card-domain">${esc(a.domain)}</div>
|
||||
<div class="agent-card-meta">
|
||||
<span class="agent-status-dot" style="background:${statusColor}"></span>
|
||||
<span style="color:${statusColor};font-size:10px">${statusLabel}</span>
|
||||
${a.tier === 'orchestrator' ? '<span style="opacity:0.5;font-size:9px;margin-left:4px">Tier 0</span>' : '<span style="opacity:0.5;font-size:9px;margin-left:4px">Tier 2</span>'}
|
||||
</div>
|
||||
</div>
|
||||
<div class="agent-card-right">
|
||||
${inboxBadge}
|
||||
<span style="opacity:0.3;font-size:18px">›</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
box.innerHTML = html;
|
||||
}
|
||||
|
||||
async function selectAgent(agentId) {
|
||||
_selectedAgent = agentId;
|
||||
// Highlight selected
|
||||
document.querySelectorAll('.agent-card').forEach(el => el.classList.remove('selected'));
|
||||
const cards = document.querySelectorAll('.agent-card');
|
||||
const agents_data = await api('/api/agents');
|
||||
const idx = agents_data.agents.findIndex(a => a.id === agentId);
|
||||
if (cards[idx]) cards[idx].classList.add('selected');
|
||||
|
||||
// Show inbox panel
|
||||
const inboxBox = $('agentInbox');
|
||||
const agentName = agents_data.agents[idx]?.name || agentId;
|
||||
const emoji = agents_data.agents[idx]?.emoji || '🤖';
|
||||
const domain = agents_data.agents[idx]?.domain || '';
|
||||
|
||||
inboxBox.innerHTML = `
|
||||
<div class="inbox-header">
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px">
|
||||
<span style="font-size:20px">${emoji}</span>
|
||||
<div>
|
||||
<div style="font-weight:600;font-size:13px">${esc(agentName)}</div>
|
||||
<div style="font-size:10px;opacity:0.5">${esc(domain)}</div>
|
||||
</div>
|
||||
<button onclick="closeAgentInbox()" style="margin-left:auto;background:rgba(255,255,255,.05);border:1px solid var(--border);border-radius:6px;padding:4px 8px;cursor:pointer;color:var(--muted);font-size:11px">× Close</button>
|
||||
</div>
|
||||
<div style="color:var(--muted);font-size:11px;text-align:center;padding:20px">Loading inbox...</div>
|
||||
</div>
|
||||
`;
|
||||
inboxBox.style.display = 'block';
|
||||
|
||||
// Fetch inbox
|
||||
try {
|
||||
const data = await api(`/api/agents/inbox/${agentId}`);
|
||||
renderAgentInbox(data);
|
||||
} catch(e) {
|
||||
inboxBox.innerHTML = `<div style="padding:12px;color:var(--accent);font-size:12px">Error: ${esc(e.message)}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderAgentInbox(data) {
|
||||
const inboxBox = $('agentInbox');
|
||||
const agentName = data.agent_name || _selectedAgent;
|
||||
const messages = data.messages || [];
|
||||
|
||||
if (messages.length === 0) {
|
||||
inboxBox.innerHTML = `
|
||||
<div class="inbox-header">
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
||||
<span style="font-size:18px">📭</span>
|
||||
<span style="font-weight:600;font-size:13px">${esc(agentName)} — Inbox</span>
|
||||
<button onclick="closeAgentInbox()" style="margin-left:auto;background:rgba(255,255,255,.05);border:1px solid var(--border);border-radius:6px;padding:4px 8px;cursor:pointer;color:var(--muted);font-size:11px">× Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding:24px;text-align:center;color:var(--muted);font-size:12px">
|
||||
<div style="font-size:28px;margin-bottom:8px">📭</div>
|
||||
<div>No messages in inbox</div>
|
||||
<div style="font-size:10px;margin-top:4px;opacity:0.5">Messages from other agents appear here</div>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
inboxBox.innerHTML = `
|
||||
<div class="inbox-header">
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
||||
<span style="font-size:18px">📥</span>
|
||||
<span style="font-weight:600;font-size:13px">${esc(agentName)} — Inbox</span>
|
||||
<span style="opacity:0.5;font-size:10px">(${messages.length} messages)</span>
|
||||
<button onclick="closeAgentInbox()" style="margin-left:auto;background:rgba(255,255,255,.05);border:1px solid var(--border);border-radius:6px;padding:4px 8px;cursor:pointer;color:var(--muted);font-size:11px">× Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inbox-messages">
|
||||
${messages.map(m => {
|
||||
const ts = m.timestamp ? new Date(m.timestamp).toLocaleString() : '';
|
||||
const content = typeof m === 'string' ? m : (m.content || JSON.stringify(m));
|
||||
return `<div class="inbox-msg">
|
||||
<div class="inbox-msg-ts">${esc(ts)}</div>
|
||||
<div class="inbox-msg-content">${esc(String(content).slice(0, 300))}</div>
|
||||
</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function closeAgentInbox() {
|
||||
$('agentInbox').style.display = 'none';
|
||||
_selectedAgent = null;
|
||||
document.querySelectorAll('.agent-card').forEach(el => el.classList.remove('selected'));
|
||||
}
|
||||
|
||||
// Event wiring
|
||||
|
||||
Reference in New Issue
Block a user