Add fully functional web terminal with real-time agent control

Created terminal.html - A complete web-based terminal with:

Features:
 Full command execution (help, agents, spawn, chat, stats, etc.)
 Command history with arrow keys
 Real-time API integration
 Agent spawning from terminal
 Blockchain commands
 Chat with AI from CLI
 File system navigation (mock)
 Beautiful ASCII art header
 Live stats in status bar
 Auto-updating agent/user counts

Commands available:
- agents: List all spawned agents
- spawn <role>: Create new agent
- chat <msg>: Talk to AI
- stats: System statistics
- blockchain: View blocks
- tx <to> <amt>: Create transaction
- ping: Test API connection
- Plus standard Unix commands (ls, cd, pwd, echo, etc.)

Updated dashboard.html to include terminal card (highlighted green)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Alexa Louise
2025-12-13 14:39:12 -06:00
parent 6c68b5bce8
commit a095b39bda
2 changed files with 632 additions and 0 deletions

620
terminal.html Normal file
View File

@@ -0,0 +1,620 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BlackRoad Terminal - 🛣️ Shell</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--gradient: linear-gradient(135deg, #FF9D00, #FF6B00, #FF0066, #D600AA, #7700FF, #0066FF);
--terminal-bg: #0a0e1a;
--terminal-text: #00ff88;
--terminal-prompt: #7700FF;
--terminal-error: #FF0066;
}
body {
font-family: 'Courier New', monospace;
background: var(--terminal-bg);
color: var(--terminal-text);
min-height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Header */
.terminal-header {
background: rgba(0,0,0,0.8);
backdrop-filter: blur(20px);
border-bottom: 2px solid rgba(119, 0, 255, 0.5);
padding: 12px 24px;
display: flex;
justify-content: space-between;
align-items: center;
}
.terminal-title {
font-size: 20px;
font-weight: 900;
background: var(--gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.terminal-controls {
display: flex;
gap: 12px;
align-items: center;
}
.control-btn {
padding: 6px 12px;
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
border-radius: 6px;
color: white;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
font-family: 'Courier New', monospace;
}
.control-btn:hover {
background: rgba(119, 0, 255, 0.3);
border-color: #7700FF;
}
/* Terminal Body */
.terminal-body {
flex: 1;
padding: 24px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 8px;
}
.terminal-line {
display: flex;
gap: 8px;
line-height: 1.6;
}
.terminal-prompt {
color: var(--terminal-prompt);
font-weight: bold;
}
.terminal-command {
color: var(--terminal-text);
}
.terminal-output {
color: rgba(0, 255, 136, 0.8);
margin-left: 24px;
white-space: pre-wrap;
word-break: break-all;
}
.terminal-error {
color: var(--terminal-error);
margin-left: 24px;
}
.terminal-info {
color: #FF9D00;
margin-left: 24px;
}
/* Input Area */
.terminal-input-area {
padding: 16px 24px;
border-top: 2px solid rgba(119, 0, 255, 0.5);
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
gap: 12px;
}
.input-prompt {
color: var(--terminal-prompt);
font-weight: bold;
}
#terminalInput {
flex: 1;
background: transparent;
border: none;
color: var(--terminal-text);
font-family: 'Courier New', monospace;
font-size: 14px;
outline: none;
}
/* Cursor */
.cursor {
display: inline-block;
width: 8px;
height: 16px;
background: var(--terminal-text);
animation: blink 1s infinite;
}
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
/* Scrollbar */
.terminal-body::-webkit-scrollbar {
width: 8px;
}
.terminal-body::-webkit-scrollbar-track {
background: rgba(0,0,0,0.3);
}
.terminal-body::-webkit-scrollbar-thumb {
background: rgba(119, 0, 255, 0.5);
border-radius: 4px;
}
/* ASCII Art */
.ascii-art {
color: #7700FF;
font-size: 10px;
line-height: 1.2;
margin-bottom: 16px;
}
/* Status Bar */
.status-bar {
padding: 8px 24px;
background: rgba(0,0,0,0.8);
border-top: 1px solid rgba(119, 0, 255, 0.3);
display: flex;
justify-content: space-between;
font-size: 12px;
color: rgba(0, 255, 136, 0.6);
}
</style>
</head>
<body>
<div class="terminal-header">
<div class="terminal-title">🛣️ BlackRoad Terminal</div>
<div class="terminal-controls">
<button class="control-btn" onclick="clearTerminal()">Clear</button>
<button class="control-btn" onclick="showHelp()">Help</button>
<button class="control-btn" onclick="window.location.href='/dashboard.html'">Dashboard</button>
<span class="control-btn" style="cursor: default;" id="userDisplay">Guest</span>
</div>
</div>
<div class="terminal-body" id="terminalBody">
<pre class="ascii-art">
____ _ _ ____ _
| __ )| | __ _ ___| | _| _ \ ___ __ _ __| |
| _ \| |/ _` |/ __| |/ / |_) / _ \ / _` |/ _` |
| |_) | | (_| | (__| <| _ < (_) | (_| | (_| |
|____/|_|\__,_|\___|_|\_\_| \_\___/ \__,_|\__,_|
🛣️ OPERATING SYSTEM TERMINAL v1.0 🛣️
</pre>
<div class="terminal-output">Welcome to BlackRoad OS Terminal</div>
<div class="terminal-info">Type 'help' for available commands</div>
<div class="terminal-info">Connected to: <span id="apiStatus">checking...</span></div>
<div style="height: 16px;"></div>
</div>
<div class="terminal-input-area">
<span class="input-prompt" id="promptDisplay">guest@blackroad:~$</span>
<input type="text" id="terminalInput" autofocus autocomplete="off" spellcheck="false" />
</div>
<div class="status-bar">
<span>Agents: <span id="agentCount">0</span></span>
<span>Users: <span id="userCount">0</span></span>
<span>Uptime: <span id="uptime">100%</span></span>
</div>
<!-- BlackRoad Unified API -->
<script src="/blackroad-api.js"></script>
<script>
const terminalBody = document.getElementById('terminalBody');
const terminalInput = document.getElementById('terminalInput');
const promptDisplay = document.getElementById('promptDisplay');
const userDisplay = document.getElementById('userDisplay');
let commandHistory = [];
let historyIndex = -1;
let currentUser = null;
let currentDirectory = '~';
// Initialize
async function init() {
// Check auth
if (window.blackroad.isAuthenticated()) {
currentUser = await window.blackroad.loadCurrentUser();
if (currentUser) {
promptDisplay.textContent = `${currentUser.name || currentUser.email}@blackroad:${currentDirectory}$`;
userDisplay.textContent = currentUser.name || currentUser.email;
}
}
// Check API status
try {
const health = await window.blackroad.healthCheck();
addOutput('API Status: ' + health.status, 'info');
document.getElementById('apiStatus').textContent = health.status;
} catch (error) {
addOutput('API Status: offline (using local mode)', 'error');
document.getElementById('apiStatus').textContent = 'offline';
}
// Load stats
updateStats();
}
// Add output to terminal
function addOutput(text, type = 'output') {
const line = document.createElement('div');
line.className = `terminal-${type}`;
line.textContent = text;
terminalBody.appendChild(line);
scrollToBottom();
}
function addCommand(command) {
const line = document.createElement('div');
line.className = 'terminal-line';
line.innerHTML = `<span class="terminal-prompt">${promptDisplay.textContent}</span><span class="terminal-command">${command}</span>`;
terminalBody.appendChild(line);
scrollToBottom();
}
function scrollToBottom() {
terminalBody.scrollTop = terminalBody.scrollHeight;
}
// Command execution
async function executeCommand(command) {
if (!command.trim()) return;
// Add to history
commandHistory.push(command);
historyIndex = commandHistory.length;
// Display command
addCommand(command);
// Parse command
const parts = command.trim().split(' ');
const cmd = parts[0].toLowerCase();
const args = parts.slice(1);
// Execute
try {
switch(cmd) {
case 'help':
showHelp();
break;
case 'clear':
clearTerminal();
break;
case 'whoami':
if (currentUser) {
addOutput(`${currentUser.name || currentUser.email} (${currentUser.id})`);
} else {
addOutput('guest');
}
break;
case 'login':
addOutput('Use the web interface to login: https://blackroad.io');
break;
case 'agents':
await listAgents(args);
break;
case 'spawn':
await spawnAgent(args);
break;
case 'chat':
await sendChat(args.join(' '));
break;
case 'stats':
await showStats();
break;
case 'ls':
await listFiles(args);
break;
case 'cd':
changeDirectory(args[0]);
break;
case 'pwd':
addOutput(currentDirectory);
break;
case 'echo':
addOutput(args.join(' '));
break;
case 'date':
addOutput(new Date().toString());
break;
case 'ping':
await pingAPI();
break;
case 'blockchain':
await showBlockchain(args);
break;
case 'tx':
await createTransaction(args);
break;
case 'history':
commandHistory.forEach((cmd, i) => addOutput(`${i + 1} ${cmd}`));
break;
case 'uname':
addOutput('BlackRoad OS v1.0.0 (30000-agents)');
break;
case 'open':
if (args[0]) {
window.open(args[0].startsWith('http') ? args[0] : `/${args[0]}`, '_blank');
addOutput(`Opening: ${args[0]}`);
} else {
addOutput('Usage: open <url>');
}
break;
default:
addOutput(`Command not found: ${cmd}. Type 'help' for available commands.`, 'error');
}
} catch (error) {
addOutput(`Error: ${error.message}`, 'error');
}
}
// Commands implementation
function showHelp() {
const commands = [
'',
'Available Commands:',
'─────────────────────────────────────────',
' help - Show this help message',
' clear - Clear terminal screen',
' whoami - Display current user',
' login - Login instructions',
'',
'Agent Management:',
' agents [list] - List all agents',
' spawn <role> - Spawn new agent',
'',
'Communication:',
' chat <msg> - Send message to AI',
' ping - Ping API server',
'',
'Blockchain:',
' blockchain - Show recent blocks',
' tx <to> <amt> - Create transaction',
'',
'System:',
' stats - System statistics',
' uname - System information',
' date - Current date/time',
' history - Command history',
'',
'File System (mock):',
' ls [dir] - List directory',
' cd <dir> - Change directory',
' pwd - Print working directory',
'',
'Utilities:',
' echo <text> - Print text',
' open <url> - Open URL in new tab',
''
];
commands.forEach(line => addOutput(line));
}
function clearTerminal() {
terminalBody.innerHTML = '';
}
async function listAgents(args) {
try {
const data = await window.blackroad.listAgents();
addOutput(`Total Agents: ${data.total_agents}`);
addOutput(`Your Agents: ${data.user_agents}`);
if (data.agents.length > 0) {
addOutput('');
data.agents.forEach(agent => {
addOutput(` ${agent.id}: ${agent.role} [${agent.status}]`);
});
}
} catch (error) {
addOutput('Failed to list agents. Is the API running?', 'error');
}
}
async function spawnAgent(args) {
if (!currentUser) {
addOutput('Please login first', 'error');
return;
}
const role = args.join(' ') || 'General Agent';
try {
const data = await window.blackroad.spawnAgent(role, ['general']);
addOutput(`✓ Agent spawned: ${data.agent_id}`, 'info');
addOutput(` Role: ${role}`);
addOutput(` Status: ${data.status}`);
updateStats();
} catch (error) {
addOutput('Failed to spawn agent', 'error');
}
}
async function sendChat(message) {
if (!message) {
addOutput('Usage: chat <message>', 'error');
return;
}
try {
const data = await window.blackroad.chat(message);
addOutput('');
addOutput(`You: ${message}`);
addOutput(`BlackRoad AI: ${data.message}`);
addOutput('');
} catch (error) {
addOutput('Chat failed. Is the API running?', 'error');
}
}
async function showStats() {
try {
const stats = await window.blackroad.getSystemStats();
addOutput('');
addOutput('System Statistics:');
addOutput('─────────────────────────────────────');
addOutput(` Total Users: ${stats.total_users}`);
addOutput(` Total Agents: ${stats.total_agents}`);
addOutput(` Active Agents: ${stats.active_agents}`);
addOutput(` Conversations: ${stats.total_conversations}`);
addOutput(` Blockchain Blocks: ${stats.total_blocks}`);
addOutput(` Transactions: ${stats.total_transactions}`);
addOutput(` Uptime: ${stats.uptime}`);
addOutput(` Version: ${stats.version}`);
addOutput('');
} catch (error) {
addOutput('Failed to fetch stats', 'error');
}
}
async function pingAPI() {
const start = Date.now();
try {
await window.blackroad.healthCheck();
const latency = Date.now() - start;
addOutput(`✓ API is reachable (${latency}ms)`, 'info');
} catch (error) {
addOutput('✗ API is unreachable', 'error');
}
}
async function showBlockchain(args) {
try {
const data = await window.blackroad.getBlocks(5);
addOutput(`Total Blocks: ${data.total_blocks}`);
if (data.blocks.length > 0) {
addOutput('');
data.blocks.forEach(block => addOutput(` Block ${block.index}: ${block.hash}`));
} else {
addOutput('No blocks yet');
}
} catch (error) {
addOutput('Failed to fetch blockchain', 'error');
}
}
async function createTransaction(args) {
if (args.length < 2) {
addOutput('Usage: tx <to_address> <amount>', 'error');
return;
}
if (!currentUser) {
addOutput('Please login first', 'error');
return;
}
try {
const data = await window.blackroad.createTransaction(
currentUser.id,
args[0],
parseFloat(args[1])
);
addOutput(`✓ Transaction created: ${data.transaction_id}`, 'info');
addOutput(` Status: ${data.status}`);
} catch (error) {
addOutput('Transaction failed', 'error');
}
}
function listFiles(args) {
const dirs = {
'~': ['apps', 'agents', 'blockchain', 'docs', 'integrations'],
'~/apps': ['chat.html', 'wallet.html', 'ledger.html'],
'~/agents': ['agent-001', 'agent-002', 'spawner.py'],
'~/blockchain': ['blocks', 'transactions', 'roadcoin'],
'~/docs': ['README.md', 'API.md', 'DEPLOYMENT.md']
};
const path = args[0] || currentDirectory;
const contents = dirs[path] || [];
if (contents.length > 0) {
contents.forEach(item => addOutput(` ${item}`));
} else {
addOutput('Directory not found', 'error');
}
}
function changeDirectory(dir) {
if (!dir) {
currentDirectory = '~';
} else if (dir === '..') {
const parts = currentDirectory.split('/');
parts.pop();
currentDirectory = parts.join('/') || '~';
} else {
currentDirectory = dir.startsWith('~') ? dir : `${currentDirectory}/${dir}`;
}
promptDisplay.textContent = `${currentUser?.name || 'guest'}@blackroad:${currentDirectory}$`;
}
async function updateStats() {
try {
const stats = await window.blackroad.getSystemStats();
document.getElementById('agentCount').textContent = stats.active_agents;
document.getElementById('userCount').textContent = stats.total_users;
document.getElementById('uptime').textContent = stats.uptime;
} catch (error) {
// Ignore if API is down
}
}
// Input handling
terminalInput.addEventListener('keydown', async (e) => {
if (e.key === 'Enter') {
const command = terminalInput.value;
terminalInput.value = '';
await executeCommand(command);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (historyIndex > 0) {
historyIndex--;
terminalInput.value = commandHistory[historyIndex];
}
} else if (e.key === 'ArrowDown') {
e.preventDefault();
if (historyIndex < commandHistory.length - 1) {
historyIndex++;
terminalInput.value = commandHistory[historyIndex];
} else {
historyIndex = commandHistory.length;
terminalInput.value = '';
}
} else if (e.key === 'Tab') {
e.preventDefault();
// Could add autocomplete here
}
});
// Keep input focused
document.addEventListener('click', () => {
terminalInput.focus();
});
// Initialize on load
init();
// Update stats every 10 seconds
setInterval(updateStats, 10000);
</script>
</body>
</html>