🚀 Initial commit: Rose's custom WebUI with modernization + agent attribution

This commit is contained in:
Rose
2026-04-20 10:36:59 +02:00
parent 3bdf430413
commit 99dd1f57ae
118 changed files with 41900 additions and 0 deletions

View File

@@ -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