Files
blackroad-io-site/agents-dynamic.html
Alexa Louise b45957f11c Add dynamic live agent dashboard with real-time spawning
Created agents-dynamic.html - Revolutionary agent management UI:

Features:
 Real-time stats (total, active, yours, uptime)
 Live agent spawning with instant feedback
 Quick-spawn buttons for common roles
 Auto-refresh every 5 seconds
 Beautiful agent cards with animations
 Terminate agents with confirmation
 Pack selection (finance, legal, research, creator, devops)
 Capability tags display
 Elapsed time tracking
 Responsive grid layout

Agent Packs Available:
- Finance: Financial analysis & crypto trading
- Legal: Contract review & compliance
- Research Lab: Data analysis & insights
- Creator Studio: Content creation
- DevOps: Infrastructure & automation
- General: All-purpose agents

Quick Spawn Options:
🤖 Data Analyst, Crypto Trader, Code Reviewer, Content Creator, Legal Advisor, General Assistant

All connected to live backend API!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 14:40:40 -06:00

517 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🤖 Live Agent Dashboard - BlackRoad OS</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--gradient: linear-gradient(135deg, #FF9D00, #FF6B00, #FF0066, #D600AA, #7700FF, #0066FF);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #02030a;
color: white;
min-height: 100vh;
overflow-x: hidden;
}
/* Navigation */
#blackroad-nav { position: sticky; top: 0; z-index: 1000; }
/* Header Stats */
.stats-header {
padding: 32px;
background: linear-gradient(180deg, rgba(119, 0, 255, 0.2) 0%, transparent 100%);
border-bottom: 2px solid rgba(119, 0, 255, 0.3);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 24px;
max-width: 1400px;
margin: 0 auto;
}
.stat-card {
background: rgba(255,255,255,0.05);
border: 2px solid rgba(255,255,255,0.1);
border-radius: 16px;
padding: 24px;
text-align: center;
transition: all 0.3s;
}
.stat-card:hover {
border-color: #7700FF;
transform: translateY(-4px);
}
.stat-value {
font-size: 48px;
font-weight: 900;
background: var(--gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 8px;
}
.stat-label {
font-size: 14px;
opacity: 0.7;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Main Content */
.container {
max-width: 1400px;
margin: 0 auto;
padding: 48px 32px;
}
/* Spawn Panel */
.spawn-panel {
background: rgba(255,255,255,0.05);
border: 2px solid rgba(119, 0, 255, 0.5);
border-radius: 16px;
padding: 32px;
margin-bottom: 48px;
}
.spawn-panel h2 {
font-size: 32px;
margin-bottom: 24px;
background: var(--gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.spawn-form {
display: grid;
grid-template-columns: 1fr 1fr auto;
gap: 16px;
margin-bottom: 16px;
}
input, select {
padding: 14px;
border-radius: 12px;
border: 1px solid rgba(255,255,255,0.2);
background: rgba(255,255,255,0.05);
color: white;
font-size: 16px;
}
input:focus, select:focus {
outline: none;
border-color: #7700FF;
}
.spawn-btn {
padding: 14px 32px;
border-radius: 12px;
border: none;
background: var(--gradient);
color: white;
font-weight: 700;
cursor: pointer;
transition: transform 0.2s;
font-size: 16px;
}
.spawn-btn:hover {
transform: scale(1.05);
}
.quick-spawn {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.quick-btn {
padding: 8px 16px;
border-radius: 8px;
border: 1px solid rgba(255,255,255,0.3);
background: rgba(255,255,255,0.1);
color: white;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.quick-btn:hover {
background: rgba(119, 0, 255, 0.3);
border-color: #7700FF;
}
/* Agents Grid */
.agents-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 24px;
}
.agent-card {
background: rgba(255,255,255,0.05);
border: 2px solid rgba(255,255,255,0.1);
border-radius: 16px;
padding: 24px;
transition: all 0.3s;
animation: slideIn 0.5s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.agent-card:hover {
border-color: #7700FF;
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(119, 0, 255, 0.3);
}
.agent-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.agent-id {
font-family: monospace;
font-size: 12px;
opacity: 0.5;
}
.agent-role {
font-size: 20px;
font-weight: 700;
margin: 8px 0;
}
.agent-status {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-active {
background: rgba(0, 255, 136, 0.2);
color: #00ff88;
border: 1px solid #00ff88;
}
.status-idle {
background: rgba(255, 157, 0, 0.2);
color: #FF9D00;
border: 1px solid #FF9D00;
}
.agent-capabilities {
margin: 12px 0;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.capability-tag {
padding: 4px 8px;
background: rgba(119, 0, 255, 0.2);
border: 1px solid rgba(119, 0, 255, 0.5);
border-radius: 6px;
font-size: 12px;
}
.agent-time {
font-size: 12px;
opacity: 0.6;
margin-top: 8px;
}
.agent-actions {
margin-top: 16px;
display: flex;
gap: 8px;
}
.action-btn {
padding: 8px 16px;
border-radius: 8px;
border: none;
background: rgba(255,255,255,0.1);
color: white;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
}
.action-btn:hover {
background: rgba(255, 0, 102, 0.3);
}
/* Loading */
.loading {
text-align: center;
padding: 48px;
opacity: 0.7;
}
.spinner {
display: inline-block;
width: 40px;
height: 40px;
border: 4px solid rgba(255,255,255,0.1);
border-top-color: #7700FF;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Empty State */
.empty-state {
text-align: center;
padding: 64px 32px;
opacity: 0.7;
}
.empty-state h3 {
font-size: 24px;
margin-bottom: 16px;
}
</style>
</head>
<body>
<!-- Navigation -->
<div id="blackroad-nav"></div>
<!-- Stats Header -->
<div class="stats-header">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value" id="totalAgents">0</div>
<div class="stat-label">Total Agents</div>
</div>
<div class="stat-card">
<div class="stat-value" id="activeAgents">0</div>
<div class="stat-label">Active Now</div>
</div>
<div class="stat-card">
<div class="stat-value" id="yourAgents">0</div>
<div class="stat-label">Your Agents</div>
</div>
<div class="stat-card">
<div class="stat-value" id="systemUptime">100%</div>
<div class="stat-label">System Uptime</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="container">
<!-- Spawn Panel -->
<div class="spawn-panel">
<h2>🚀 Spawn New Agent</h2>
<div class="spawn-form">
<input type="text" id="agentRole" placeholder="Agent Role (e.g., Financial Analyst)" />
<select id="agentPack">
<option value="">Select Pack (Optional)</option>
<option value="pack-finance">Finance</option>
<option value="pack-legal">Legal</option>
<option value="pack-research-lab">Research</option>
<option value="pack-creator-studio">Creator</option>
<option value="pack-infra-devops">DevOps</option>
</select>
<button class="spawn-btn" onclick="spawnAgent()">Spawn Agent</button>
</div>
<div style="margin-top: 16px; opacity: 0.7; font-size: 14px;">
Quick Spawn:
</div>
<div class="quick-spawn">
<button class="quick-btn" onclick="quickSpawn('Data Analyst', 'pack-research-lab')">📊 Data Analyst</button>
<button class="quick-btn" onclick="quickSpawn('Crypto Trader', 'pack-finance')">💰 Crypto Trader</button>
<button class="quick-btn" onclick="quickSpawn('Code Reviewer', 'pack-infra-devops')">👨‍💻 Code Reviewer</button>
<button class="quick-btn" onclick="quickSpawn('Content Creator', 'pack-creator-studio')">✍️ Content Creator</button>
<button class="quick-btn" onclick="quickSpawn('Legal Advisor', 'pack-legal')">⚖️ Legal Advisor</button>
<button class="quick-btn" onclick="quickSpawn('General Assistant', '')">🤖 General</button>
</div>
</div>
<!-- Agents Grid -->
<div id="agentsList">
<div class="loading">
<div class="spinner"></div>
<p style="margin-top: 16px;">Loading agents...</p>
</div>
</div>
</div>
<!-- BlackRoad API -->
<script src="/blackroad-api.js"></script>
<script src="/blackroad-nav.js"></script>
<script>
let agents = [];
let autoRefresh = true;
// Initialize
async function init() {
await loadAgents();
await updateStats();
// Auto-refresh every 5 seconds
setInterval(() => {
if (autoRefresh) {
loadAgents();
updateStats();
}
}, 5000);
}
// Load agents from backend
async function loadAgents() {
try {
const data = await window.blackroad.listAgents();
agents = data.agents || [];
renderAgents();
return data;
} catch (error) {
console.error('Failed to load agents:', error);
document.getElementById('agentsList').innerHTML = `
<div class="empty-state">
<h3>Unable to connect to backend</h3>
<p>Make sure the API is running at localhost:8000</p>
<button class="spawn-btn" onclick="init()">Retry</button>
</div>
`;
}
}
// Render agents
function renderAgents() {
const container = document.getElementById('agentsList');
if (agents.length === 0) {
container.innerHTML = `
<div class="empty-state">
<h3>No agents spawned yet</h3>
<p>Click "Spawn Agent" above to create your first AI agent!</p>
</div>
`;
return;
}
container.innerHTML = `<div class="agents-grid">${
agents.map(agent => createAgentCard(agent)).join('')
}</div>`;
}
// Create agent card HTML
function createAgentCard(agent) {
const elapsed = getElapsedTime(agent.created_at);
const capabilities = agent.capabilities || ['general'];
return `
<div class="agent-card" data-agent-id="${agent.id}">
<div class="agent-header">
<span class="agent-status status-${agent.status}">${agent.status}</span>
<span class="agent-id">${agent.id.substring(0, 12)}...</span>
</div>
<div class="agent-role">🤖 ${agent.role}</div>
${agent.pack ? `<div style="opacity: 0.7; font-size: 13px;">📦 ${agent.pack}</div>` : ''}
<div class="agent-capabilities">
${capabilities.map(cap => `<span class="capability-tag">${cap}</span>`).join('')}
</div>
<div class="agent-time">Spawned ${elapsed}</div>
<div class="agent-actions">
<button class="action-btn" onclick="viewAgent('${agent.id}')">View</button>
<button class="action-btn" onclick="terminateAgent('${agent.id}')">Terminate</button>
</div>
</div>
`;
}
// Spawn new agent
async function spawnAgent() {
const role = document.getElementById('agentRole').value.trim();
const pack = document.getElementById('agentPack').value;
if (!role) {
alert('Please enter an agent role');
return;
}
if (!window.blackroad.isAuthenticated()) {
alert('Please login first');
window.location.href = '/';
return;
}
try {
const capabilities = ['general'];
const data = await window.blackroad.spawnAgent(role, capabilities, pack || null);
// Add to local list immediately for instant feedback
agents.unshift(data.agent);
renderAgents();
// Clear form
document.getElementById('agentRole').value = '';
document.getElementById('agentPack').value = '';
// Update stats
updateStats();
alert(`✓ Agent spawned successfully!\nID: ${data.agent_id}\nRole: ${role}`);
} catch (error) {
alert('Failed to spawn agent: ' + error.message);
}
}
// Quick spawn
async function quickSpawn(role, pack) {
document.getElementById('agentRole').value = role;
document.getElementById('agentPack').value = pack;
await spawnAgent();
}
// Terminate agent
async function terminateAgent(agentId) {
if (!confirm('Are you sure you want to terminate this agent?')) return;
try {
await window.blackroad.terminateAgent(agentId);
agents = agents.filter(a => a.id !== agentId);
renderAgents();
updateStats();
alert('Agent terminated');
} catch (error) {
alert('Failed to terminate agent: ' + error.message);
}
}
// View agent details
function viewAgent(agentId) {
const agent = agents.find(a => a.id === agentId);
if (!agent) return;
alert(`Agent Details:\n\nID: ${agent.id}\nRole: ${agent.role}\nStatus: ${agent.status}\nCapabilities: ${agent.capabilities.join(', ')}\nPack: ${agent.pack || 'None'}\nCreated: ${new Date(agent.created_at).toLocaleString()}`);
}
// Update stats
async function updateStats() {
try {
const stats = await window.blackroad.getSystemStats();
document.getElementById('totalAgents').textContent = stats.total_agents;
document.getElementById('activeAgents').textContent = stats.active_agents;
document.getElementById('yourAgents').textContent = agents.length;
document.getElementById('systemUptime').textContent = stats.uptime;
} catch (error) {
console.error('Failed to update stats:', error);
}
}
// Utility: Get elapsed time
function getElapsedTime(timestamp) {
const now = new Date();
const created = new Date(timestamp);
const diff = now - created;
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days}d ago`;
if (hours > 0) return `${hours}h ago`;
if (minutes > 0) return `${minutes}m ago`;
return `${seconds}s ago`;
}
// Initialize on load
init();
</script>
</body>
</html>