mirror of
https://github.com/blackboxprogramming/blackroad.io.git
synced 2026-03-17 03:57:11 -05:00
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>
This commit is contained in:
516
agents-dynamic.html
Normal file
516
agents-dynamic.html
Normal file
@@ -0,0 +1,516 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user