← back to Dw War Room
src/main.ts
238 lines
// ═══════════════════════════════════════════════
// DW War Room 3D — Main Entry (Enhanced)
// Three.js command center with full data wiring
// System health, governance, feed, agent click
// ═══════════════════════════════════════════════
import { WarRoom } from './scene/WarRoom';
import { HudOverlay } from './panels/HudOverlay';
import { DualWebSocketClient } from './data/WebSocketClient';
import * as Api from './data/ApiClient';
async function main() {
const container = document.getElementById('war-room')!;
// Initialize 3D scene
const warRoom = new WarRoom(container);
// Initialize HUD overlay
const hud = new HudOverlay(container);
// Initialize WebSocket connections
const ws = new DualWebSocketClient();
// Agent click → show tooltip
warRoom.onAgentClick = (agent, screenX, screenY) => {
hud.showAgentTooltip(agent, screenX, screenY);
};
// Load initial data (all in parallel)
try {
const [agentData, decisionStats, escalations, meetingState, governanceStatus] = await Promise.all([
Api.getAgents(),
Api.getDecisionStats(),
Api.getActiveEscalations(),
Api.getActiveMeeting(),
Api.getGovernanceStatus().catch(() => null),
]);
// Build agent hierarchy
warRoom.agentNodes.buildHierarchy(agentData.agents, agentData.executives);
const allAgents = warRoom.agentNodes.getAllAgents();
hud.updateAgentCount(allAgents.length, 0);
// Update HUD panels
hud.updateDecisions(decisionStats);
hud.updateEscalations(escalations);
hud.updateMeeting(meetingState);
if (governanceStatus) hud.updateGovernance(governanceStatus);
// Wire data to 3D DataWalls
warRoom.dataWalls.updatePanelData('decisions', {
Pending: String(decisionStats.pending || 0),
Approved: String(decisionStats.approved || 0),
Rejected: String(decisionStats.rejected || 0),
Deferred: String(decisionStats.deferred || 0),
});
warRoom.dataWalls.updatePanelData('escalations', {
Active: String(escalations.length),
'Avg Age': escalations.length > 0
? `${(escalations.reduce((s, e) => s + e.hours_elapsed, 0) / escalations.length).toFixed(1)}h`
: '0h',
Critical: String(escalations.filter(e => e.hours_elapsed > 24).length),
Resolved: '\u2014',
});
warRoom.dataWalls.updatePanelData('meeting', {
Status: meetingState.active ? 'ACTIVE' : 'IDLE',
Phase: meetingState.phase || '\u2014',
Agents: String(allAgents.length),
Duration: '\u2014',
});
// Set escalation pulse
warRoom.setEscalationPulse(escalations.length > 0);
} catch (err) {
console.error('[WarRoom] Initial data load failed:', err);
}
// Load system health (separate try-catch — may not be available)
try {
const health = await Api.getSystemHealth();
if (health) {
hud.updateSystemHealth(health);
warRoom.dataWalls.updatePanelData('status', {
CPU: health.cpu || '\u2014',
Memory: health.memory || '\u2014',
Services: String(health.services || 0),
Uptime: health.uptime || '\u2014',
});
}
} catch {
console.warn('[WarRoom] System health not available');
}
// Load activity feed (separate try-catch — may not be available)
try {
const feed = await Api.getFeed();
if (feed && feed.length > 0) {
hud.loadFeed(feed);
}
} catch {
console.warn('[WarRoom] Feed not available');
}
// WebSocket event handling
ws.onEvent((event) => {
switch (event.type) {
case 'meeting_message': {
const msg = event.payload;
hud.addFeedMessage(msg.agent_name, msg.message);
warRoom.agentNodes.setSpeaking(msg.agent_id, true);
setTimeout(() => warRoom.agentNodes.setSpeaking(msg.agent_id, false), 3000);
// Update speaking count
const agents = warRoom.agentNodes.getAllAgents();
hud.updateAgentCount(agents.length, agents.filter(a => a.speaking).length);
break;
}
case 'meeting_started':
case 'meeting_completed':
case 'meeting_halted':
Api.getActiveMeeting().then(state => {
hud.updateMeeting(state);
warRoom.dataWalls.updatePanelData('meeting', {
Status: state.active ? 'ACTIVE' : 'IDLE',
Phase: state.phase || '\u2014',
});
}).catch(() => {});
if (event.type === 'meeting_completed' || event.type === 'meeting_halted') {
warRoom.agentNodes.clearAllSpeaking();
}
break;
case 'meeting_phase':
hud.updateMeeting({
active: true,
phase: event.payload.phase,
type: event.payload.meetingType,
messages: [],
});
warRoom.dataWalls.updatePanelData('meeting', {
Status: 'ACTIVE',
Phase: event.payload.phase,
});
break;
case 'new_decision':
case 'decision_resolved':
Api.getDecisionStats().then(stats => {
hud.updateDecisions(stats);
warRoom.dataWalls.updatePanelData('decisions', {
Pending: String(stats.pending || 0),
Approved: String(stats.approved || 0),
Rejected: String(stats.rejected || 0),
Deferred: String(stats.deferred || 0),
});
}).catch(() => {});
break;
case 'escalation':
Api.getActiveEscalations().then(esc => {
hud.updateEscalations(esc);
warRoom.setEscalationPulse(esc.length > 0);
warRoom.dataWalls.updatePanelData('escalations', {
Active: String(esc.length),
'Avg Age': esc.length > 0
? `${(esc.reduce((s, e) => s + e.hours_elapsed, 0) / esc.length).toFixed(1)}h`
: '0h',
Critical: String(esc.filter(e => e.hours_elapsed > 24).length),
});
}).catch(() => {});
break;
}
});
ws.connect();
// Polling fallback — refresh every 30s
setInterval(async () => {
try {
const [stats, escalations, meeting, governance] = await Promise.all([
Api.getDecisionStats(),
Api.getActiveEscalations(),
Api.getActiveMeeting(),
Api.getGovernanceStatus().catch(() => null),
]);
hud.updateDecisions(stats);
hud.updateEscalations(escalations);
hud.updateMeeting(meeting);
hud.updateConnection(ws.govConnected, ws.brConnected);
warRoom.setEscalationPulse(escalations.length > 0);
if (governance) hud.updateGovernance(governance);
// Update 3D DataWalls
warRoom.dataWalls.updatePanelData('decisions', {
Pending: String(stats.pending || 0),
Approved: String(stats.approved || 0),
Rejected: String(stats.rejected || 0),
Deferred: String(stats.deferred || 0),
});
} catch {}
}, 30000);
// System health polling — every 60s
setInterval(async () => {
try {
const health = await Api.getSystemHealth();
if (health) {
hud.updateSystemHealth(health);
warRoom.dataWalls.updatePanelData('status', {
CPU: health.cpu || '\u2014',
Memory: health.memory || '\u2014',
Services: String(health.services || 0),
Uptime: health.uptime || '\u2014',
});
}
} catch {}
}, 60000);
// Clock update
setInterval(() => hud.updateClock(), 1000);
hud.updateClock();
// Camera view buttons
document.getElementById('btn-overview')?.addEventListener('click', () => warRoom.viewOverview());
document.getElementById('btn-meeting')?.addEventListener('click', () => warRoom.viewMeetingFocus());
document.getElementById('btn-agents')?.addEventListener('click', () => warRoom.viewAgentFocus());
// Start animation loop
warRoom.animate();
console.log('[WarRoom] Initialized — enhanced with system health, governance, agent tooltips');
}
main().catch(err => console.error('[WarRoom] Fatal:', err));